From 176e01c9d3255268a23057332116b4ed72fc9a91 Mon Sep 17 00:00:00 2001 From: simsa393 <simsa393@student.liu.se> Date: Wed, 16 Nov 2022 11:03:39 +0100 Subject: [PATCH] jekyll test --- .codeclimate.yml | 53 + .devcontainer/Dockerfile | 55 + .devcontainer/devcontainer.json | 26 + .editorconfig | 14 + .gitattributes | 1 + .github/CODEOWNERS | 86 + .github/CODE_OF_CONDUCT.markdown | 76 + .github/CONTRIBUTING.markdown | 158 + .github/FUNDING.yml | 5 + .github/ISSUE_TEMPLATE/bug_report.yml | 104 + .github/ISSUE_TEMPLATE/config.yml | 5 + .github/ISSUE_TEMPLATE/documentation.md | 24 + .github/ISSUE_TEMPLATE/feature_request.md | 73 + .github/PULL_REQUEST_TEMPLATE.md | 46 + .github/SECURITY.markdown | 32 + .github/SUPPORT.markdown | 20 + .github/actions/spelling/README.md | 15 + .github/actions/spelling/advice.md | 28 + .github/actions/spelling/allow.txt | 5 + .github/actions/spelling/excludes.txt | 35 + .github/actions/spelling/expect.txt | 762 +++ .github/actions/spelling/only.txt | 1 + .github/actions/spelling/patterns.txt | 75 + .github/actions/spelling/reject.txt | 7 + .github/config.yml | 15 + .github/dependabot.yml | 6 + .github/first-timers-issue-template.md | 44 + .github/first-timers.yml | 6 + .github/workflows/actions/memprof.rb | 14 + .github/workflows/benchmark.yml | 30 + .github/workflows/ci.yml | 68 + .github/workflows/docs.yml | 52 + .github/workflows/release.yml | 34 + .github/workflows/spelling.yml | 39 + .github/workflows/third-party.yml | 41 + .gitignore | 28 + .jrubyrc | 3 + .rubocop.yml | 474 ++ .rubocop_todo.yml | 25 + Earthfile | 49 + Gemfile | 105 + History.markdown | 3956 +++++++++++++++ LICENSE | 21 + README.markdown | 83 + Rakefile | 163 + appveyor.yml | 47 + benchmark/capture-assign.rb | 21 + benchmark/conditional_liquid.rb | 101 + benchmark/end-with-vs-regexp | 16 + benchmark/file-dir-ensure-trailing-slash | 54 + .../find-filter-vs-where-first-filters.rb | 49 + benchmark/flat-map | 17 + benchmark/hash-fetch | 10 + benchmark/jekyll-sanitize-path | 46 + benchmark/local-require | 29 + benchmark/native-vs-pathutil-relative | 33 + benchmark/parse-date | 25 + benchmark/parse-include-tag-params.rb | 91 + benchmark/path-manager.rb | 65 + benchmark/proc-call-vs-yield | 15 + benchmark/regexp-vs-include.rb | 51 + benchmark/sanitize-url.rb | 26 + benchmark/schwartzian_transform.rb | 110 + benchmark/sequential-assignment | 12 + benchmark/static-drop-vs-forwarded.rb | 83 + benchmark/string-concat | 9 + benchmark/string-replacement | 14 + benchmark/symbol-to-proc | 7 + docs/.gitignore | 5 + docs/CNAME | 1 + docs/_config.yml | 64 + docs/_data/config_options/build.yml | 123 + docs/_data/config_options/global.yml | 77 + docs/_data/config_options/serve.yml | 71 + docs/_data/docs_nav.yml | 37 + docs/_data/jekyll_filters.yml | 348 ++ docs/_data/jekyll_variables.yml | 193 + docs/_data/jekyllconf-talks.yml | 209 + docs/_data/primary_nav.yml | 15 + docs/_data/ruby.yml | 3 + docs/_data/showcase.yml | 315 ++ docs/_data/tutorials.yml | 14 + docs/_docs/assets.md | 94 + docs/_docs/code_of_conduct.md | 82 + docs/_docs/collections.md | 382 ++ docs/_docs/community/bug.md | 8 + docs/_docs/community/community.md | 44 + docs/_docs/conduct.md | 82 + docs/_docs/configuration.md | 19 + docs/_docs/configuration/default.md | 89 + docs/_docs/configuration/environments.md | 49 + .../configuration/front-matter-defaults.md | 153 + .../configuration/incremental-regeneration.md | 36 + docs/_docs/configuration/liquid.md | 38 + docs/_docs/configuration/markdown.md | 97 + docs/_docs/configuration/options.md | 162 + docs/_docs/configuration/sass.md | 15 + docs/_docs/configuration/webrick.md | 20 + .../continuous-integration/buddyworks.md | 60 + docs/_docs/continuous-integration/circleci.md | 137 + .../continuous-integration/github-actions.md | 232 + docs/_docs/continuous-integration/razorops.md | 61 + .../_docs/continuous-integration/travis-ci.md | 233 + docs/_docs/contributing.md | 162 + docs/_docs/datafiles.md | 177 + docs/_docs/deployment.md | 11 + docs/_docs/deployment/automated.md | 70 + docs/_docs/deployment/manual.md | 36 + docs/_docs/deployment/third-party.md | 74 + docs/_docs/front-matter.md | 212 + docs/_docs/github-pages.md | 131 + docs/_docs/history.md | 4423 +++++++++++++++++ docs/_docs/includes.md | 156 + docs/_docs/index.md | 55 + docs/_docs/installation.md | 22 + docs/_docs/installation/macos.md | 94 + docs/_docs/installation/other-linux.md | 60 + docs/_docs/installation/ubuntu.md | 32 + docs/_docs/installation/windows.md | 145 + docs/_docs/layouts.md | 141 + docs/_docs/liquid.md | 19 + docs/_docs/liquid/filters.md | 154 + docs/_docs/liquid/tags.md | 170 + .../maintaining/affinity-team-captain.md | 28 + docs/_docs/maintaining/avoiding-burnout.md | 30 + .../maintaining/becoming-a-maintainer.md | 38 + docs/_docs/maintaining/index.md | 22 + .../maintaining/merging-a-pull-request.md | 55 + .../maintaining/releasing-a-new-version.md | 162 + .../releasing-off-stable-branches.md | 63 + .../maintaining/reviewing-a-pull-request.md | 46 + docs/_docs/maintaining/special-labels.md | 24 + docs/_docs/maintaining/triaging-an-issue.md | 54 + docs/_docs/markdown-101.md | 6 + docs/_docs/migrations.md | 8 + docs/_docs/pages.md | 41 + docs/_docs/pagination.md | 171 + docs/_docs/permalinks.md | 488 ++ docs/_docs/plugins.md | 20 + docs/_docs/plugins/commands.md | 62 + docs/_docs/plugins/converters.md | 91 + docs/_docs/plugins/filters.md | 32 + docs/_docs/plugins/generators.md | 144 + docs/_docs/plugins/hooks.md | 289 ++ docs/_docs/plugins/installation.md | 125 + docs/_docs/plugins/tags.md | 114 + docs/_docs/plugins/your-first-plugin.md | 142 + docs/_docs/posts.md | 240 + docs/_docs/rendering-process.md | 28 + docs/_docs/ruby-101.md | 56 + docs/_docs/security.md | 36 + docs/_docs/static_files.md | 96 + docs/_docs/step-by-step/01-setup.md | 101 + docs/_docs/step-by-step/02-liquid.md | 101 + docs/_docs/step-by-step/03-front-matter.md | 58 + docs/_docs/step-by-step/04-layouts.md | 89 + docs/_docs/step-by-step/05-includes.md | 83 + docs/_docs/step-by-step/06-data-files.md | 48 + docs/_docs/step-by-step/07-assets.md | 92 + docs/_docs/step-by-step/08-blogging.md | 145 + docs/_docs/step-by-step/09-collections.md | 256 + docs/_docs/step-by-step/10-deployment.md | 172 + docs/_docs/structure.md | 239 + docs/_docs/support.md | 24 + docs/_docs/themes.md | 397 ++ docs/_docs/troubleshooting.md | 323 ++ docs/_docs/upgrading.md | 30 + docs/_docs/upgrading/0-to-2.md | 138 + docs/_docs/upgrading/2-to-3.md | 142 + docs/_docs/upgrading/3-to-4.md | 175 + docs/_docs/usage.md | 33 + docs/_docs/variables.md | 52 + docs/_includes/analytics.html | 12 + docs/_includes/anchor_links.html | 33 + docs/_includes/docs_contents.html | 15 + docs/_includes/docs_contents_mobile.html | 15 + docs/_includes/docs_variables_table.html | 18 + docs/_includes/docs_version_badge.html | 1 + docs/_includes/footer.html | 24 + docs/_includes/header.html | 27 + docs/_includes/mobile-nav-items.html | 14 + docs/_includes/news_contents.html | 33 + docs/_includes/news_contents_mobile.html | 11 + docs/_includes/news_item.html | 25 + docs/_includes/news_item_archive.html | 26 + docs/_includes/primary-nav-items.html | 11 + docs/_includes/search/input.html | 1 + docs/_includes/search/script.html | 9 + docs/_includes/section_nav_tutorials.html | 39 + docs/_includes/step-index.html | 34 + docs/_includes/top.html | 19 + docs/_includes/tutorials_contents.html | 16 + docs/_includes/tutorials_contents_mobile.html | 14 + docs/_layouts/default.html | 13 + docs/_layouts/docs.html | 21 + docs/_layouts/error.html | 23 + docs/_layouts/news.html | 14 + docs/_layouts/news_item.html | 31 + docs/_layouts/page.html | 20 + docs/_layouts/step.html | 21 + docs/_layouts/tutorials.html | 43 + .../2013-05-06-jekyll-1-0-0-released.markdown | 22 + .../2013-05-08-jekyll-1-0-1-released.markdown | 26 + .../2013-05-12-jekyll-1-0-2-released.markdown | 27 + .../2013-06-07-jekyll-1-0-3-released.markdown | 24 + .../2013-07-14-jekyll-1-1-0-released.markdown | 26 + .../2013-07-24-jekyll-1-1-1-released.markdown | 23 + .../2013-07-25-jekyll-1-0-4-released.markdown | 19 + .../2013-07-25-jekyll-1-1-2-released.markdown | 19 + .../2013-09-06-jekyll-1-2-0-released.markdown | 22 + .../2013-09-14-jekyll-1-2-1-released.markdown | 18 + ...3-10-28-jekyll-1-3-0-rc1-released.markdown | 16 + .../2013-11-04-jekyll-1-3-0-released.markdown | 42 + .../2013-11-26-jekyll-1-3-1-released.markdown | 20 + .../2013-12-07-jekyll-1-4-0-released.markdown | 29 + .../2013-12-09-jekyll-1-4-1-released.markdown | 17 + .../2013-12-16-jekyll-1-4-2-released.markdown | 17 + .../2014-01-13-jekyll-1-4-3-released.markdown | 25 + .../2014-03-24-jekyll-1-5-0-released.markdown | 18 + .../2014-03-27-jekyll-1-5-1-released.markdown | 25 + .../2014-05-06-jekyll-turns-2-0-0.markdown | 30 + .../2014-05-08-jekyll-2-0-3-released.markdown | 17 + ...yll-stickers-1-dollar-stickermule.markdown | 18 + ...6-28-jekyll-turns-21-i-mean-2-1-0.markdown | 30 + .../2014-07-01-jekyll-2-1-1-released.markdown | 29 + .../2014-07-29-jekyll-2-2-0-released.markdown | 18 + .../2014-08-10-jekyll-2-3-0-released.markdown | 40 + .../2014-09-09-jekyll-2-4-0-released.markdown | 24 + ...midlife-crisis-jekyll-turns-2-5-0.markdown | 46 + .../2014-11-08-jekyll-2-5-1-released.markdown | 28 + .../2014-11-12-jekyll-2-5-2-released.markdown | 17 + ...12-17-alfredxing-welcome-to-jekyll-core.md | 26 + .../2014-12-22-jekyll-2-5-3-released.markdown | 19 + .../2015-01-20-jekyll-meet-and-greet.markdown | 19 + ...01-24-jekyll-3-0-0-beta1-released.markdown | 39 + ...015-02-26-introducing-jekyll-talk.markdown | 14 + .../2015-10-26-jekyll-3-0-released.markdown | 34 + .../2015-11-17-jekyll-3-0-1-released.markdown | 24 + .../2016-01-20-jekyll-3-0-2-released.markdown | 18 + .../2016-01-24-jekyll-3-1-0-released.markdown | 49 + .../2016-01-28-jekyll-3-1-1-released.markdown | 32 + .../2016-02-08-jekyll-3-0-3-released.markdown | 31 + .../2016-02-19-jekyll-3-1-2-released.markdown | 19 + ...aking-it-easier-to-contribute-to-jekyll.md | 16 + .../2016-04-19-jekyll-3-0-4-released.markdown | 23 + .../2016-04-19-jekyll-3-1-3-released.markdown | 16 + .../2016-04-26-jekyll-3-0-5-released.markdown | 24 + .../2016-05-18-jekyll-3-1-4-released.markdown | 25 + .../2016-05-18-jekyll-3-1-5-released.markdown | 16 + .../2016-05-19-jekyll-3-1-6-released.markdown | 18 + ...-s-google-summer-of-code-projects.markdown | 18 + .../2016-07-26-jekyll-3-2-0-released.markdown | 124 + .../2016-08-02-jekyll-3-2-1-released.markdown | 23 + ...8-24-jekyll-admin-initial-release.markdown | 17 + docs/_posts/2016-10-06-jekyll-3-3-is-here.md | 109 + .../2016-11-14-jekyll-3-3-1-released.markdown | 18 + .../2017-01-18-jekyll-3-4-0-released.markdown | 42 + .../2017-03-02-jekyll-3-4-1-released.markdown | 106 + .../2017-03-09-jekyll-3-4-2-released.markdown | 51 + .../2017-03-21-jekyll-3-4-3-released.markdown | 49 + .../2017-06-14-jekyll-3-5-0-released.markdown | 38 + .../2017-07-17-jekyll-3-5-1-released.markdown | 19 + .../2017-08-12-jekyll-3-5-2-released.markdown | 23 + .../2017-09-21-jekyll-3-6-0-released.markdown | 17 + .../2017-10-19-diversity-open-source.markdown | 44 + .../2017-10-21-jekyll-3-6-2-released.markdown | 42 + .../2018-01-02-jekyll-3-7-0-released.md | 37 + .../2018-01-25-jekyll-3-7-2-released.md | 67 + ...-meet-jekyll-s-new-lead-developer.markdown | 43 + .../2018-02-25-jekyll-3-7-3-released.markdown | 19 + docs/_posts/2018-03-14-development-update.md | 28 + .../2018-03-15-jekyll-3-8-0-released.markdown | 43 + .../2018-05-01-jekyll-3-8-1-released.markdown | 20 + .../2018-05-18-jekyll-3-8-2-released.markdown | 19 + .../2018-06-04-jekyll-3-8-3-released.markdown | 13 + .../2018-08-01-jekyll-sponsoring.markdown | 78 + ...19-security-fixes-for-3-6-3-7-3-8.markdown | 26 + .../2018-11-04-jekyll-3-8-5-released.markdown | 16 + ...-jekyll-4-0-0-pre-alpha1-released.markdown | 42 + .../2019-07-02-jekyll-3-8-6-released.markdown | 19 + ...4-jekyll-4-0-0-pre-beta1-released.markdown | 41 + .../2019-08-19-jekyll-4-0-0-released.markdown | 139 + .../2020-05-08-jekyll-4-0-1-released.markdown | 23 + .../2020-05-27-jekyll-4-1-0-released.markdown | 74 + .../2020-06-24-jekyll-4-1-1-released.markdown | 38 + .../2020-08-05-jekyll-3-9-0-released.markdown | 28 + .../2020-12-14-jekyll-4-2-0-released.markdown | 31 + .../2021-04-08-jekyll-3-9-1-released.markdown | 28 + .../2021-09-14-goodbye-dear-frank.markdown | 32 + .../2021-09-27-jekyll-4-2-1-released.markdown | 40 + .../2022-03-03-jekyll-4-2-2-released.markdown | 30 + .../2022-03-27-jekyll-3-9-2-released.markdown | 19 + .../2022-10-20-jekyll-4-3-0-released.markdown | 88 + .../2022-10-26-jekyll-4-3-1-released.markdown | 21 + docs/_sass/_docsearch.scss | 579 +++ docs/_sass/_font-awesome.scss | 28 + docs/_sass/_fonts.scss | 90 + docs/_sass/_gridism.scss | 125 + docs/_sass/_mixins.scss | 38 + docs/_sass/_normalize.scss | 447 ++ docs/_sass/_pygments.scss | 86 + docs/_sass/_style.scss | 1350 +++++ docs/_tutorials/cache-api.md | 86 + docs/_tutorials/convert-site-to-jekyll.md | 540 ++ docs/_tutorials/csv-to-table.md | 213 + docs/_tutorials/custom-404-page.md | 72 + docs/_tutorials/index.md | 32 + docs/_tutorials/navigation.md | 672 +++ docs/_tutorials/orderofinterpretation.md | 159 + docs/_tutorials/using-jekyll-with-bundler.md | 125 + docs/_tutorials/video-walkthroughs.md | 33 + docs/css/screen.scss | 11 + docs/favicon.ico | Bin 0 -> 5430 bytes docs/fonts/FontAwesome.eot | Bin 0 -> 1804 bytes docs/fonts/FontAwesome.svg | 12 + docs/fonts/FontAwesome.ttf | Bin 0 -> 1624 bytes docs/fonts/FontAwesome.woff | Bin 0 -> 1700 bytes docs/fonts/lato-v14-latin-300.woff | Bin 0 -> 29852 bytes docs/fonts/lato-v14-latin-300.woff2 | Bin 0 -> 23208 bytes docs/fonts/lato-v14-latin-300italic.woff | Bin 0 -> 22312 bytes docs/fonts/lato-v14-latin-300italic.woff2 | Bin 0 -> 17528 bytes docs/fonts/lato-v14-latin-700.woff | Bin 0 -> 27848 bytes docs/fonts/lato-v14-latin-700.woff2 | Bin 0 -> 22820 bytes docs/fonts/lato-v14-latin-700italic.woff | Bin 0 -> 29692 bytes docs/fonts/lato-v14-latin-700italic.woff2 | Bin 0 -> 24240 bytes docs/fonts/lato-v14-latin-900.woff | Bin 0 -> 27260 bytes docs/fonts/lato-v14-latin-900.woff2 | Bin 0 -> 22352 bytes docs/fonts/lato-v14-latin-900italic.woff | Bin 0 -> 28688 bytes docs/fonts/lato-v14-latin-900italic.woff2 | Bin 0 -> 23524 bytes docs/fonts/lato-v14-latin-italic.woff | Bin 0 -> 29600 bytes docs/fonts/lato-v14-latin-italic.woff2 | Bin 0 -> 24192 bytes docs/fonts/lato-v14-latin-regular.woff | Bin 0 -> 28412 bytes docs/fonts/lato-v14-latin-regular.woff2 | Bin 0 -> 23316 bytes docs/icomoon-selection.json | 96 + docs/img/article-footer.png | Bin 0 -> 1581 bytes docs/img/footer-arrow.png | Bin 0 -> 840 bytes docs/img/footer-logo.png | Bin 0 -> 3688 bytes docs/img/forestry-logo.svg | 19 + docs/img/jekyll-og.png | Bin 0 -> 68048 bytes docs/img/jekyll-sticker.jpg | Bin 0 -> 113785 bytes docs/img/jekyll-sticker.png | Bin 0 -> 15126 bytes docs/img/jekylllayoutconcept.png | Bin 0 -> 17121 bytes docs/img/logo-2x.png | Bin 0 -> 45966 bytes docs/img/logo-rss.png | Bin 0 -> 6022 bytes docs/img/octojekyll.png | Bin 0 -> 22360 bytes docs/img/spacer.gif | Bin 0 -> 42 bytes docs/img/twitter-card.png | Bin 0 -> 72287 bytes docs/js/html5shiv.min.js | 4 + docs/js/respond.min.js | 5 + docs/latest_version.txt | 1 + docs/pages/404.html | 29 + docs/pages/index.html | 90 + docs/pages/jekyllconf.md | 25 + docs/pages/news.html | 14 + docs/pages/philosophy.md | 52 + docs/pages/redirects/github.html | 4 + docs/pages/redirects/issues.html | 4 + docs/pages/releases.html | 14 + docs/pages/resources.md | 99 + docs/pages/showcase.html | 23 + docs/pages/team.md | 32 + docs/readme.md | 23 + exe/jekyll | 57 + features/cache.feature | 46 + features/collections.feature | 659 +++ features/collections_dir.feature | 435 ++ features/create_sites.feature | 214 + features/data.feature | 133 + features/drafts.feature | 50 + features/embed_filters.feature | 161 + features/frontmatter_defaults.feature | 195 + features/highlighting.feature | 33 + features/hooks.feature | 489 ++ features/include_relative_tag.feature | 60 + features/include_tag.feature | 131 + features/incremental_rebuild.feature | 104 + features/layout_data.feature | 90 + features/link_tag.feature | 78 + features/markdown.feature | 34 + features/pagination.feature | 87 + features/permalinks.feature | 198 + features/plugins.feature | 37 + features/post_data.feature | 428 ++ features/post_excerpts.feature | 114 + features/post_url_tag.feature | 159 + features/rendering.feature | 241 + features/site_configuration.feature | 362 ++ features/site_data.feature | 118 + features/step_definitions.rb | 417 ++ features/support/formatter.rb | 225 + features/support/helpers.rb | 169 + features/theme.feature | 151 + features/theme_configuration.feature | 36 + features/theme_gem.feature | 32 + jekyll.gemspec | 50 + lib/blank_template/_config.yml | 3 + lib/blank_template/_layouts/default.html | 12 + lib/blank_template/_sass/main.scss | 9 + lib/blank_template/assets/css/main.scss | 4 + lib/blank_template/index.md | 8 + lib/jekyll.rb | 195 + lib/jekyll/cache.rb | 186 + lib/jekyll/cleaner.rb | 111 + lib/jekyll/collection.rb | 310 ++ lib/jekyll/command.rb | 105 + lib/jekyll/commands/build.rb | 82 + lib/jekyll/commands/clean.rb | 44 + lib/jekyll/commands/doctor.rb | 177 + lib/jekyll/commands/help.rb | 34 + lib/jekyll/commands/new.rb | 168 + lib/jekyll/commands/new_theme.rb | 39 + lib/jekyll/commands/serve.rb | 367 ++ .../commands/serve/live_reload_reactor.rb | 119 + .../serve/livereload_assets/livereload.js | 1183 +++++ .../commands/serve/mime_types_charset.json | 71 + lib/jekyll/commands/serve/servlet.rb | 206 + lib/jekyll/commands/serve/websockets.rb | 81 + lib/jekyll/configuration.rb | 313 ++ lib/jekyll/converter.rb | 54 + lib/jekyll/converters/identity.rb | 41 + lib/jekyll/converters/markdown.rb | 113 + .../converters/markdown/kramdown_parser.rb | 197 + lib/jekyll/converters/smartypants.rb | 70 + lib/jekyll/convertible.rb | 257 + lib/jekyll/deprecator.rb | 50 + lib/jekyll/document.rb | 543 ++ lib/jekyll/drops/collection_drop.rb | 20 + lib/jekyll/drops/document_drop.rb | 74 + lib/jekyll/drops/drop.rb | 293 ++ lib/jekyll/drops/excerpt_drop.rb | 23 + lib/jekyll/drops/jekyll_drop.rb | 32 + lib/jekyll/drops/site_drop.rb | 66 + lib/jekyll/drops/static_file_drop.rb | 14 + lib/jekyll/drops/theme_drop.rb | 36 + lib/jekyll/drops/unified_payload_drop.rb | 30 + lib/jekyll/drops/url_drop.rb | 140 + lib/jekyll/entry_filter.rb | 117 + lib/jekyll/errors.rb | 20 + lib/jekyll/excerpt.rb | 200 + lib/jekyll/external.rb | 75 + lib/jekyll/filters.rb | 532 ++ lib/jekyll/filters/date_filters.rb | 110 + lib/jekyll/filters/grouping_filters.rb | 64 + lib/jekyll/filters/url_filters.rb | 98 + lib/jekyll/frontmatter_defaults.rb | 238 + lib/jekyll/generator.rb | 5 + lib/jekyll/hooks.rb | 107 + lib/jekyll/inclusion.rb | 32 + lib/jekyll/layout.rb | 55 + lib/jekyll/liquid_extensions.rb | 22 + lib/jekyll/liquid_renderer.rb | 80 + lib/jekyll/liquid_renderer/file.rb | 77 + lib/jekyll/liquid_renderer/table.rb | 55 + lib/jekyll/log_adapter.rb | 151 + lib/jekyll/mime.types | 940 ++++ lib/jekyll/page.rb | 215 + lib/jekyll/page_excerpt.rb | 25 + lib/jekyll/page_without_a_file.rb | 14 + lib/jekyll/path_manager.rb | 74 + lib/jekyll/plugin.rb | 92 + lib/jekyll/plugin_manager.rb | 123 + lib/jekyll/profiler.rb | 55 + lib/jekyll/publisher.rb | 23 + lib/jekyll/reader.rb | 209 + lib/jekyll/readers/collection_reader.rb | 23 + lib/jekyll/readers/data_reader.rb | 113 + lib/jekyll/readers/layout_reader.rb | 62 + lib/jekyll/readers/page_reader.rb | 25 + lib/jekyll/readers/post_reader.rb | 85 + lib/jekyll/readers/static_file_reader.rb | 25 + lib/jekyll/readers/theme_assets_reader.rb | 52 + lib/jekyll/regenerator.rb | 195 + lib/jekyll/related_posts.rb | 52 + lib/jekyll/renderer.rb | 263 + lib/jekyll/site.rb | 582 +++ lib/jekyll/static_file.rb | 205 + lib/jekyll/stevenson.rb | 60 + lib/jekyll/tags/highlight.rb | 114 + lib/jekyll/tags/include.rb | 275 + lib/jekyll/tags/link.rb | 42 + lib/jekyll/tags/post_url.rb | 106 + lib/jekyll/theme.rb | 90 + lib/jekyll/theme_builder.rb | 121 + lib/jekyll/url.rb | 167 + lib/jekyll/utils.rb | 371 ++ lib/jekyll/utils/ansi.rb | 57 + lib/jekyll/utils/exec.rb | 26 + lib/jekyll/utils/internet.rb | 37 + lib/jekyll/utils/platforms.rb | 67 + lib/jekyll/utils/thread_event.rb | 31 + lib/jekyll/utils/win_tz.rb | 46 + lib/jekyll/version.rb | 5 + lib/site_template/.gitignore | 5 + lib/site_template/404.html | 25 + lib/site_template/_config.yml | 55 + .../0000-00-00-welcome-to-jekyll.markdown.erb | 29 + lib/site_template/about.markdown | 18 + lib/site_template/index.markdown | 6 + lib/theme_template/CODE_OF_CONDUCT.md.erb | 74 + lib/theme_template/Gemfile | 4 + lib/theme_template/LICENSE.txt.erb | 21 + lib/theme_template/README.md.erb | 50 + lib/theme_template/_layouts/default.html | 1 + lib/theme_template/_layouts/page.html | 5 + lib/theme_template/_layouts/post.html | 5 + lib/theme_template/example/_config.yml.erb | 1 + lib/theme_template/example/_post.md | 12 + lib/theme_template/example/index.html | 14 + lib/theme_template/example/style.scss | 7 + lib/theme_template/gitignore.erb | 6 + lib/theme_template/theme.gemspec.erb | 16 + rake/profile.rake | 57 + rake/release.rake | 30 + rake/site.rake | 126 + rubocop/jekyll.rb | 5 + rubocop/jekyll/assert_equal_literal_actual.rb | 149 + rubocop/jekyll/no_p_allowed.rb | 23 + rubocop/jekyll/no_puts_allowed.rb | 23 + script/backport-pr | 83 + script/bootstrap | 4 + script/branding | 11 + script/cibuild | 19 + script/console | 38 + script/cucumber | 4 + script/default-site | 20 + script/fmt | 8 + script/memprof | 5 + script/profile-docs | 10 + script/proof | 35 + script/rubies | 14 + script/rubyprof | 23 + script/stackprof | 49 + script/test | 56 + script/travis | 40 + script/vendor-mimes | 77 + test/fixtures/broken_front_matter1.erb | 5 + test/fixtures/broken_front_matter2.erb | 4 + test/fixtures/broken_front_matter3.erb | 6 + test/fixtures/empty_permalink.erb | 4 + test/fixtures/exploit_front_matter.erb | 4 + test/fixtures/front_matter.erb | 4 + test/fixtures/no_liquid.erb | 4 + test/fixtures/physical.html | 6 + test/fixtures/sample.csv | 3 + test/fixtures/sample.tsv | 3 + .../test-dependency-theme.gemspec | 13 + .../test-theme-skinny/_layouts/default.html | 11 + .../test-theme-skinny/_layouts/home.html | 5 + .../test-theme-skinny.gemspec | 11 + .../test-theme-symlink.gemspec | 11 + .../test-theme-w-empty-data/_data/.gitkeep | 0 .../_layouts/default.html | 11 + .../test-theme-w-empty-data.gemspec | 11 + test/fixtures/test-theme/_config.yml | 14 + test/fixtures/test-theme/_data/cars.yml | 6 + .../test-theme/_data/categories/dairy.yaml | 6 + test/fixtures/test-theme/_data/greetings.yml | 1 + .../test-theme/_data/i18n/testimonials.yml | 2 + .../test-theme/_includes/include.html | 1 + .../test-theme/_includes/testimonials.html | 9 + .../fixtures/test-theme/_layouts/default.html | 1 + .../test-theme/_sass/test-theme-black.scss | 3 + .../test-theme/_sass/test-theme-red.scss | 3 + test/fixtures/test-theme/_symlink | 1 + .../test-theme/assets/application.coffee | 3 + test/fixtures/test-theme/assets/base.js | 1 + .../test-theme/assets/img/another-logo.png | 1 + test/fixtures/test-theme/assets/img/logo.png | Bin 0 -> 308 bytes test/fixtures/test-theme/assets/style.scss | 3 + test/fixtures/test-theme/test-theme.gemspec | 11 + test/fixtures/webrick/bar.html | 1 + test/fixtures/webrick/bar/baz.html | 1 + test/fixtures/webrick/bar/foo.xhtml | 1 + test/helper.rb | 264 + test/safe_glob_test[/find_me.txt | 0 test/simplecov_custom_profile.rb | 12 + test/source/+/%# +.md | 6 + test/source/+/foo.md | 7 + test/source/.gitattributes | 1 + test/source/.htaccess | 8 + test/source/_broken/bad_post.md | 4 + test/source/_config.dev.toml | 2 + test/source/_config_folded.yml | 8 + test/source/_data/categories.01/dairy.yaml | 6 + test/source/_data/categories/dairy.yaml | 6 + test/source/_data/greetings.yml | 1 + test/source/_data/i18n.yml | 3 + test/source/_data/languages.yml | 2 + test/source/_data/members.json | 12 + test/source/_data/members.yaml | 7 + test/source/_data/products.yml | 1 + test/source/_dates/date_without_time.md | 4 + test/source/_dates/time_with_timezone.md | 4 + test/source/_dates/time_without_timezone.md | 4 + test/source/_drafts/draft-properties.text | 11 + test/source/_encodings/UTF8CRLFandBOM.md | 11 + .../_encodings/Unicode16LECRLFandBOM.md | Bin 0 -> 1556 bytes .../_glob_include_test/_is_dir/include_me.txt | 0 test/source/_glob_include_test/_not_dir | 0 test/source/_includes/include.html | 1 + test/source/_includes/params.html | 7 + test/source/_includes/params@2.0.html | 7 + test/source/_includes/sig.markdown | 3 + test/source/_includes/tmp | 1 + test/source/_includes_custom/custom.html | 1 + test/source/_layouts/default.html | 30 + test/source/_layouts/post/simple.html | 1 + test/source/_layouts/simple.html | 1 + .../3940394-21-9393050-fifif1323-test.md | 5 + test/source/_methods/_do_not_read_me.md | 5 + test/source/_methods/collection/entries | 5 + test/source/_methods/configuration.md | 8 + test/source/_methods/escape-+ #%20[].md | 5 + .../source/_methods/extensionless_static_file | 1 + test/source/_methods/sanitized_path.md | 5 + .../_methods/site/_dont_include_me_either.md | 5 + test/source/_methods/site/generate.md | 6 + test/source/_methods/site/initialize.md | 4 + test/source/_methods/trailing-dots...md | 3 + test/source/_methods/um_hi.md | 1 + test/source/_methods/with.dots/.gitignore | 1 + test/source/_methods/with.dots/.htaccess | 1 + test/source/_methods/yaml_with_dots.md | 8 + test/source/_plugins/custom_block.rb | 14 + test/source/_plugins/dummy.rb | 9 + .../_posts/2008-02-02-not-published.markdown | 8 + .../_posts/2008-02-02-published.markdown | 7 + .../_posts/2008-02-03-wrong-extension.yml | 8 + .../source/_posts/2008-10-18-foo-bar.markdown | 8 + .../source/_posts/2008-11-21-complex.markdown | 8 + .../2008-12-03-permalinked-post.markdown | 9 + .../source/_posts/2008-12-13-include.markdown | 8 + .../2009-01-27-array-categories.markdown | 10 + .../_posts/2009-01-27-categories.markdown | 7 + .../_posts/2009-01-27-category.markdown | 7 + .../2009-01-27-empty-categories.markdown | 7 + .../_posts/2009-01-27-empty-category.markdown | 7 + .../_posts/2009-01-27-no-category.markdown | 6 + .../source/_posts/2009-03-12-hash-#1.markdown | 6 + .../_posts/2009-05-18-empty-tag.markdown | 6 + .../_posts/2009-05-18-empty-tags.markdown | 6 + test/source/_posts/2009-05-18-tag.markdown | 6 + test/source/_posts/2009-05-18-tags.markdown | 9 + .../_posts/2009-06-22-empty-yaml.markdown | 3 + .../source/_posts/2009-06-22-no-yaml.markdown | 1 + .../_posts/2010-01-08-triple-dash.markdown | 5 + .../_posts/2010-01-09-date-override.markdown | 7 + .../_posts/2010-01-09-time-override.markdown | 7 + .../2010-01-09-timezone-override.markdown | 7 + .../_posts/2010-01-16-override-data.markdown | 6 + test/source/_posts/2011-04-12-md-extension.md | 7 + .../_posts/2011-04-12-text-extension.text | 1 + .../_posts/2013-01-02-post-excerpt.markdown | 14 + .../_posts/2013-01-12-nil-layout.markdown | 6 + .../_posts/2013-01-12-no-layout.markdown | 5 + .../2013-03-19-not-a-post.markdown/.gitkeep | 0 .../2013-03-19-not-a-post/dubious.markdown | 6 + .../_posts/2013-04-11-custom-excerpt.markdown | 10 + .../2013-05-10-number-category.markdown | 7 + ...13-07-22-post-excerpt-with-layout.markdown | 24 + .../_posts/2013-08-01-mkdn-extension.mkdn | 1 + ...13-12-17-include-variable-filters.markdown | 25 + test/source/_posts/2013-12-20-properties.text | 11 + .../_posts/2014-01-06-permalink-traversal.md | 5 + .../_posts/2014-03-03-yaml-with-dots.md | 5 + .../_posts/2014-03-22-escape-+ %20[].markdown | 6 + ...07-05-another-mixed-case-category.markdown | 7 + .../2014-07-05-mixed-case-category.markdown | 7 + .../2014-09-02-relative-includes.markdown | 29 + .../_posts/2014-11-24-Rmd-extension.Rmd | 1 + ...2015-01-08-post-excerpt-separator.markdown | 15 + ...015-02-20-extensionless-permalink.markdown | 7 + .../_posts/2015-12-27-extra-spaces.markdown | 5 + ...16-08-16-indented-link-references.markdown | 16 + .../2016-11-26-special-chars-(+).markdown | 8 + .../_posts/2017-2-5-i-dont-like-zeroes.md | 5 + ...01-28-closed-liquid-block-excerpt.markdown | 24 + ...8-01-28-open-liquid-block-excerpt.markdown | 21 + ...liquid-block-excerpt-whitespace-control.md | 9 + ...-15-excerpt-whitespace-control-variable.md | 7 + ...liquid-block-excerpt-whitespace-control.md | 10 + .../2018-10-12-trailing-dots...markdown | 5 + .../_posts/2018-11-15-excerpt-liquid-block.md | 29 + .../_posts/es/2008-11-21-nested.markdown | 8 + .../_posts/include_relative/params.html | 7 + .../_posts/include_relative/rel_include.html | 1 + test/source/_roles/named.md | 5 + test/source/_roles/unnamed.md | 4 + test/source/_sass/_grid.scss | 1 + test/source/_slides/example-slide-1.html | 6 + test/source/_slides/example-slide-2.html | 7 + test/source/_slides/example-slide-3.html | 5 + test/source/_slides/example-slide-4.html | 15 + test/source/_slides/example-slide-5.html | 5 + test/source/_slides/example-slide-6.html | 15 + test/source/_slides/example-slide-7.md | 6 + .../_slides/example-slide-Upper-Cased.html | 6 + test/source/_slides/non-outputted-slide.html | 7 + test/source/_slides/octojekyll.png | Bin 0 -> 22360 bytes .../_thanksgiving/2015-11-26-thanksgiving.md | 3 + test/source/_thanksgiving/black-friday.md | 3 + .../_tutorials/dive-in-and-publish-already.md | 9 + .../_tutorials/extending-with-plugins.md | 9 + test/source/_tutorials/getting-started.md | 7 + test/source/_tutorials/graduation-day.md | 10 + test/source/_tutorials/lets-roll.md | 16 + test/source/_tutorials/tip-of-the-iceberg.md | 6 + .../_urls_differ_by_case_invalid/page1.html | 6 + .../_urls_differ_by_case_invalid/page2.html | 6 + .../_urls_differ_by_case_valid/page1.html | 6 + test/source/_with.dots/all.dots/2.4.0.md | 5 + test/source/_with.dots/file.with.dots.md | 4 + test/source/_with.dots/mit.txt | 4 + .../_with.dots/permalink.with.slash.tho.md | 5 + test/source/about.html | 6 + test/source/assets/application.coffee | 3 + test/source/assets/base.js | 1 + test/source/assets/test-styles.scss | 4 + .../_posts/2008-09-23-categories.markdown | 6 + test/source/contacts.html | 5 + test/source/contacts/bar.html | 5 + test/source/contacts/foo.md | 7 + test/source/contacts/humans.txt | 5 + test/source/contacts/index.html | 5 + test/source/css/main.scss | 4 + test/source/css/screen.css | 76 + test/source/deal.with.dots.html | 5 + test/source/dynamic_file.php | 4 + test/source/environment.html | 5 + test/source/exploit.md | 5 + .../bar/2008-12-12-topical-post.markdown | 8 + test/source/index.html | 24 + test/source/info.md | 7 + test/source/js/coffeescript.coffee | 11 + test/source/pgp.key | 2 + test/source/products.yml | 4 + test/source/properties.html | 8 + test/source/sitemap.xml | 32 + test/source/static_files.html | 4 + test/source/symlink-test/_data | 1 + test/source/symlink-test/symlinked-dir | 1 + test/source/symlink-test/symlinked-file | 1 + .../symlinked-file-outside-source | 1 + test/source/trailing-dots...md | 3 + test/source/unpublished.html | 7 + .../_posts/2009-05-24-yaml-linebreak.markdown | 7 + .../_posts/2008-09-23-categories.markdown | 6 + test/test_ansi.rb | 25 + test/test_cleaner.rb | 107 + test/test_coffeescript.rb | 49 + test/test_collections.rb | 355 ++ test/test_command.rb | 26 + test/test_commands_serve.rb | 316 ++ test/test_commands_serve_servlet.rb | 48 + test/test_configuration.rb | 542 ++ test/test_convertible.rb | 84 + test/test_data_reader.rb | 60 + test/test_doctor_command.rb | 42 + test/test_document.rb | 662 +++ test/test_drop.rb | 154 + test/test_entry_filter.rb | 170 + test/test_excerpt.rb | 310 ++ test/test_excerpt_drop.rb | 42 + test/test_filters.rb | 1542 ++++++ test/test_front_matter_defaults.rb | 225 + test/test_generated_site.rb | 110 + test/test_kramdown.rb | 202 + test/test_layout_reader.rb | 83 + test/test_liquid_extensions.rb | 32 + test/test_liquid_renderer.rb | 58 + test/test_log_adapter.rb | 135 + test/test_new_command.rb | 143 + test/test_page.rb | 428 ++ test/test_page_without_a_file.rb | 182 + test/test_path_manager.rb | 35 + test/test_path_sanitization.rb | 77 + test/test_plugin_manager.rb | 186 + test/test_post_reader.rb | 42 + test/test_regenerator.rb | 328 ++ test/test_related_posts.rb | 66 + test/test_sass.rb | 35 + test/test_site.rb | 762 +++ test/test_site_drop.rb | 23 + test/test_static_file.rb | 189 + test/test_tags.rb | 1191 +++++ test/test_theme.rb | 85 + test/test_theme_assets_reader.rb | 102 + test/test_theme_data_reader.rb | 90 + test/test_theme_drop.rb | 34 + test/test_url.rb | 84 + test/test_utils.rb | 438 ++ test/test_win_tz.rb | 31 + 792 files changed, 63720 insertions(+) create mode 100644 .codeclimate.yml create mode 100644 .devcontainer/Dockerfile create mode 100644 .devcontainer/devcontainer.json create mode 100644 .editorconfig create mode 100644 .gitattributes create mode 100644 .github/CODEOWNERS create mode 100644 .github/CODE_OF_CONDUCT.markdown create mode 100644 .github/CONTRIBUTING.markdown create mode 100644 .github/FUNDING.yml create mode 100644 .github/ISSUE_TEMPLATE/bug_report.yml create mode 100644 .github/ISSUE_TEMPLATE/config.yml create mode 100644 .github/ISSUE_TEMPLATE/documentation.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md create mode 100644 .github/PULL_REQUEST_TEMPLATE.md create mode 100644 .github/SECURITY.markdown create mode 100644 .github/SUPPORT.markdown create mode 100644 .github/actions/spelling/README.md create mode 100644 .github/actions/spelling/advice.md create mode 100644 .github/actions/spelling/allow.txt create mode 100644 .github/actions/spelling/excludes.txt create mode 100644 .github/actions/spelling/expect.txt create mode 100644 .github/actions/spelling/only.txt create mode 100644 .github/actions/spelling/patterns.txt create mode 100644 .github/actions/spelling/reject.txt create mode 100644 .github/config.yml create mode 100644 .github/dependabot.yml create mode 100644 .github/first-timers-issue-template.md create mode 100644 .github/first-timers.yml create mode 100644 .github/workflows/actions/memprof.rb create mode 100644 .github/workflows/benchmark.yml create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/docs.yml create mode 100644 .github/workflows/release.yml create mode 100644 .github/workflows/spelling.yml create mode 100644 .github/workflows/third-party.yml create mode 100644 .gitignore create mode 100644 .jrubyrc create mode 100644 .rubocop.yml create mode 100644 .rubocop_todo.yml create mode 100644 Earthfile create mode 100644 Gemfile create mode 100644 History.markdown create mode 100644 LICENSE create mode 100644 README.markdown create mode 100644 Rakefile create mode 100644 appveyor.yml create mode 100644 benchmark/capture-assign.rb create mode 100644 benchmark/conditional_liquid.rb create mode 100644 benchmark/end-with-vs-regexp create mode 100644 benchmark/file-dir-ensure-trailing-slash create mode 100644 benchmark/find-filter-vs-where-first-filters.rb create mode 100644 benchmark/flat-map create mode 100644 benchmark/hash-fetch create mode 100644 benchmark/jekyll-sanitize-path create mode 100644 benchmark/local-require create mode 100644 benchmark/native-vs-pathutil-relative create mode 100644 benchmark/parse-date create mode 100644 benchmark/parse-include-tag-params.rb create mode 100644 benchmark/path-manager.rb create mode 100644 benchmark/proc-call-vs-yield create mode 100644 benchmark/regexp-vs-include.rb create mode 100644 benchmark/sanitize-url.rb create mode 100644 benchmark/schwartzian_transform.rb create mode 100644 benchmark/sequential-assignment create mode 100644 benchmark/static-drop-vs-forwarded.rb create mode 100644 benchmark/string-concat create mode 100644 benchmark/string-replacement create mode 100644 benchmark/symbol-to-proc create mode 100644 docs/.gitignore create mode 100644 docs/CNAME create mode 100644 docs/_config.yml create mode 100644 docs/_data/config_options/build.yml create mode 100644 docs/_data/config_options/global.yml create mode 100644 docs/_data/config_options/serve.yml create mode 100644 docs/_data/docs_nav.yml create mode 100644 docs/_data/jekyll_filters.yml create mode 100644 docs/_data/jekyll_variables.yml create mode 100644 docs/_data/jekyllconf-talks.yml create mode 100644 docs/_data/primary_nav.yml create mode 100644 docs/_data/ruby.yml create mode 100644 docs/_data/showcase.yml create mode 100644 docs/_data/tutorials.yml create mode 100644 docs/_docs/assets.md create mode 100644 docs/_docs/code_of_conduct.md create mode 100644 docs/_docs/collections.md create mode 100644 docs/_docs/community/bug.md create mode 100644 docs/_docs/community/community.md create mode 100644 docs/_docs/conduct.md create mode 100644 docs/_docs/configuration.md create mode 100644 docs/_docs/configuration/default.md create mode 100644 docs/_docs/configuration/environments.md create mode 100644 docs/_docs/configuration/front-matter-defaults.md create mode 100644 docs/_docs/configuration/incremental-regeneration.md create mode 100644 docs/_docs/configuration/liquid.md create mode 100644 docs/_docs/configuration/markdown.md create mode 100644 docs/_docs/configuration/options.md create mode 100644 docs/_docs/configuration/sass.md create mode 100644 docs/_docs/configuration/webrick.md create mode 100644 docs/_docs/continuous-integration/buddyworks.md create mode 100644 docs/_docs/continuous-integration/circleci.md create mode 100644 docs/_docs/continuous-integration/github-actions.md create mode 100644 docs/_docs/continuous-integration/razorops.md create mode 100644 docs/_docs/continuous-integration/travis-ci.md create mode 100644 docs/_docs/contributing.md create mode 100644 docs/_docs/datafiles.md create mode 100644 docs/_docs/deployment.md create mode 100644 docs/_docs/deployment/automated.md create mode 100644 docs/_docs/deployment/manual.md create mode 100644 docs/_docs/deployment/third-party.md create mode 100644 docs/_docs/front-matter.md create mode 100644 docs/_docs/github-pages.md create mode 100644 docs/_docs/history.md create mode 100644 docs/_docs/includes.md create mode 100644 docs/_docs/index.md create mode 100644 docs/_docs/installation.md create mode 100644 docs/_docs/installation/macos.md create mode 100644 docs/_docs/installation/other-linux.md create mode 100644 docs/_docs/installation/ubuntu.md create mode 100644 docs/_docs/installation/windows.md create mode 100644 docs/_docs/layouts.md create mode 100644 docs/_docs/liquid.md create mode 100644 docs/_docs/liquid/filters.md create mode 100644 docs/_docs/liquid/tags.md create mode 100644 docs/_docs/maintaining/affinity-team-captain.md create mode 100644 docs/_docs/maintaining/avoiding-burnout.md create mode 100644 docs/_docs/maintaining/becoming-a-maintainer.md create mode 100644 docs/_docs/maintaining/index.md create mode 100644 docs/_docs/maintaining/merging-a-pull-request.md create mode 100644 docs/_docs/maintaining/releasing-a-new-version.md create mode 100644 docs/_docs/maintaining/releasing-off-stable-branches.md create mode 100644 docs/_docs/maintaining/reviewing-a-pull-request.md create mode 100644 docs/_docs/maintaining/special-labels.md create mode 100644 docs/_docs/maintaining/triaging-an-issue.md create mode 100644 docs/_docs/markdown-101.md create mode 100644 docs/_docs/migrations.md create mode 100644 docs/_docs/pages.md create mode 100644 docs/_docs/pagination.md create mode 100644 docs/_docs/permalinks.md create mode 100644 docs/_docs/plugins.md create mode 100644 docs/_docs/plugins/commands.md create mode 100644 docs/_docs/plugins/converters.md create mode 100644 docs/_docs/plugins/filters.md create mode 100644 docs/_docs/plugins/generators.md create mode 100644 docs/_docs/plugins/hooks.md create mode 100644 docs/_docs/plugins/installation.md create mode 100644 docs/_docs/plugins/tags.md create mode 100644 docs/_docs/plugins/your-first-plugin.md create mode 100644 docs/_docs/posts.md create mode 100644 docs/_docs/rendering-process.md create mode 100644 docs/_docs/ruby-101.md create mode 100644 docs/_docs/security.md create mode 100644 docs/_docs/static_files.md create mode 100644 docs/_docs/step-by-step/01-setup.md create mode 100644 docs/_docs/step-by-step/02-liquid.md create mode 100644 docs/_docs/step-by-step/03-front-matter.md create mode 100644 docs/_docs/step-by-step/04-layouts.md create mode 100644 docs/_docs/step-by-step/05-includes.md create mode 100644 docs/_docs/step-by-step/06-data-files.md create mode 100644 docs/_docs/step-by-step/07-assets.md create mode 100644 docs/_docs/step-by-step/08-blogging.md create mode 100644 docs/_docs/step-by-step/09-collections.md create mode 100644 docs/_docs/step-by-step/10-deployment.md create mode 100644 docs/_docs/structure.md create mode 100644 docs/_docs/support.md create mode 100644 docs/_docs/themes.md create mode 100644 docs/_docs/troubleshooting.md create mode 100644 docs/_docs/upgrading.md create mode 100644 docs/_docs/upgrading/0-to-2.md create mode 100644 docs/_docs/upgrading/2-to-3.md create mode 100644 docs/_docs/upgrading/3-to-4.md create mode 100644 docs/_docs/usage.md create mode 100644 docs/_docs/variables.md create mode 100644 docs/_includes/analytics.html create mode 100644 docs/_includes/anchor_links.html create mode 100644 docs/_includes/docs_contents.html create mode 100644 docs/_includes/docs_contents_mobile.html create mode 100644 docs/_includes/docs_variables_table.html create mode 100644 docs/_includes/docs_version_badge.html create mode 100644 docs/_includes/footer.html create mode 100644 docs/_includes/header.html create mode 100644 docs/_includes/mobile-nav-items.html create mode 100644 docs/_includes/news_contents.html create mode 100644 docs/_includes/news_contents_mobile.html create mode 100644 docs/_includes/news_item.html create mode 100644 docs/_includes/news_item_archive.html create mode 100644 docs/_includes/primary-nav-items.html create mode 100644 docs/_includes/search/input.html create mode 100644 docs/_includes/search/script.html create mode 100644 docs/_includes/section_nav_tutorials.html create mode 100644 docs/_includes/step-index.html create mode 100644 docs/_includes/top.html create mode 100644 docs/_includes/tutorials_contents.html create mode 100644 docs/_includes/tutorials_contents_mobile.html create mode 100644 docs/_layouts/default.html create mode 100644 docs/_layouts/docs.html create mode 100644 docs/_layouts/error.html create mode 100644 docs/_layouts/news.html create mode 100644 docs/_layouts/news_item.html create mode 100644 docs/_layouts/page.html create mode 100644 docs/_layouts/step.html create mode 100644 docs/_layouts/tutorials.html create mode 100644 docs/_posts/2013-05-06-jekyll-1-0-0-released.markdown create mode 100644 docs/_posts/2013-05-08-jekyll-1-0-1-released.markdown create mode 100644 docs/_posts/2013-05-12-jekyll-1-0-2-released.markdown create mode 100644 docs/_posts/2013-06-07-jekyll-1-0-3-released.markdown create mode 100644 docs/_posts/2013-07-14-jekyll-1-1-0-released.markdown create mode 100644 docs/_posts/2013-07-24-jekyll-1-1-1-released.markdown create mode 100644 docs/_posts/2013-07-25-jekyll-1-0-4-released.markdown create mode 100644 docs/_posts/2013-07-25-jekyll-1-1-2-released.markdown create mode 100644 docs/_posts/2013-09-06-jekyll-1-2-0-released.markdown create mode 100644 docs/_posts/2013-09-14-jekyll-1-2-1-released.markdown create mode 100644 docs/_posts/2013-10-28-jekyll-1-3-0-rc1-released.markdown create mode 100644 docs/_posts/2013-11-04-jekyll-1-3-0-released.markdown create mode 100644 docs/_posts/2013-11-26-jekyll-1-3-1-released.markdown create mode 100644 docs/_posts/2013-12-07-jekyll-1-4-0-released.markdown create mode 100644 docs/_posts/2013-12-09-jekyll-1-4-1-released.markdown create mode 100644 docs/_posts/2013-12-16-jekyll-1-4-2-released.markdown create mode 100644 docs/_posts/2014-01-13-jekyll-1-4-3-released.markdown create mode 100644 docs/_posts/2014-03-24-jekyll-1-5-0-released.markdown create mode 100644 docs/_posts/2014-03-27-jekyll-1-5-1-released.markdown create mode 100644 docs/_posts/2014-05-06-jekyll-turns-2-0-0.markdown create mode 100644 docs/_posts/2014-05-08-jekyll-2-0-3-released.markdown create mode 100644 docs/_posts/2014-06-04-jekyll-stickers-1-dollar-stickermule.markdown create mode 100644 docs/_posts/2014-06-28-jekyll-turns-21-i-mean-2-1-0.markdown create mode 100644 docs/_posts/2014-07-01-jekyll-2-1-1-released.markdown create mode 100644 docs/_posts/2014-07-29-jekyll-2-2-0-released.markdown create mode 100644 docs/_posts/2014-08-10-jekyll-2-3-0-released.markdown create mode 100644 docs/_posts/2014-09-09-jekyll-2-4-0-released.markdown create mode 100644 docs/_posts/2014-11-06-jekylls-midlife-crisis-jekyll-turns-2-5-0.markdown create mode 100644 docs/_posts/2014-11-08-jekyll-2-5-1-released.markdown create mode 100644 docs/_posts/2014-11-12-jekyll-2-5-2-released.markdown create mode 100644 docs/_posts/2014-12-17-alfredxing-welcome-to-jekyll-core.md create mode 100644 docs/_posts/2014-12-22-jekyll-2-5-3-released.markdown create mode 100644 docs/_posts/2015-01-20-jekyll-meet-and-greet.markdown create mode 100644 docs/_posts/2015-01-24-jekyll-3-0-0-beta1-released.markdown create mode 100644 docs/_posts/2015-02-26-introducing-jekyll-talk.markdown create mode 100644 docs/_posts/2015-10-26-jekyll-3-0-released.markdown create mode 100644 docs/_posts/2015-11-17-jekyll-3-0-1-released.markdown create mode 100644 docs/_posts/2016-01-20-jekyll-3-0-2-released.markdown create mode 100644 docs/_posts/2016-01-24-jekyll-3-1-0-released.markdown create mode 100644 docs/_posts/2016-01-28-jekyll-3-1-1-released.markdown create mode 100644 docs/_posts/2016-02-08-jekyll-3-0-3-released.markdown create mode 100644 docs/_posts/2016-02-19-jekyll-3-1-2-released.markdown create mode 100644 docs/_posts/2016-03-10-making-it-easier-to-contribute-to-jekyll.md create mode 100644 docs/_posts/2016-04-19-jekyll-3-0-4-released.markdown create mode 100644 docs/_posts/2016-04-19-jekyll-3-1-3-released.markdown create mode 100644 docs/_posts/2016-04-26-jekyll-3-0-5-released.markdown create mode 100644 docs/_posts/2016-05-18-jekyll-3-1-4-released.markdown create mode 100644 docs/_posts/2016-05-18-jekyll-3-1-5-released.markdown create mode 100644 docs/_posts/2016-05-19-jekyll-3-1-6-released.markdown create mode 100644 docs/_posts/2016-06-03-update-on-jekyll-s-google-summer-of-code-projects.markdown create mode 100644 docs/_posts/2016-07-26-jekyll-3-2-0-released.markdown create mode 100644 docs/_posts/2016-08-02-jekyll-3-2-1-released.markdown create mode 100644 docs/_posts/2016-08-24-jekyll-admin-initial-release.markdown create mode 100644 docs/_posts/2016-10-06-jekyll-3-3-is-here.md create mode 100644 docs/_posts/2016-11-14-jekyll-3-3-1-released.markdown create mode 100644 docs/_posts/2017-01-18-jekyll-3-4-0-released.markdown create mode 100644 docs/_posts/2017-03-02-jekyll-3-4-1-released.markdown create mode 100644 docs/_posts/2017-03-09-jekyll-3-4-2-released.markdown create mode 100644 docs/_posts/2017-03-21-jekyll-3-4-3-released.markdown create mode 100644 docs/_posts/2017-06-14-jekyll-3-5-0-released.markdown create mode 100644 docs/_posts/2017-07-17-jekyll-3-5-1-released.markdown create mode 100644 docs/_posts/2017-08-12-jekyll-3-5-2-released.markdown create mode 100644 docs/_posts/2017-09-21-jekyll-3-6-0-released.markdown create mode 100644 docs/_posts/2017-10-19-diversity-open-source.markdown create mode 100644 docs/_posts/2017-10-21-jekyll-3-6-2-released.markdown create mode 100644 docs/_posts/2018-01-02-jekyll-3-7-0-released.md create mode 100644 docs/_posts/2018-01-25-jekyll-3-7-2-released.md create mode 100644 docs/_posts/2018-02-19-meet-jekyll-s-new-lead-developer.markdown create mode 100644 docs/_posts/2018-02-25-jekyll-3-7-3-released.markdown create mode 100644 docs/_posts/2018-03-14-development-update.md create mode 100644 docs/_posts/2018-03-15-jekyll-3-8-0-released.markdown create mode 100644 docs/_posts/2018-05-01-jekyll-3-8-1-released.markdown create mode 100644 docs/_posts/2018-05-18-jekyll-3-8-2-released.markdown create mode 100644 docs/_posts/2018-06-04-jekyll-3-8-3-released.markdown create mode 100644 docs/_posts/2018-08-01-jekyll-sponsoring.markdown create mode 100644 docs/_posts/2018-09-19-security-fixes-for-3-6-3-7-3-8.markdown create mode 100644 docs/_posts/2018-11-04-jekyll-3-8-5-released.markdown create mode 100644 docs/_posts/2019-03-18-jekyll-4-0-0-pre-alpha1-released.markdown create mode 100644 docs/_posts/2019-07-02-jekyll-3-8-6-released.markdown create mode 100644 docs/_posts/2019-08-04-jekyll-4-0-0-pre-beta1-released.markdown create mode 100644 docs/_posts/2019-08-19-jekyll-4-0-0-released.markdown create mode 100644 docs/_posts/2020-05-08-jekyll-4-0-1-released.markdown create mode 100644 docs/_posts/2020-05-27-jekyll-4-1-0-released.markdown create mode 100644 docs/_posts/2020-06-24-jekyll-4-1-1-released.markdown create mode 100644 docs/_posts/2020-08-05-jekyll-3-9-0-released.markdown create mode 100644 docs/_posts/2020-12-14-jekyll-4-2-0-released.markdown create mode 100644 docs/_posts/2021-04-08-jekyll-3-9-1-released.markdown create mode 100644 docs/_posts/2021-09-14-goodbye-dear-frank.markdown create mode 100644 docs/_posts/2021-09-27-jekyll-4-2-1-released.markdown create mode 100644 docs/_posts/2022-03-03-jekyll-4-2-2-released.markdown create mode 100644 docs/_posts/2022-03-27-jekyll-3-9-2-released.markdown create mode 100644 docs/_posts/2022-10-20-jekyll-4-3-0-released.markdown create mode 100644 docs/_posts/2022-10-26-jekyll-4-3-1-released.markdown create mode 100644 docs/_sass/_docsearch.scss create mode 100644 docs/_sass/_font-awesome.scss create mode 100644 docs/_sass/_fonts.scss create mode 100644 docs/_sass/_gridism.scss create mode 100644 docs/_sass/_mixins.scss create mode 100644 docs/_sass/_normalize.scss create mode 100644 docs/_sass/_pygments.scss create mode 100644 docs/_sass/_style.scss create mode 100644 docs/_tutorials/cache-api.md create mode 100644 docs/_tutorials/convert-site-to-jekyll.md create mode 100644 docs/_tutorials/csv-to-table.md create mode 100644 docs/_tutorials/custom-404-page.md create mode 100644 docs/_tutorials/index.md create mode 100644 docs/_tutorials/navigation.md create mode 100644 docs/_tutorials/orderofinterpretation.md create mode 100644 docs/_tutorials/using-jekyll-with-bundler.md create mode 100644 docs/_tutorials/video-walkthroughs.md create mode 100644 docs/css/screen.scss create mode 100644 docs/favicon.ico create mode 100644 docs/fonts/FontAwesome.eot create mode 100644 docs/fonts/FontAwesome.svg create mode 100644 docs/fonts/FontAwesome.ttf create mode 100644 docs/fonts/FontAwesome.woff create mode 100644 docs/fonts/lato-v14-latin-300.woff create mode 100644 docs/fonts/lato-v14-latin-300.woff2 create mode 100644 docs/fonts/lato-v14-latin-300italic.woff create mode 100644 docs/fonts/lato-v14-latin-300italic.woff2 create mode 100644 docs/fonts/lato-v14-latin-700.woff create mode 100644 docs/fonts/lato-v14-latin-700.woff2 create mode 100644 docs/fonts/lato-v14-latin-700italic.woff create mode 100644 docs/fonts/lato-v14-latin-700italic.woff2 create mode 100644 docs/fonts/lato-v14-latin-900.woff create mode 100644 docs/fonts/lato-v14-latin-900.woff2 create mode 100644 docs/fonts/lato-v14-latin-900italic.woff create mode 100644 docs/fonts/lato-v14-latin-900italic.woff2 create mode 100644 docs/fonts/lato-v14-latin-italic.woff create mode 100644 docs/fonts/lato-v14-latin-italic.woff2 create mode 100644 docs/fonts/lato-v14-latin-regular.woff create mode 100644 docs/fonts/lato-v14-latin-regular.woff2 create mode 100644 docs/icomoon-selection.json create mode 100644 docs/img/article-footer.png create mode 100644 docs/img/footer-arrow.png create mode 100644 docs/img/footer-logo.png create mode 100644 docs/img/forestry-logo.svg create mode 100644 docs/img/jekyll-og.png create mode 100644 docs/img/jekyll-sticker.jpg create mode 100644 docs/img/jekyll-sticker.png create mode 100644 docs/img/jekylllayoutconcept.png create mode 100644 docs/img/logo-2x.png create mode 100644 docs/img/logo-rss.png create mode 100644 docs/img/octojekyll.png create mode 100644 docs/img/spacer.gif create mode 100644 docs/img/twitter-card.png create mode 100644 docs/js/html5shiv.min.js create mode 100644 docs/js/respond.min.js create mode 100644 docs/latest_version.txt create mode 100644 docs/pages/404.html create mode 100644 docs/pages/index.html create mode 100644 docs/pages/jekyllconf.md create mode 100644 docs/pages/news.html create mode 100644 docs/pages/philosophy.md create mode 100644 docs/pages/redirects/github.html create mode 100644 docs/pages/redirects/issues.html create mode 100644 docs/pages/releases.html create mode 100644 docs/pages/resources.md create mode 100644 docs/pages/showcase.html create mode 100644 docs/pages/team.md create mode 100644 docs/readme.md create mode 100644 exe/jekyll create mode 100644 features/cache.feature create mode 100644 features/collections.feature create mode 100644 features/collections_dir.feature create mode 100644 features/create_sites.feature create mode 100644 features/data.feature create mode 100644 features/drafts.feature create mode 100644 features/embed_filters.feature create mode 100644 features/frontmatter_defaults.feature create mode 100644 features/highlighting.feature create mode 100644 features/hooks.feature create mode 100644 features/include_relative_tag.feature create mode 100644 features/include_tag.feature create mode 100644 features/incremental_rebuild.feature create mode 100644 features/layout_data.feature create mode 100644 features/link_tag.feature create mode 100644 features/markdown.feature create mode 100644 features/pagination.feature create mode 100644 features/permalinks.feature create mode 100644 features/plugins.feature create mode 100644 features/post_data.feature create mode 100644 features/post_excerpts.feature create mode 100644 features/post_url_tag.feature create mode 100644 features/rendering.feature create mode 100644 features/site_configuration.feature create mode 100644 features/site_data.feature create mode 100644 features/step_definitions.rb create mode 100644 features/support/formatter.rb create mode 100644 features/support/helpers.rb create mode 100644 features/theme.feature create mode 100644 features/theme_configuration.feature create mode 100644 features/theme_gem.feature create mode 100644 jekyll.gemspec create mode 100644 lib/blank_template/_config.yml create mode 100644 lib/blank_template/_layouts/default.html create mode 100644 lib/blank_template/_sass/main.scss create mode 100644 lib/blank_template/assets/css/main.scss create mode 100644 lib/blank_template/index.md create mode 100644 lib/jekyll.rb create mode 100644 lib/jekyll/cache.rb create mode 100644 lib/jekyll/cleaner.rb create mode 100644 lib/jekyll/collection.rb create mode 100644 lib/jekyll/command.rb create mode 100644 lib/jekyll/commands/build.rb create mode 100644 lib/jekyll/commands/clean.rb create mode 100644 lib/jekyll/commands/doctor.rb create mode 100644 lib/jekyll/commands/help.rb create mode 100644 lib/jekyll/commands/new.rb create mode 100644 lib/jekyll/commands/new_theme.rb create mode 100644 lib/jekyll/commands/serve.rb 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/mime_types_charset.json create mode 100644 lib/jekyll/commands/serve/servlet.rb create mode 100644 lib/jekyll/commands/serve/websockets.rb create mode 100644 lib/jekyll/configuration.rb create mode 100644 lib/jekyll/converter.rb create mode 100644 lib/jekyll/converters/identity.rb create mode 100644 lib/jekyll/converters/markdown.rb create mode 100644 lib/jekyll/converters/markdown/kramdown_parser.rb create mode 100644 lib/jekyll/converters/smartypants.rb create mode 100644 lib/jekyll/convertible.rb create mode 100644 lib/jekyll/deprecator.rb create mode 100644 lib/jekyll/document.rb create mode 100644 lib/jekyll/drops/collection_drop.rb create mode 100644 lib/jekyll/drops/document_drop.rb create mode 100644 lib/jekyll/drops/drop.rb create mode 100644 lib/jekyll/drops/excerpt_drop.rb create mode 100644 lib/jekyll/drops/jekyll_drop.rb create mode 100644 lib/jekyll/drops/site_drop.rb create mode 100644 lib/jekyll/drops/static_file_drop.rb create mode 100644 lib/jekyll/drops/theme_drop.rb create mode 100644 lib/jekyll/drops/unified_payload_drop.rb create mode 100644 lib/jekyll/drops/url_drop.rb create mode 100644 lib/jekyll/entry_filter.rb create mode 100644 lib/jekyll/errors.rb create mode 100644 lib/jekyll/excerpt.rb create mode 100644 lib/jekyll/external.rb create mode 100644 lib/jekyll/filters.rb create mode 100644 lib/jekyll/filters/date_filters.rb create mode 100644 lib/jekyll/filters/grouping_filters.rb create mode 100644 lib/jekyll/filters/url_filters.rb create mode 100644 lib/jekyll/frontmatter_defaults.rb create mode 100644 lib/jekyll/generator.rb create mode 100644 lib/jekyll/hooks.rb create mode 100644 lib/jekyll/inclusion.rb create mode 100644 lib/jekyll/layout.rb create mode 100644 lib/jekyll/liquid_extensions.rb create mode 100644 lib/jekyll/liquid_renderer.rb create mode 100644 lib/jekyll/liquid_renderer/file.rb create mode 100644 lib/jekyll/liquid_renderer/table.rb create mode 100644 lib/jekyll/log_adapter.rb create mode 100644 lib/jekyll/mime.types create mode 100644 lib/jekyll/page.rb create mode 100644 lib/jekyll/page_excerpt.rb create mode 100644 lib/jekyll/page_without_a_file.rb create mode 100644 lib/jekyll/path_manager.rb create mode 100644 lib/jekyll/plugin.rb create mode 100644 lib/jekyll/plugin_manager.rb create mode 100644 lib/jekyll/profiler.rb create mode 100644 lib/jekyll/publisher.rb create mode 100644 lib/jekyll/reader.rb create mode 100644 lib/jekyll/readers/collection_reader.rb create mode 100644 lib/jekyll/readers/data_reader.rb create mode 100644 lib/jekyll/readers/layout_reader.rb create mode 100644 lib/jekyll/readers/page_reader.rb create mode 100644 lib/jekyll/readers/post_reader.rb create mode 100644 lib/jekyll/readers/static_file_reader.rb create mode 100644 lib/jekyll/readers/theme_assets_reader.rb create mode 100644 lib/jekyll/regenerator.rb create mode 100644 lib/jekyll/related_posts.rb create mode 100644 lib/jekyll/renderer.rb create mode 100644 lib/jekyll/site.rb create mode 100644 lib/jekyll/static_file.rb create mode 100644 lib/jekyll/stevenson.rb create mode 100644 lib/jekyll/tags/highlight.rb create mode 100644 lib/jekyll/tags/include.rb create mode 100644 lib/jekyll/tags/link.rb create mode 100644 lib/jekyll/tags/post_url.rb create mode 100644 lib/jekyll/theme.rb create mode 100644 lib/jekyll/theme_builder.rb create mode 100644 lib/jekyll/url.rb create mode 100644 lib/jekyll/utils.rb create mode 100644 lib/jekyll/utils/ansi.rb create mode 100644 lib/jekyll/utils/exec.rb create mode 100644 lib/jekyll/utils/internet.rb create mode 100644 lib/jekyll/utils/platforms.rb create mode 100644 lib/jekyll/utils/thread_event.rb create mode 100644 lib/jekyll/utils/win_tz.rb create mode 100644 lib/jekyll/version.rb create mode 100644 lib/site_template/.gitignore create mode 100644 lib/site_template/404.html create mode 100644 lib/site_template/_config.yml create mode 100644 lib/site_template/_posts/0000-00-00-welcome-to-jekyll.markdown.erb create mode 100644 lib/site_template/about.markdown create mode 100644 lib/site_template/index.markdown create mode 100644 lib/theme_template/CODE_OF_CONDUCT.md.erb create mode 100644 lib/theme_template/Gemfile create mode 100644 lib/theme_template/LICENSE.txt.erb create mode 100644 lib/theme_template/README.md.erb create mode 100644 lib/theme_template/_layouts/default.html create mode 100644 lib/theme_template/_layouts/page.html create mode 100644 lib/theme_template/_layouts/post.html create mode 100644 lib/theme_template/example/_config.yml.erb create mode 100644 lib/theme_template/example/_post.md create mode 100644 lib/theme_template/example/index.html create mode 100644 lib/theme_template/example/style.scss create mode 100644 lib/theme_template/gitignore.erb create mode 100644 lib/theme_template/theme.gemspec.erb create mode 100644 rake/profile.rake create mode 100644 rake/release.rake create mode 100644 rake/site.rake create mode 100644 rubocop/jekyll.rb create mode 100644 rubocop/jekyll/assert_equal_literal_actual.rb create mode 100644 rubocop/jekyll/no_p_allowed.rb create mode 100644 rubocop/jekyll/no_puts_allowed.rb create mode 100644 script/backport-pr create mode 100644 script/bootstrap create mode 100644 script/branding create mode 100644 script/cibuild create mode 100644 script/console create mode 100644 script/cucumber create mode 100644 script/default-site create mode 100644 script/fmt create mode 100644 script/memprof create mode 100644 script/profile-docs create mode 100644 script/proof create mode 100644 script/rubies create mode 100644 script/rubyprof create mode 100644 script/stackprof create mode 100644 script/test create mode 100644 script/travis create mode 100644 script/vendor-mimes create mode 100644 test/fixtures/broken_front_matter1.erb create mode 100644 test/fixtures/broken_front_matter2.erb create mode 100644 test/fixtures/broken_front_matter3.erb create mode 100644 test/fixtures/empty_permalink.erb create mode 100644 test/fixtures/exploit_front_matter.erb create mode 100644 test/fixtures/front_matter.erb create mode 100644 test/fixtures/no_liquid.erb create mode 100644 test/fixtures/physical.html create mode 100644 test/fixtures/sample.csv create mode 100644 test/fixtures/sample.tsv create mode 100644 test/fixtures/test-dependency-theme/test-dependency-theme.gemspec create mode 100644 test/fixtures/test-theme-skinny/_layouts/default.html create mode 100644 test/fixtures/test-theme-skinny/_layouts/home.html create mode 100644 test/fixtures/test-theme-skinny/test-theme-skinny.gemspec create mode 100644 test/fixtures/test-theme-symlink/test-theme-symlink.gemspec create mode 100644 test/fixtures/test-theme-w-empty-data/_data/.gitkeep create mode 100644 test/fixtures/test-theme-w-empty-data/_layouts/default.html create mode 100644 test/fixtures/test-theme-w-empty-data/test-theme-w-empty-data.gemspec create mode 100644 test/fixtures/test-theme/_config.yml create mode 100644 test/fixtures/test-theme/_data/cars.yml create mode 100644 test/fixtures/test-theme/_data/categories/dairy.yaml create mode 100644 test/fixtures/test-theme/_data/greetings.yml create mode 100644 test/fixtures/test-theme/_data/i18n/testimonials.yml create mode 100644 test/fixtures/test-theme/_includes/include.html create mode 100644 test/fixtures/test-theme/_includes/testimonials.html create mode 100644 test/fixtures/test-theme/_layouts/default.html create mode 100644 test/fixtures/test-theme/_sass/test-theme-black.scss create mode 100644 test/fixtures/test-theme/_sass/test-theme-red.scss create mode 100644 test/fixtures/test-theme/_symlink create mode 100644 test/fixtures/test-theme/assets/application.coffee create mode 100644 test/fixtures/test-theme/assets/base.js create mode 100644 test/fixtures/test-theme/assets/img/another-logo.png create mode 100644 test/fixtures/test-theme/assets/img/logo.png create mode 100644 test/fixtures/test-theme/assets/style.scss create mode 100644 test/fixtures/test-theme/test-theme.gemspec create mode 100644 test/fixtures/webrick/bar.html create mode 100644 test/fixtures/webrick/bar/baz.html create mode 100644 test/fixtures/webrick/bar/foo.xhtml create mode 100644 test/helper.rb create mode 100644 test/safe_glob_test[/find_me.txt create mode 100644 test/simplecov_custom_profile.rb create mode 100644 test/source/+/%# +.md create mode 100644 test/source/+/foo.md create mode 100644 test/source/.gitattributes create mode 100644 test/source/.htaccess create mode 100644 test/source/_broken/bad_post.md create mode 100644 test/source/_config.dev.toml create mode 100644 test/source/_config_folded.yml create mode 100644 test/source/_data/categories.01/dairy.yaml create mode 100644 test/source/_data/categories/dairy.yaml create mode 100644 test/source/_data/greetings.yml create mode 100644 test/source/_data/i18n.yml create mode 100644 test/source/_data/languages.yml create mode 100644 test/source/_data/members.json create mode 100644 test/source/_data/members.yaml create mode 100644 test/source/_data/products.yml create mode 100644 test/source/_dates/date_without_time.md create mode 100644 test/source/_dates/time_with_timezone.md create mode 100644 test/source/_dates/time_without_timezone.md create mode 100644 test/source/_drafts/draft-properties.text create mode 100644 test/source/_encodings/UTF8CRLFandBOM.md create mode 100644 test/source/_encodings/Unicode16LECRLFandBOM.md create mode 100644 test/source/_glob_include_test/_is_dir/include_me.txt create mode 100644 test/source/_glob_include_test/_not_dir create mode 100644 test/source/_includes/include.html create mode 100644 test/source/_includes/params.html create mode 100644 test/source/_includes/params@2.0.html create mode 100644 test/source/_includes/sig.markdown create mode 100644 test/source/_includes/tmp create mode 100644 test/source/_includes_custom/custom.html create mode 100644 test/source/_layouts/default.html create mode 100644 test/source/_layouts/post/simple.html create mode 100644 test/source/_layouts/simple.html create mode 100644 test/source/_methods/3940394-21-9393050-fifif1323-test.md create mode 100644 test/source/_methods/_do_not_read_me.md create mode 100644 test/source/_methods/collection/entries create mode 100644 test/source/_methods/configuration.md create mode 100644 test/source/_methods/escape-+ #%20[].md create mode 100644 test/source/_methods/extensionless_static_file create mode 100644 test/source/_methods/sanitized_path.md create mode 100644 test/source/_methods/site/_dont_include_me_either.md create mode 100644 test/source/_methods/site/generate.md create mode 100644 test/source/_methods/site/initialize.md create mode 100644 test/source/_methods/trailing-dots...md create mode 100644 test/source/_methods/um_hi.md create mode 100644 test/source/_methods/with.dots/.gitignore create mode 100644 test/source/_methods/with.dots/.htaccess create mode 100644 test/source/_methods/yaml_with_dots.md create mode 100644 test/source/_plugins/custom_block.rb create mode 100644 test/source/_plugins/dummy.rb create mode 100644 test/source/_posts/2008-02-02-not-published.markdown create mode 100644 test/source/_posts/2008-02-02-published.markdown create mode 100644 test/source/_posts/2008-02-03-wrong-extension.yml create mode 100644 test/source/_posts/2008-10-18-foo-bar.markdown create mode 100644 test/source/_posts/2008-11-21-complex.markdown create mode 100644 test/source/_posts/2008-12-03-permalinked-post.markdown create mode 100644 test/source/_posts/2008-12-13-include.markdown create mode 100644 test/source/_posts/2009-01-27-array-categories.markdown create mode 100644 test/source/_posts/2009-01-27-categories.markdown create mode 100644 test/source/_posts/2009-01-27-category.markdown create mode 100644 test/source/_posts/2009-01-27-empty-categories.markdown create mode 100644 test/source/_posts/2009-01-27-empty-category.markdown create mode 100644 test/source/_posts/2009-01-27-no-category.markdown create mode 100644 test/source/_posts/2009-03-12-hash-#1.markdown create mode 100644 test/source/_posts/2009-05-18-empty-tag.markdown create mode 100644 test/source/_posts/2009-05-18-empty-tags.markdown create mode 100644 test/source/_posts/2009-05-18-tag.markdown create mode 100644 test/source/_posts/2009-05-18-tags.markdown create mode 100644 test/source/_posts/2009-06-22-empty-yaml.markdown create mode 100644 test/source/_posts/2009-06-22-no-yaml.markdown create mode 100644 test/source/_posts/2010-01-08-triple-dash.markdown create mode 100644 test/source/_posts/2010-01-09-date-override.markdown create mode 100644 test/source/_posts/2010-01-09-time-override.markdown create mode 100644 test/source/_posts/2010-01-09-timezone-override.markdown create mode 100644 test/source/_posts/2010-01-16-override-data.markdown create mode 100644 test/source/_posts/2011-04-12-md-extension.md create mode 100644 test/source/_posts/2011-04-12-text-extension.text create mode 100644 test/source/_posts/2013-01-02-post-excerpt.markdown create mode 100644 test/source/_posts/2013-01-12-nil-layout.markdown create mode 100644 test/source/_posts/2013-01-12-no-layout.markdown create mode 100644 test/source/_posts/2013-03-19-not-a-post.markdown/.gitkeep create mode 100644 test/source/_posts/2013-03-19-not-a-post/dubious.markdown create mode 100644 test/source/_posts/2013-04-11-custom-excerpt.markdown create mode 100644 test/source/_posts/2013-05-10-number-category.markdown create mode 100644 test/source/_posts/2013-07-22-post-excerpt-with-layout.markdown create mode 100644 test/source/_posts/2013-08-01-mkdn-extension.mkdn create mode 100644 test/source/_posts/2013-12-17-include-variable-filters.markdown create mode 100644 test/source/_posts/2013-12-20-properties.text create mode 100644 test/source/_posts/2014-01-06-permalink-traversal.md create mode 100644 test/source/_posts/2014-03-03-yaml-with-dots.md create mode 100644 test/source/_posts/2014-03-22-escape-+ %20[].markdown create mode 100644 test/source/_posts/2014-07-05-another-mixed-case-category.markdown create mode 100644 test/source/_posts/2014-07-05-mixed-case-category.markdown create mode 100644 test/source/_posts/2014-09-02-relative-includes.markdown create mode 100644 test/source/_posts/2014-11-24-Rmd-extension.Rmd create mode 100644 test/source/_posts/2015-01-08-post-excerpt-separator.markdown create mode 100644 test/source/_posts/2015-02-20-extensionless-permalink.markdown create mode 100644 test/source/_posts/2015-12-27-extra-spaces.markdown create mode 100644 test/source/_posts/2016-08-16-indented-link-references.markdown create mode 100644 test/source/_posts/2016-11-26-special-chars-(+).markdown create mode 100644 test/source/_posts/2017-2-5-i-dont-like-zeroes.md create mode 100644 test/source/_posts/2018-01-28-closed-liquid-block-excerpt.markdown create mode 100644 test/source/_posts/2018-01-28-open-liquid-block-excerpt.markdown create mode 100644 test/source/_posts/2018-05-15-closed-liquid-block-excerpt-whitespace-control.md create mode 100644 test/source/_posts/2018-05-15-excerpt-whitespace-control-variable.md create mode 100644 test/source/_posts/2018-05-15-open-liquid-block-excerpt-whitespace-control.md create mode 100644 test/source/_posts/2018-10-12-trailing-dots...markdown create mode 100644 test/source/_posts/2018-11-15-excerpt-liquid-block.md create mode 100644 test/source/_posts/es/2008-11-21-nested.markdown create mode 100644 test/source/_posts/include_relative/params.html create mode 100644 test/source/_posts/include_relative/rel_include.html create mode 100644 test/source/_roles/named.md create mode 100644 test/source/_roles/unnamed.md create mode 100644 test/source/_sass/_grid.scss create mode 100644 test/source/_slides/example-slide-1.html create mode 100644 test/source/_slides/example-slide-2.html create mode 100644 test/source/_slides/example-slide-3.html create mode 100644 test/source/_slides/example-slide-4.html create mode 100644 test/source/_slides/example-slide-5.html create mode 100644 test/source/_slides/example-slide-6.html create mode 100644 test/source/_slides/example-slide-7.md create mode 100644 test/source/_slides/example-slide-Upper-Cased.html create mode 100644 test/source/_slides/non-outputted-slide.html create mode 100644 test/source/_slides/octojekyll.png create mode 100644 test/source/_thanksgiving/2015-11-26-thanksgiving.md create mode 100644 test/source/_thanksgiving/black-friday.md create mode 100644 test/source/_tutorials/dive-in-and-publish-already.md create mode 100644 test/source/_tutorials/extending-with-plugins.md create mode 100644 test/source/_tutorials/getting-started.md create mode 100644 test/source/_tutorials/graduation-day.md create mode 100644 test/source/_tutorials/lets-roll.md create mode 100644 test/source/_tutorials/tip-of-the-iceberg.md create mode 100644 test/source/_urls_differ_by_case_invalid/page1.html create mode 100644 test/source/_urls_differ_by_case_invalid/page2.html create mode 100644 test/source/_urls_differ_by_case_valid/page1.html create mode 100644 test/source/_with.dots/all.dots/2.4.0.md create mode 100644 test/source/_with.dots/file.with.dots.md create mode 100644 test/source/_with.dots/mit.txt create mode 100644 test/source/_with.dots/permalink.with.slash.tho.md create mode 100644 test/source/about.html create mode 100644 test/source/assets/application.coffee create mode 100644 test/source/assets/base.js create mode 100644 test/source/assets/test-styles.scss create mode 100644 test/source/category/_posts/2008-09-23-categories.markdown create mode 100644 test/source/contacts.html create mode 100644 test/source/contacts/bar.html create mode 100644 test/source/contacts/foo.md create mode 100644 test/source/contacts/humans.txt create mode 100644 test/source/contacts/index.html create mode 100644 test/source/css/main.scss create mode 100644 test/source/css/screen.css create mode 100644 test/source/deal.with.dots.html create mode 100644 test/source/dynamic_file.php create mode 100644 test/source/environment.html create mode 100644 test/source/exploit.md create mode 100644 test/source/foo/_posts/bar/2008-12-12-topical-post.markdown create mode 100644 test/source/index.html create mode 100644 test/source/info.md create mode 100644 test/source/js/coffeescript.coffee create mode 100644 test/source/pgp.key create mode 100644 test/source/products.yml create mode 100644 test/source/properties.html create mode 100644 test/source/sitemap.xml create mode 100644 test/source/static_files.html create mode 100644 test/source/symlink-test/_data create mode 100644 test/source/symlink-test/symlinked-dir create mode 100644 test/source/symlink-test/symlinked-file create mode 100644 test/source/symlink-test/symlinked-file-outside-source create mode 100644 test/source/trailing-dots...md create mode 100644 test/source/unpublished.html create mode 100644 test/source/win/_posts/2009-05-24-yaml-linebreak.markdown create mode 100644 test/source/z_category/_posts/2008-09-23-categories.markdown create mode 100644 test/test_ansi.rb create mode 100644 test/test_cleaner.rb create mode 100644 test/test_coffeescript.rb create mode 100644 test/test_collections.rb create mode 100644 test/test_command.rb create mode 100644 test/test_commands_serve.rb create mode 100644 test/test_commands_serve_servlet.rb create mode 100644 test/test_configuration.rb create mode 100644 test/test_convertible.rb create mode 100644 test/test_data_reader.rb create mode 100644 test/test_doctor_command.rb create mode 100644 test/test_document.rb create mode 100644 test/test_drop.rb create mode 100644 test/test_entry_filter.rb create mode 100644 test/test_excerpt.rb create mode 100644 test/test_excerpt_drop.rb create mode 100644 test/test_filters.rb create mode 100644 test/test_front_matter_defaults.rb create mode 100644 test/test_generated_site.rb create mode 100644 test/test_kramdown.rb create mode 100644 test/test_layout_reader.rb create mode 100644 test/test_liquid_extensions.rb create mode 100644 test/test_liquid_renderer.rb create mode 100644 test/test_log_adapter.rb create mode 100644 test/test_new_command.rb create mode 100644 test/test_page.rb create mode 100644 test/test_page_without_a_file.rb create mode 100644 test/test_path_manager.rb create mode 100644 test/test_path_sanitization.rb create mode 100644 test/test_plugin_manager.rb create mode 100644 test/test_post_reader.rb create mode 100644 test/test_regenerator.rb create mode 100644 test/test_related_posts.rb create mode 100644 test/test_sass.rb create mode 100644 test/test_site.rb create mode 100644 test/test_site_drop.rb create mode 100644 test/test_static_file.rb create mode 100644 test/test_tags.rb create mode 100644 test/test_theme.rb create mode 100644 test/test_theme_assets_reader.rb create mode 100644 test/test_theme_data_reader.rb create mode 100644 test/test_theme_drop.rb create mode 100644 test/test_url.rb create mode 100644 test/test_utils.rb create mode 100644 test/test_win_tz.rb diff --git a/.codeclimate.yml b/.codeclimate.yml new file mode 100644 index 0000000..d9f5d35 --- /dev/null +++ b/.codeclimate.yml @@ -0,0 +1,53 @@ +version: "2" +checks: + argument-count: + enabled: true + config: + threshold: 5 + file-lines: + enabled: true + config: + threshold: 300 + method-complexity: + enabled: true + config: + threshold: 15 + method-count: + enabled: true + config: + threshold: 50 + method-lines: + enabled: true + config: + threshold: 30 +plugins: + fixme: + enabled: false + rubocop: + enabled: true + channel: rubocop-0-60 + +exclude_patterns: + - "*.*" + - ".*" + + - Gemfile + - LICENSE + - Rakefile + + - benchmark/ + - docs/ + - exe/ + - features/ + - rake/ + - rubocop/ + - script/ + - spec/ + - test/ + - vendor/ + + - lib/blank_template/ + - lib/site_template/ + - lib/theme_template/ + - lib/jekyll/mime.types + - lib/jekyll/commands/serve/livereload_assets/livereload.js diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 0000000..d79e1e7 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,55 @@ +#------------------------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information. +#------------------------------------------------------------------------------------------------------------- + +FROM ruby:2 + +# Avoid warnings by switching to noninteractive +ENV DEBIAN_FRONTEND=noninteractive + +# This Dockerfile adds a non-root user with sudo access. Use the "remoteUser" +# property in devcontainer.json to use it. On Linux, the container user's GID/UIDs +# will be updated to match your local UID/GID (when using the dockerFile property). +# See https://aka.ms/vscode-remote/containers/non-root-user for details. +ARG USERNAME=vscode +ARG USER_UID=1000 +ARG USER_GID=$USER_UID + +# Configure apt and install packages +RUN apt-get update \ + && apt-get -y install --no-install-recommends apt-utils dialog locales 2>&1 \ + # Verify git, process tools installed + && apt-get -y install git openssh-client iproute2 procps lsb-release \ + # + # Install ruby-debug-ide and debase + && gem install ruby-debug-ide \ + && gem install debase \ + # + # Install node.js + && apt-get -y install curl software-properties-common \ + && curl -sL https://deb.nodesource.com/setup_13.x | bash - \ + && apt-get -y install nodejs \ + # + # Create a non-root user to use if preferred - see https://aka.ms/vscode-remote/containers/non-root-user. + && groupadd --gid $USER_GID $USERNAME \ + && useradd -s /bin/bash --uid $USER_UID --gid $USER_GID -m $USERNAME \ + # [Optional] Add sudo support for the non-root user + && apt-get install -y sudo \ + && echo $USERNAME ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/$USERNAME\ + && chmod 0440 /etc/sudoers.d/$USERNAME \ + # + # Clean up + && apt-get autoremove -y \ + && apt-get clean -y \ + && rm -rf /var/lib/apt/lists/* + +# Set the locale +RUN sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen && \ + dpkg-reconfigure --frontend=noninteractive locales && \ + update-locale LANG=en_US.UTF-8 + +ENV LANG en_US.UTF-8 + +# Switch back to dialog for any ad-hoc use of apt-get +ENV DEBIAN_FRONTEND=dialog diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..6b449c6 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,26 @@ +// For format details, see https://aka.ms/vscode-remote/devcontainer.json or this file's README at: +// https://github.com/microsoft/vscode-dev-containers/tree/v0.101.1/containers/ruby-2 +{ + "name": "Ruby 2", + "dockerFile": "Dockerfile", + + // Set *default* container specific settings.json values on container create. + "settings": { + "terminal.integrated.shell.linux": "/bin/bash" + }, + + // Add the IDs of extensions you want installed when the container is created. + "extensions": [ + "rebornix.Ruby" + ] + + // Use 'forwardPorts' to make a list of ports inside the container available locally. + // "forwardPorts": [], + + // Use 'postCreateCommand' to run commands after the container is created. + "postCreateCommand": "bundle install", + + // Uncomment to connect as a non-root user. See https://aka.ms/vscode-remote/containers/non-root. + // "remoteUser": "vscode" + +} \ No newline at end of file diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..9d5248e --- /dev/null +++ b/.editorconfig @@ -0,0 +1,14 @@ +# editorconfig.org + +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_size = 2 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true + +[*.md] +trim_trailing_whitespace = false diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..176a458 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text=auto diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..a6f9434 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,86 @@ +# The Jekyll project has 6 affinity teams, shown here: https://teams.jekyllrb.com/ +# They are as follows: +# +# 1. @jekyll/build +# 2. @jekyll/documentation +# 3. @jekyll/ecosystem +# 4. @jekyll/performance +# 5. @jekyll/stability +# 6. @jekyll/windows +# +# Each of these teams has a mission. Wherever possible, GitHub should +# automatically require review from these teams on the pieces of the +# repository they maintain. + +# @jekyll/documentation +/docs/ @jekyll/documentation + +# @jekyll/build +/exe/ @jekyll/build +/lib/jekyll.rb @jekyll/build +/lib/jekyll/cleaner.rb @jekyll/build +/lib/jekyll/collection.rb @jekyll/build +/lib/jekyll/command.rb @jekyll/build +/lib/jekyll/commands/ @jekyll/build +/lib/jekyll/converter.rb @jekyll/build +/lib/jekyll/converters/ @jekyll/build +/lib/jekyll/convertible.rb @jekyll/build +/lib/jekyll/document.rb @jekyll/build +/lib/jekyll/drops/ @jekyll/build +/lib/jekyll/entry_filter.rb @jekyll/build +/lib/jekyll/errors.rb @jekyll/build +/lib/jekyll/excerpt.rb @jekyll/build +/lib/jekyll/filters/ @jekyll/build +/lib/jekyll/filters.rb @jekyll/build +/lib/jekyll/layout.rb @jekyll/build +/lib/jekyll/liquid_extensions.rb @jekyll/build +/lib/jekyll/liquid_renderer/ @jekyll/build +/lib/jekyll/liquid_renderer.rb @jekyll/build +/lib/jekyll/log_adapter.rb @jekyll/build +/lib/jekyll/mime.types @jekyll/build +/lib/jekyll/page.rb @jekyll/build +/lib/jekyll/publisher.rb @jekyll/build +/lib/jekyll/reader.rb @jekyll/build +/lib/jekyll/readers/ @jekyll/build +/lib/jekyll/regenerator.rb @jekyll/build +/lib/jekyll/related_posts.rb @jekyll/build +/lib/jekyll/renderer.rb @jekyll/build +/lib/jekyll/site.rb @jekyll/build +/lib/jekyll/static_file.rb @jekyll/build +/lib/jekyll/stevenson.rb @jekyll/build +/lib/jekyll/tags/ @jekyll/build +/lib/jekyll/url.rb @jekyll/build +/lib/jekyll/utils/ @jekyll/build +/lib/jekyll/utils.rb @jekyll/build + +# @jekyll/ecosystem +/lib/jekyll/external.rb @jekyll/ecosystem +/lib/jekyll/generator.rb @jekyll/ecosystem +/lib/jekyll/hooks.rb @jekyll/ecosystem +/lib/jekyll/plugin.rb @jekyll/ecosystem +/lib/jekyll/plugin_manager.rb @jekyll/ecosystem +/lib/jekyll/theme.rb @jekyll/ecosystem +/lib/jekyll/theme_builder.rb @jekyll/ecosystem + +# @jekyll/stability +Gemfile @jekyll/stability +*.gemspec @jekyll/stability +.travis.yml @jekyll/stability +appveyor.yml @jekyll/stability +/lib/jekyll/configuration.rb @jekyll/stability +/lib/jekyll/deprecator.rb @jekyll/stability +/lib/jekyll/frontmatter_defaults.rb @jekyll/stability +/lib/site_template @jekyll/stability +/lib/theme_template @jekyll/stability +/features/ @jekyll/stability +/test/ @jekyll/stability + +# Special cases +.github/ @jekyll/affinity-team-captains +CODE_OF_CONDUCT.markdown @jekyll/affinity-team-captains +History.markdown @jekyll/affinity-team-captains +LICENSE @jekyll/affinity-team-captains # This file should never change. +README.markdown @jekyll/affinity-team-captains +/lib/jekyll/version.rb @jekyll/affinity-team-captains +/rake/ @jekyll/affinity-team-captains +/script/ @jekyll/affinity-team-captains diff --git a/.github/CODE_OF_CONDUCT.markdown b/.github/CODE_OF_CONDUCT.markdown new file mode 100644 index 0000000..0ab543e --- /dev/null +++ b/.github/CODE_OF_CONDUCT.markdown @@ -0,0 +1,76 @@ +# Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, sex characteristics, gender identity and expression, +level of experience, education, socio-economic status, nationality, personal +appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or + advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at [olivia@jekyllrb.com](mailto:olivia@jekyllrb.com). All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at [https://www.contributor-covenant.org/version/1/4/code-of-conduct.html](https://www.contributor-covenant.org/version/1/4/code-of-conduct.html) + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see +[https://www.contributor-covenant.org/faq](https://www.contributor-covenant.org/faq) diff --git a/.github/CONTRIBUTING.markdown b/.github/CONTRIBUTING.markdown new file mode 100644 index 0000000..3ae6c74 --- /dev/null +++ b/.github/CONTRIBUTING.markdown @@ -0,0 +1,158 @@ +# Contributing to Jekyll + +Hi there! Interested in contributing to Jekyll? We'd love your help. Jekyll is an open source project, built one contribution at a time by users like you. + +## Where to get help or report a problem + +See the [support guidelines](https://jekyllrb.com/docs/support/) + +## Ways to contribute + +Whether you're a developer, a designer, or just a Jekyll devotee, there are lots of ways to contribute. Here's a few ideas: + +- [Install Jekyll on your computer](https://jekyllrb.com/docs/installation/) and kick the tires. Does it work? Does it do what you'd expect? If not, [open an issue](https://github.com/jekyll/jekyll/issues/new) and let us know. +- Comment on some of the project's [open issues](https://github.com/jekyll/jekyll/issues). Have you experienced the same problem? Know a work around? Do you have a suggestion for how the feature could be better? +- Read through the [documentation](https://jekyllrb.com/docs/home/), and click the "improve this page" button, any time you see something confusing, or have a suggestion for something that could be improved. +- Browse through the [Jekyll discussion forum](https://talk.jekyllrb.com/), and lend a hand answering questions. There's a good chance you've already experienced what another user is experiencing. +- Find an [open issue](https://github.com/jekyll/jekyll/issues) (especially [those labeled `help-wanted`](https://github.com/jekyll/jekyll/issues?q=is%3Aopen+is%3Aissue+label%3Ahelp-wanted)), and submit a proposed fix. If it's your first pull request, we promise we won't bite, and are glad to answer any questions. +- Help evaluate [open pull requests](https://github.com/jekyll/jekyll/pulls), by testing the changes locally and reviewing what's proposed. + +## Submitting a pull request + +### Pull requests generally + +- The smaller the proposed change, the better. If you'd like to propose two unrelated changes, submit two pull requests. + +- The more information, the better. Make judicious use of the pull request body. Describe what changes were made, why you made them, and what impact they will have for users. + +- If this is your first pull request, it may help to [understand GitHub Flow](https://guides.github.com/introduction/flow/). + +- If you're submitting a code contribution, be sure to read the [code contributions](#code-contributions) section below. + +### Submitting a pull request via github.com + +Many small changes can be made entirely through the github.com web interface. + +1. Navigate to the file within [`jekyll/jekyll`](https://github.com/jekyll/jekyll) that you'd like to edit. +2. Click the pencil icon in the top right corner to edit the file +3. Make your proposed changes +4. Click "Propose file change" +5. Click "Create pull request" +6. Add a descriptive title and detailed description for your proposed change. The more information the better. +7. Click "Create pull request" + +That's it! You'll be automatically subscribed to receive updates as others review your proposed change and provide feedback. + +### Submitting a pull request via Git command line + +1. Fork the project by clicking "Fork" in the top right corner of [`jekyll/jekyll`](https://github.com/jekyll/jekyll). +2. Clone the repository locally `git clone https://github.com/<you-username>/jekyll`. +3. Create a new, descriptively named branch to contain your change ( `git checkout -b my-awesome-feature` ). +4. Hack away, add tests. Not necessarily in that order. +5. Make sure everything still passes by running `script/cibuild` (see the [tests section](#running-tests-locally) below) +6. Push the branch up ( `git push origin my-awesome-feature` ). +7. Create a pull request by visiting `https://github.com/<your-username>/jekyll` and following the instructions at the top of the screen. + +## Proposing updates to the documentation + +We want the Jekyll documentation to be the best it can be. We've open-sourced our docs and we welcome any pull requests if you find it lacking. + +### How to submit changes + +You can find the documentation for jekyllrb.com in the [docs](https://github.com/jekyll/jekyll/tree/master/docs) directory. See the section above, [submitting a pull request](#submitting-a-pull-request) for information on how to propose a change. + +One gotcha, all pull requests should be directed at the `master` branch (the default branch). + +### Updating FontAwesome iconset for jekyllrb.com + +We use a custom version of FontAwesome which contains just the icons we use. + +If you ever need to update our documentation with an icon that is not already available in our custom iconset, you'll have to regenerate the iconset using Icomoon's Generator: + +1. Go to <https://icomoon.io/app/>. +2. Click `Import Icons` on the top-horizontal-bar and upload the existing `<jekyll>/docs/icomoon-selection.json`. +3. Click `Add Icons from Library..` further down on the page, and add 'Font Awesome'. +4. Select the required icon(s) from the Library (make sure its the 'FontAwesome' library instead of 'IcoMoon-Free' library). +5. Click `Generate Font` on the bottom-horizontal-bar. +6. Inspect the included icons and proceed by clicking `Download`. +7. Extract the font files and adapt the CSS to the paths we use in Jekyll: + +- Copy the entire `fonts` directory over and overwrite existing ones at `<jekyll>/docs/`. +- Copy the contents of `selection.json` and overwrite existing content inside `<jekyll>/docs/icomoon-selection.json`. +- Copy the entire `@font-face {}` declaration and only the **new-icon(s)' css declarations** further below, to update the + `<jekyll>/docs/_sass/_font-awesome.scss` sass partial. +- Fix paths in the `@font-face {}` declaration by adding `../` before `fonts/FontAwesome.*` like so: + `('../fonts/Fontawesome.woff?9h6hxj')`. + +### Adding plugins + +If you want to add your plugin to the [list of plugins](https://jekyllrb.com/docs/plugins/#available-plugins), please submit a pull request modifying the [plugins page source file](https://github.com/jekyll/jekyll/blob/master/docs/_docs/plugins.md) by adding a link to your plugin under the proper subheading depending upon its type. + +## Code Contributions + +Interested in submitting a pull request? Awesome. Read on. There's a few common gotchas that we'd love to help you avoid. + +### Tests and documentation + +Any time you propose a code change, you should also include updates to the documentation and tests within the same pull request. + +#### Documentation + +If your contribution changes any Jekyll behavior, make sure to update the documentation. Documentation lives in the `docs/_docs` folder (spoiler alert: it's a Jekyll site!). If the docs are missing information, please feel free to add it in. Great docs make a great project. Include changes to the documentation within your pull request, and once merged, `jekyllrb.com` will be updated. + +#### Tests + +- If you're creating a small fix or patch to an existing feature, a simple test is more than enough. You can usually copy/paste from an existing example in the `tests` folder, but if you need you can find out about our tests suites [Shoulda](https://github.com/thoughtbot/shoulda/tree/master) and [RSpec-Mocks](https://github.com/rspec/rspec-mocks). + +- If it's a brand new feature, create a new [Cucumber](https://github.com/cucumber/cucumber/) feature, reusing existing steps where appropriate. + +### Code contributions generally + +- Jekyll uses the [Rubocop](https://github.com/bbatsov/rubocop) static analyzer to ensure that contributions follow the [GitHub Ruby Styleguide](https://github.com/styleguide/ruby). Please check your code using `script/fmt` and resolve any errors before pushing your branch. + +- Don't bump the Gem version in your pull request (if you don't know what that means, you probably didn't). + +- You can use the command `script/console` to start a REPL to explore the result of + Jekyll's methods. It also provides you with helpful methods to quickly create a + site or configuration. [Feel free to check it out!](https://github.com/jekyll/jekyll/blob/master/script/console) + +- Previously, we've used the WIP Probot app to help contributors determine whether their pull request is ready for review. Please use a [draft pull request](https://help.github.com/en/articles/about-pull-requests#draft-pull-requests) instead. When you're ready, [mark the pull request as ready for review](https://help.github.com/en/articles/changing-the-stage-of-a-pull-request) + +## Running tests locally + +### Test Dependencies + +To run the test suite and build the gem you'll need to install Jekyll's dependencies by running the following command: + +```sh +script/bootstrap +``` + +Before you make any changes, run the tests and make sure that they pass (to confirm your environment is configured properly): + +```sh +script/cibuild +``` + +If you are only updating a file in `test/`, you can use the command: + +```sh +script/test test/blah_test.rb +``` + +If you are only updating a `.feature` file, you can use the command: + +```sh +script/cucumber features/blah.feature +``` + +Both `script/test` and `script/cucumber` can be run without arguments to +run its entire respective suite. + +## Visual Studio Code Development Container + +If you've got [Visual Studio Code](https://code.visualstudio.com/) with the [Remote Development Extension Pack](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.vscode-remote-extensionpack) installed then simply opening this repository in Visual Studio Code and following the prompts to "Re-open In A Development Container" will get you setup and ready to go with a fresh environment with all the requirements installed. + +## A thank you + +Thanks! Hacking on Jekyll should be fun. If you find any of this hard to figure out, let us know so we can improve our process or documentation! diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..d72014b --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,5 @@ +# These are supported funding model platforms + +# github: jekyll +open_collective: jekyll +tidelift: rubygems/jekyll diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000..766e2b7 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,104 @@ +name: Bug Report +description: "Is something not working as expected?" +title: "[Bug]: " +body: + - type: markdown + attributes: + value: | + Hi! Thank you for taking the time to report a bug with Jekyll. + + Please consider asking your question at https://talk.jekyllrb.com if one or more of the following is applicable to your situation: + + - You are not sure if the issue is a bug in Jekyll. + - The issue is caused by a third-party plugin. + - This is just a generic usage question. + + Additionally, please note that this platform is meant for bugs in Jekyll core only. + Issues regarding dependencies and plugins should be reported in their respective repositories. + - type: input + id: os + attributes: + label: Operating System + description: The operating system of your computer. + placeholder: "Ubuntu 21.10" + validations: + required: true + - type: input + id: ruby-version + attributes: + label: Ruby Version + description: | + The Ruby version you were using at the time. + Run `ruby -v` in your terminal and paste the output in the input field. + placeholder: "ruby 2.7.3p183 (2021-04-05 revision 6847ee089d) [x64-mingw32]" + validations: + required: true + - type: input + id: jekyll-version + attributes: + label: Jekyll Version + description: | + The version of Jekyll used in your project. + Run `bundle exec jekyll -v` and paste the output in the input field. + *If you are not using a Gemfile, run `jekyll -v` instead.* + placeholder: "jekyll 4.2.1" + validations: + required: true + - type: input + id: ghp-version + attributes: + label: GitHub Pages Version + description: | + Are you deploying your site using GitHub Pages? + If yes, then we need to know the `github-pages` version used by your project. Proceed ahead otherwise. + If you're using the `github-pages` gem in your Gemfile, paste the output from running the following: + ``` + bundle exec github-pages -v + ``` + Otherwise, enter `Latest` in the input field and proceed ahead. + - type: textarea + id: expected + attributes: + label: Expected Behavior + description: Briefly describe what you expected to see or get with a certain functionality. + placeholder: | + I expected my site to be built successfully when I run the following: + ``` + bundle exec jekyll build + ``` + validations: + required: true + - type: textarea + id: actual + attributes: + label: Current Behavior + description: > + Describe the details of the bug. + Be sure to include any steps you took for the problem to exist, such as the directories + you created and the full command you ran. + Include any plugins you have configured for use in the site. + validations: + required: true + - type: textarea + id: logs + attributes: + label: Relevant log output + description: | + Please copy and paste any relevant log output from your terminal. + *Note: This will be automatically formatted into code, so no need for backticks.* + render: shell + - type: textarea + id: sample + attributes: + label: Code Sample + description: > + The easiest way for someone to understand an issue is if they could reproduce your issue + in their environment. Therefore, please provide a link to your project repository alongwith + instructions to reproduce your issue. If your project is not publicly accessible, please + consider setting up a minimal test repository complete with necessary instructions. + placeholder: | + ### Steps to reproduce issue + + - Clone [my repo](https://github.com/owner/repo) + - Install site dependencies + - Run `bundle exec jekyll build -s src -d src/dist` diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..037c1df --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: true +contact_links: + - name: Jekyll Community Forum + url: https://talk.jekyllrb.com/ + about: Please ask and answer questions here. diff --git a/.github/ISSUE_TEMPLATE/documentation.md b/.github/ISSUE_TEMPLATE/documentation.md new file mode 100644 index 0000000..a8e11cf --- /dev/null +++ b/.github/ISSUE_TEMPLATE/documentation.md @@ -0,0 +1,24 @@ +--- +name: Documentation +about: Found a typo or something that isn't crystal clear in our docs? +title: '[Docs]: ' +labels: documentation +assignees: '' + +--- + +<!-- Thanks for taking the time to open an issue and help us make Jekyll better! --> + +## Motivation + +<!-- Why should we update our docs? --> + + + +## Suggestion + +<!-- What should we do instead? --> + + + +<!-- Thanks for taking the time to open an issue and help us make Jekyll better! --> diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..fd2c5b4 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,73 @@ +--- +name: Feature Request +about: Want us to add any features to Jekyll? +title: 'feat: ' +labels: feature +assignees: '' + +--- + +<!-- + Hi! Thanks for considering to file a feature request with Jekyll. Please take the time to + answer the basic questions. Please try to be as detailed as possible. + + Thanks! +--> + +## Summary + +<!-- + A one-paragraph explanation of the feature. +--> + +## Motivation + +<!-- + Why do you want to see this feature in Jekyll? What makes you sure that it should not be + implemented at the plugin level, but in Jekyll core? What use cases does it support? + + NOTE: Please be mindful of the Jekyll philosophy (https://jekyllrb.com/philosophy/), + particularly Section 5. Think about if 90% of the users would benefit from your + feature request, and whether your feature would be better off in a plugin. +--> + +## Guide-level explanation + +<!-- + Explain the proposal as if it was already included in the project and you + were teaching it to another programmer. That generally means: + + - Introducing new named concepts. + - Explaining the feature largely in terms of examples. + - If applicable, provide sample error messages, deprecation warnings, or + migration guidance. + + If this is a small feature, you may omit this section. +--> + +## Reference-level explanation + +<!-- + This is the technical portion of the feature request. Explain the design in + sufficient detail that: + + - Its interaction with other features is clear. + - It is reasonably clear how the feature would be implemented. + - Corner cases are dissected by example. + + If you do not know how to answer this, you can omit it. No worries! +--> + +## Drawbacks + +<!-- + Why should we *not* do this? +--> + +## Unresolved Questions + +<!-- + What related issues do you consider out of scope for this feature that could be + addressed in the future independently of the solution that comes out of this + feature? +--> diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..19ce7c6 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,46 @@ +<!-- + Thanks for creating a Pull Request! Before you submit, please make sure + you've done the following: + + - I read the contributing document at https://jekyllrb.com/docs/contributing/ +--> + +<!-- + Make our lives easier! Choose one of the following by uncommenting it: +--> + +<!-- This is a 🐛 bug fix. --> +<!-- This is a 🙋 feature or enhancement. --> +<!-- This is a 🔦 documentation change. --> +<!-- This is a 🔨 code refactoring. --> + +<!-- + Before you submit this pull request, make sure to have a look at the following + checklist. If you don't know how to do some of these, that's fine! Submit + your pull request and we will help you out on the way. + + - I've added tests (if it's a bug, feature or enhancement) + - I've adjusted the documentation (if it's a feature or enhancement) + - The test suite passes locally (run `script/cibuild` to verify this) +--> + +## Summary + +<!-- + Provide a description of what your pull request changes. +--> + +## Context + +<!-- + Is this related to any GitHub issue(s)? + + You can use keywords to automatically close the related issue. + For example, (all of) the following will close issue #4567 when your PR is merged. + + Closes #4567 + Fixes #4567 + Resolves #4567 + + Use any one of the above as applicable. +--> diff --git a/.github/SECURITY.markdown b/.github/SECURITY.markdown new file mode 100644 index 0000000..c704a28 --- /dev/null +++ b/.github/SECURITY.markdown @@ -0,0 +1,32 @@ +# Security Policy + +## Supported Versions + +Security updates are applied to the latest MINOR version of Jekyll, and the version used by GitHub Pages, v3.9.x. + +| Version | Supported | +| ------- | ------------------ | +| 4.2.x | :white_check_mark: | +| 3.9.x | :white_check_mark: | +| < 3.9.x | :x: | + +## Reporting a Vulnerability + +Please report vulnerabilities by sending an email to security@jekyllrb.com with the following information: + +1. A description of the vulnerability +2. Reproduction steps and/or a sample site (share a private repo to the [Jekyll Security Team](docs/pages/team.md)) +3. Your contact information + +The Jekyll security team will respond to your submission and notify you whether it has been confirmed by the team. +Your confidentiality is kindly requested as we work on a fix. We will provide our patch to you to test and verify that the vulnerability has +been closed. + +If you have created a patch and would like to submit that to us as well, we will happily consider it though we cannot guarantee that we will +use it. If we use your patch, we will attribute authorship to you either as the commit author, or as a co-author. + +Once a fix is verified, we will release PATCH versions of the supported MINOR versions and assign a CVE to the vulnerability. You will receive +credit in our release post. + +Once the patched version has been released, we will no longer request you to maintain confidentiality and you may choose to share details on +how you found the vulnerability with the community. diff --git a/.github/SUPPORT.markdown b/.github/SUPPORT.markdown new file mode 100644 index 0000000..0939723 --- /dev/null +++ b/.github/SUPPORT.markdown @@ -0,0 +1,20 @@ +# Jekyll Support + +## Getting Help + +**Jekyll's issue tracker is not a support forum.** + +If you're looking for support for Jekyll, there are a lot of options: + +* Read [Jekyll Documentation](https://jekyllrb.com/docs/home/) +* If you have a question about using Jekyll, start a discussion on [Jekyll Forum](https://talk.jekyllrb.com/) or [StackOverflow](https://stackoverflow.com/questions/tagged/jekyll) +* Chat with Jekyllers — Join [our Gitter channel](https://gitter.im/jekyll/jekyll) or [our IRC channel on Freenode](irc:irc.freenode.net/jekyll) + +There are a bunch of helpful community members on these services that should be willing to point you in the right direction. + +## Report a bug + +* If you think you've found a bug within a Jekyll plugin, open an issue in that plugin's repository — First [look for the plugin on rubygems](https://rubygems.org/) then click on the `Homepage` link to access the plugin repository. +* If you think you've found a bug within Jekyll itself, [open an issue](https://github.com/jekyll/jekyll/issues/new). + +Happy Jekyllin'! diff --git a/.github/actions/spelling/README.md b/.github/actions/spelling/README.md new file mode 100644 index 0000000..71d377c --- /dev/null +++ b/.github/actions/spelling/README.md @@ -0,0 +1,15 @@ +# check-spelling/check-spelling configuration + +File | Purpose | Format | Info +-|-|-|- +[dictionary.txt](dictionary.txt) | Replacement dictionary (creating this file will override the default dictionary) | one word per line | [dictionary](https://github.com/check-spelling/check-spelling/wiki/Configuration#dictionary) +[allow.txt](allow.txt) | Add words to the dictionary | one word per line (only letters and `'`s allowed) | [allow](https://github.com/check-spelling/check-spelling/wiki/Configuration#allow) +[reject.txt](reject.txt) | Remove words from the dictionary (after allow) | grep pattern matching whole dictionary words | [reject](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-reject) +[excludes.txt](excludes.txt) | Files to ignore entirely | perl regular expression | [excludes](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-excludes) +[only.txt](only.txt) | Only check matching files (applied after excludes) | perl regular expression | [only](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-only) +[patterns.txt](patterns.txt) | Patterns to ignore from checked lines | perl regular expression (order matters, first match wins) | [patterns](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-patterns) +[expect.txt](expect.txt) | Expected words that aren't in the dictionary | one word per line (sorted, alphabetically) | [expect](https://github.com/check-spelling/check-spelling/wiki/Configuration#expect) +[advice.txt](advice.txt) | Supplement for GitHub comment when unrecognized words are found | GitHub Markdown | [advice](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-advice) + +Note: you can replace any of these files with a directory by the same name (minus the `.txt` extension) and +then include multiple files (with a `.txt` extension) inside that directory to merge multiple files together. diff --git a/.github/actions/spelling/advice.md b/.github/actions/spelling/advice.md new file mode 100644 index 0000000..5cde8ef --- /dev/null +++ b/.github/actions/spelling/advice.md @@ -0,0 +1,28 @@ +<!-- See https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-advice --> <!-- markdownlint-disable MD033 MD041 --> + +<details><summary>If you see a bunch of garbage</summary> + +If it relates to a ... +<details><summary>well-formed pattern</summary> + +See if there's a [pattern](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples:-patterns) that would match it. + +If not, try writing one and adding it to the `patterns.txt` file. + +Patterns are Perl 5 Regular Expressions - you can [test]( +https://www.regexplanet.com/advanced/perl/) yours before committing to verify it will match your lines. + +Note that patterns can't match multiline strings. +</details> +<details><summary>binary-ish string</summary> + +Please add a file path to the `excludes.txt` file instead of just accepting the garbage. + +File paths are Perl 5 Regular Expressions - you can [test]( +https://www.regexplanet.com/advanced/perl/) yours before committing to verify it will match your files. + +`^` refers to the file's path from the root of the repository, so `^README\.md$` would exclude [README.md]( +../tree/HEAD/README.md) (on whichever branch you're using). +</details> + +</details> diff --git a/.github/actions/spelling/allow.txt b/.github/actions/spelling/allow.txt new file mode 100644 index 0000000..cb2757c --- /dev/null +++ b/.github/actions/spelling/allow.txt @@ -0,0 +1,5 @@ +statictastic +Statictastic +Linting +hakiri +built-ins diff --git a/.github/actions/spelling/excludes.txt b/.github/actions/spelling/excludes.txt new file mode 100644 index 0000000..9b9ae8c --- /dev/null +++ b/.github/actions/spelling/excludes.txt @@ -0,0 +1,35 @@ +# See https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples:-excludes + +(?:^|/)(?i)COPYRIGHT +(?:^|/)(?i)LICEN[CS]E +(?:^|/)package(?:-lock|)\.json$ +(?:^|/)vendor/ + +/fonts/ +ignore$ + +\.avi$ +\.eot$ +\.ico$ +\.jpe?g$ +\.lock$ +\.map$ +\.min\. +\.mod$ +\.mp[34]$ +\.png$ +\.svg$ +\.ttf$ +\.wav$ +\.woff$ +\.woff2$ + +^docs/pages/redirects/github\.html$ +^lib/jekyll/mime\.types$ +^lib/theme_template/example/index\.html$ +^lib/theme_template/example/_post\.md$ +^test/fixtures/empty_permalink\.erb$ +^test/fixtures/webrick/bar/baz\.html$ +^test/fixtures/webrick/bar/foo\.xhtml$ +^test/source/_posts/2009-06-22-no-yaml\.markdown$ +^\.github/ diff --git a/.github/actions/spelling/expect.txt b/.github/actions/spelling/expect.txt new file mode 100644 index 0000000..5738b15 --- /dev/null +++ b/.github/actions/spelling/expect.txt @@ -0,0 +1,762 @@ +acl +activesupport +adaoraul +addons +aeiou +AFile +afterall +Alexey +alfredxing +algolia +allowfullscreen +Anatoliy +andreyvit +Ankit +Anning +apps +appveyor +arengu +args +ariejan +arounds +asciinema +asdf +ashmaroli +attr +Autobuild +autocompletion +autogenerated +Autolink +autoload +autoreconf +autosave +awood +aws +awscli +backend +backport +backtick +barcamp +baseurl +bashrc +baz +bbatsov +bdimcheff +bellvat +benbalter +Beney +binstubs +bip +bitbucket +blog +Blogger +blogging +bonafide +Bou +breadcrumbs +briandoll +bridgetown +bridgetownrb +brightbox +brighterplanet +buddyworks +Bugfix +Burela +byparker +cachegrind +calavera +callgraphs +cartera +cavalle +CDNs +cgi +changefreq +changelog +chango +charset +Chayoung +chcp +chdir +Cheatsheet +Checkoway +chmod +chown +Chrononaut +chruby +cibuild +cimg +circleci +CJK +classname +cloudcannon +Cloudinary +cloudsh +CLT +codebase +codeclimate +CODEOWNERS +coderay +codeslinger +coffeescript +colorator +commandline +commonmark +compat +compatibilize +concat +config +configyml +contentblocks +CORS +Cov +CRLFs +cron +crontab +cruft +css +csv +Currin +CVE +CWD +cygwin +daringfireball +Dassonville +datafiles +datetime +DCEU +Debian +debuggability +defunkt +delegators +dependabot +deployer +deps +dest +Devkit +devops +digitalocean +dirs +disqus +ditaa +dnf +doclist +doctype +doeorg +dommmel +dotfile +Dousse +downcase +downcased +duckduckgo +duritong +Dusseau +dysinger +ecf +editorconfig +eduardoboucas +Elasticsearch +elsif +Emacs +emails +emoji +endcapture +endcomment +endfor +endhighlight +endif +endraw +endrender +endtablerow +Enumerables +EOL +erb +errordocument +Espinaco +eugenebolshakov +evaled +exe +execjs +extensionpack +extname +exts +favicon +Fengyun +ffi +figcaption +filesystem +Finazzo +firstimage +FIXME +flakey +flickr +fnmatch +fontello +forloop +formcake +formcarry +formester +formingo +formkeep +formspark +formspree +formx +Forwardable +frameborder +freenode +frontmatter +fsnotify +ftp +fullstory +Gaudino +gcc +gcnovus +gemfile +gemset +gemspec +getform +getset +getsimpleform +gettalong +gfm +ghp +ghpages +giraffeacademy +github +githubcom +githubusercontent +gitignore +gitlab +gjtorikian +globbed +globbing +google +gotcha +Goulven +gridism +GSo +gsub +gsubbing +Hakiri +hardcode +hashbang +hashmap +helaili +henrik +heredoc +heroku +highlighter +hilighting +Hoizey +homepage +hostman +hostname +href +htaccess +htm +html +htmlproofer +http +httpd +httpdocs +hyperlinks +Iaa +ial +ico +icomoon +iconset +ified +iframe +img +Impl +Inlining +invokables +irc +ivey +ize +jalali +jameshamann +jamstackthemes +jan +javascript +Jax +jayferd +jcon +jdoe +jeffreytse +jeffrydegrande +Jekpack +jekyllbot +jekyllconf +Jekyllers +Jekyllin +Jekylling +jekyllized +jekylllayoutconcept +jekyllrb +jekyllthemes +jemoji +jmcglone +jneen +johnreilly +jpg +jqr +jruby +json +jsonify +juretta +jwarby +Kacper +Kasberg +kbd +Kentico +Kewin +keycdn +kickster +Kinnula +kiwifruit +Kolesky +konklone +kontent +Kotvinsky +kramdown +Kulig +Kwokfu +Lamprecht +laquo +lastmod +launchctl +launchy +laurilehmijoki +ldquo +learnxinyminutes +lexer +LGTM +libcurl +libffi +lifecycle +lightgray +limjh +linenos +linkify +linux +liufengyun +livereload +localheinz +localhost +localtime +Locher +loglevel +Losslessly +lovin +lsi +lsquo +lstrip +lyche +macos +macromates +mademistakes +mailto +Manmeet +markdownify +Maroli +Marsceill +maruku +mathjax +mathml +mattr +Maximiliano +mchung +mdash +memberspace +Memoize +memoized +memoizing +mentoring +mergable +Mertcan +mertkahyaoglu +metadata +microdata +microsoft +mimetype +mingw +minibundle +minifier +minitest +Mittal +mixin +mkasberg +mkd +mkdir +mkdn +mkdown +mmistakes +modernizr +mojombo +moncefbelyamani +moz +mreid +msdn +mswin +MSYS +mtime +multiline +munging +Mvvm +myblog +mycontent +mydata +mydoc +myimage +mypage +myposts +myproject +myrepo +mysite +myvalue +myvar +myvariable +Nadjib +nakanishi +namespace +namespaced +navbar +nbsp +nearlyfreespeech +nethack +netlify +netlifycms +Neue +nginx +ngx +nielsenramon +nior +nodejs +noifniof +nokogiri +notextile +onclick +onebox +oneclick +onschedule +opensource +openssl +Optim +orderofinterpretation +orgs +OSVDB +osx +packagecontrol +pacman +paginator +pandoc +pantulis +params +parkr +parseable +paspagon +passthrough +pathawks +Pathutil +paywall +pdf +Pelykh +permalink +PHP +pinboard +Piwigo +pjhyett +pkill +pkpass +placeholders +planetjekyll +plantuml +plugin +png +podcasts +popen +Porcel +Posterous +postfiles +postlayout +postmodern +preinstalled +prepends +Prioritise +Probot +projectlist +pubstorm +pufuwozu +pwa +pwd +pygments +qrush +Quaid +quickstart +rackup +Rakefile +raquo +razorops +rbenv +rdiscount +rdoc +rdquo +readme +realz +rebund +redcarpet +redcloth +redgreen +redhat +refactor +refactoring +Refheap +regen +regex +regexp +remi +reqs +Responsify +revertable +rfc +rfelix +RHEL +ridk +roadmap +rowspan +rspec +rsquo +rss +rstrip +rsync +rtomayko +Rubo +rubocop +rubychan +rubygem +rubyinstaller +rubyprof +Ruparelia +Rusiczki +rvm +ryanflorence +saas +samplelist +samrayner +sandboxed +Sassc +sassify +schemastore +Schroers +Schwartzian +scp +screenshot +scrollbar +scroller +scss +scssify +sdk +SDKROOT +sectore +semver +seo +serverless +setenv +SFTP +shingo +shopify +shortlog +shortlinks +shoulda +sieversii +sigpipe +simplecov +Singhaniya +siteleaf +sitemap +SITENAME +Slicehost +slugified +slugify +smartforms +smartify +snipcart +socio +somedir +sonnym +Sonomy +sourced +sourcemaps +spam +spotify +src +ssg +ssh +SSL +stackoverflow +standalone +staticfiles +staticman +statictastic +STDERR +stdout +Stickyposts +strftime +stringified +Stringify +styleguide +stylesheet +subdir +subdomain +subfolder +subfolderitems +subnav +subpages +subpath +subpiece +subsubfolderitems +subthing +subvalues +subwidget +sudo +superdirectories +superdirs +SUSE +sverrirs +svg +svn +swfobject +swupd +symlink +symlinking +tablerow +tada +Taillandier +talkyard +tbody +technicalpickles +templating +templatize +Termux +textilize +textpattern +thead +therubyracer +Theunissen +Thornquest +thoughtbot +throughs +Tidelift +timeago +timezone +titleize +TLS +tmm +tmp +toc +tok +tomjoht +toml +tomo +toolset +toshimaru +triaged +triaging +truncatewords +tsv +ttf +Tudou +Tumblr +Tweetsert +txtpen +Tyborska +tzinfo +ubuntu +uby +ujh +ultron +undumpable +unencode +Unescape +unescaping +unicode +uniq +upcase +uppercasing +uri +url +urlset +username +usr +utf +utils +utime +utm +vanpelt +Vasovi +vendored +vercel +versioned +versioning +vertycal +Veyor +vilcans +Vishesh +visualstudio +vnd +vohedge +vps +vscode +vwochnik +Walkthroughs +wdm +We'd +webfont +webhook +webhosting +webmentions +webrick +website +weekdate +whitelist +whitelisting +wiki +wikipedia +wildcards +willcodeforfoo +woff +wordpress +Workaround +workflow +wsl +www +xcode +xcrun +xdg +Xhmikos +xhtml +Xiaoiver +XMinutes +xml +xmlns +xmlschema +yajl +yaml +Yarp +Yashu +Yastreb +yml +Youku +youtube +yunbox +zeropadding +Zlatan +zlib +zoneinfo +zpinter +Zsh +zshrc +zypper +zzot +frontend +prefetching diff --git a/.github/actions/spelling/only.txt b/.github/actions/spelling/only.txt new file mode 100644 index 0000000..af5f5b1 --- /dev/null +++ b/.github/actions/spelling/only.txt @@ -0,0 +1 @@ +^docs/.*\.md$ diff --git a/.github/actions/spelling/patterns.txt b/.github/actions/spelling/patterns.txt new file mode 100644 index 0000000..341825d --- /dev/null +++ b/.github/actions/spelling/patterns.txt @@ -0,0 +1,75 @@ +# See https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples:-patterns + +# data urls +(['"])data:.*?\g{-1} +data:[-a-zA-Z=;:/0-9+]*,\S* + +# YouTube +https?://(?:(?:www\.|)youtube\.com|youtu.be)/(?:channel/|embed/|playlist\?list=|watch\?v=|v/|)[-a-zA-Z0-9?&=_]* +<\s*youtube\s+id=['"][-a-zA-Z0-9?_]*['"] +\bimg\.youtube\.com/vi/[-a-zA-Z0-9?&=_]* +youtube_id:\s*[-a-zA-Z0-9?&=_]* + +# Google Analytics +\bgoogle-analytics\.com/collect.[-0-9a-zA-Z?%=&_.~]* + +# Google APIs +\bgoogleapis\.com/[a-z]+/v\d+/[a-z]+/[@./?=\w]+ +\b[-a-zA-Z0-9.]*\bstorage\d*\.googleapis\.com(?:/\S*|) + +# Google Calendar +\bcalendar\.google\.com/calendar(?:/u/\d+|)/embed\?src=[@./?=\w&%]+ +\w+\@group\.calendar\.google\.com\b + +# Google DataStudio +\bdatastudio\.google\.com/(?:(?:c/|)u/\d+/|)(?:embed/|)(?:open|reporting|datasources|s)/[-0-9a-zA-Z]+(?:/page/[-0-9a-zA-Z]+|) + +# The leading `/` here is as opposed to the `\b` above +# ... a short way to match `https://` or `http://` since most urls have one of those prefixes +# Google Docs +/docs\.google\.com/[a-z]+/d/(?:e/|)[0-9a-zA-Z_-]+/? + +# Google Groups +https://groups\.google\.com/d/topic/[^/]+/[a-zA-Z0-9]+/discussion +https://groups\.google\.com/d/msg/[^/]+/[a-zA-Z0-9]+/[a-zA-Z0-9]+ + +# Google themes +themes\.googleusercontent\.com/static/fonts/[^/]+/v\d+/[^.]+. + +# Google CDN +\bclients2\.google(?:usercontent|)\.com[-0-9a-zA-Z/.]* + +# Goo.gl +/goo\.gl/[a-zA-Z0-9]+ + +# Google Chrome Store +\bchrome\.google\.com/webstore/detail/\w*(?:/\w*|) + +# google_site_verification: +google_site_verification: [-a-zA-Z=;:/0-9+]* + +# Ruby-doc.org +https://ruby-doc\.org/.* + +# Contributors +alphabetical order.*:.* +twitter_handle: .* + +# apiKey +apiKey: '[a-f0-9]+' + +# FontAwesome +/(?:(?i)FontAwesome\.\w+\?\w+) + +# Lorem +(?:\w|\s|[,.])*\b(?i)(?:amet|consectetur|cursus|dolor|eros|ipsum|lacus|libero|ligula|lorem|magna|neque|nulla|suscipit|tempus|ultrices)\b(?:\w|\s|[,.])* + +# URL escaped characters +\%[0-9A-F]{2} +# c99 hex digits (not the full format, just one I've seen) + +# hex digits including css/html color classes: +(?:[\\0][xX]|\\u|[uU]\+|#x?|\%23)[0-9a-fA-FgGrR_]{2,}(?:[uU]?[lL]{0,2}|u\d+)\b + +# ignore long runs of a single character: +\b([A-Za-z])\g{-1}{3,}\b diff --git a/.github/actions/spelling/reject.txt b/.github/actions/spelling/reject.txt new file mode 100644 index 0000000..a5ba6f6 --- /dev/null +++ b/.github/actions/spelling/reject.txt @@ -0,0 +1,7 @@ +^attache$ +benefitting +occurence +Sorce +^[Ss]pae +^untill +^wether diff --git a/.github/config.yml b/.github/config.yml new file mode 100644 index 0000000..3015da1 --- /dev/null +++ b/.github/config.yml @@ -0,0 +1,15 @@ +updateDocsComment: > + Thanks for opening this pull request! The maintainers of this repository would appreciate it if you would update some of our documentation based on your changes. + +updateDocsWhiteList: + - bug + - fix + - Backport + - dev + - Update + - WIP + - chore + +updateDocsTargetFiles: + - README + - docs/ diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..5ace460 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,6 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" diff --git a/.github/first-timers-issue-template.md b/.github/first-timers-issue-template.md new file mode 100644 index 0000000..c778791 --- /dev/null +++ b/.github/first-timers-issue-template.md @@ -0,0 +1,44 @@ +### 🆕🐥☝ First Timers Only. + +This issue is reserved for people who never contributed to Open Source before. We know that the process of creating a pull request is the biggest barrier for new contributors. This issue is for you 💝 + +[About First Timers Only](https://www.firsttimersonly.com/). + +### 🤔 What you will need to know. + +Nothing. This issue is meant to welcome you to Open Source :) We are happy to walk you through the process. + +### 📋 Step by Step + +- [ ] 👌 **Join the team**: Add yourself to a Jekyll affinity team. + + Go to [teams.jekyllrb.com](https://teams.jekyllrb.com/) and join a team that best fits your interests. Once you click the link to join a team, you will soon receive an email inviting you to join the Jekyll organization. + +- [ ] 🙋 **Claim this issue**: Comment below. + + Leave a comment that you have claimed this issue. + +- [ ] 📝 **Update** the file [$FILENAME]($BRANCH_URL) in the `$REPO` repository (press the little pen Icon) and edit the line as shown below. + + +```diff +$DIFF +``` + + +- [ ] 💾 **Commit** your changes + +- [ ] 🔀 **Start a Pull Request**. There are two ways how you can start a pull request: + + 1. If you are familiar with the terminal or would like to learn it, [here is a great tutorial](https://egghead.io/series/how-to-contribute-to-an-open-source-project-on-github) on how to send a pull request using the terminal. + + 2. You can [edit files directly in your browser](https://help.github.com/articles/editing-files-in-your-repository/) + +- [ ] 🏁 **Done** Ask in comments for a review :) + +### 🤔❓ Questions + +Leave a comment below! + + +This issue was created by [First-Timers-Bot](https://github.com/hoodiehq/first-timers-bot). diff --git a/.github/first-timers.yml b/.github/first-timers.yml new file mode 100644 index 0000000..9f866d7 --- /dev/null +++ b/.github/first-timers.yml @@ -0,0 +1,6 @@ +repository: jekyll +labels: + - good first issue + - help-wanted + - first-time-only +template: .github/first-timers-issue-template.md diff --git a/.github/workflows/actions/memprof.rb b/.github/workflows/actions/memprof.rb new file mode 100644 index 0000000..f54186c --- /dev/null +++ b/.github/workflows/actions/memprof.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +require 'jekyll' +require 'memory_profiler' + +MemoryProfiler.report(allow_files: ['lib/jekyll/', 'lib/jekyll.rb']) do + Jekyll::PluginManager.require_from_bundler + Jekyll::Commands::Build.process({ + "source" => File.expand_path(ARGV[0]), + "destination" => File.expand_path("#{ARGV[0]}/_site"), + "disable_disk_cache" => true, + }) + puts '' +end.pretty_print(scale_bytes: true, normalize_paths: true) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml new file mode 100644 index 0000000..0758e39 --- /dev/null +++ b/.github/workflows/benchmark.yml @@ -0,0 +1,30 @@ +name: Micro Benchmark Runs + +on: + workflow_dispatch: + inputs: + path: + description: "Path to benchmark script relative to 'benchmark' directory." + required: true + default: "capture-assign.rb" + ruby_version: + description: "Ruby version to use (via `ruby/setup-ruby@v1`) action." + required: false + default: "2.7" + +jobs: + benchmark: + name: "Benchmark (${{ github.event.inputs.path }}) (Ruby ${{ github.event.inputs.ruby_version }})" + runs-on: "ubuntu-latest" + env: + BENCHMARK: true + steps: + - name: Checkout Jekyll + uses: actions/checkout@v3 + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ github.event.inputs.ruby_version }} + bundler-cache: true + - name: Run Benchmark + run: "bundle exec ruby benchmark/${{ github.event.inputs.path }}" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..ad8547e --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,68 @@ +name: Continuous Integration + +on: + push: + branches: + - master + - "*-stable" + pull_request: + branches: + - master + - "*-stable" + +jobs: + ci: + name: "Run Tests (${{ matrix.label }})" + runs-on: "ubuntu-latest" + strategy: + fail-fast: false + matrix: + include: + - label: Ruby 2.7 + ruby_version: "2.7" + - label: Ruby 3.0 + ruby_version: "3.0" + - label: Ruby 3.1 + ruby_version: "3.1" + - label: JRuby 9.3.4.0 + ruby_version: "jruby-9.3.4.0" + steps: + - name: Checkout Repository + uses: actions/checkout@v3 + - name: "Set up ${{ matrix.label }}" + uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby_version }} + bundler-cache: true + - name: Run Minitest based tests + run: bash script/test + - name: Run Cucumber based tests + run: bash script/cucumber + - name: Generate and Build a new site + run: bash script/default-site + + xtras: + name: "${{ matrix.job_name }} (Ruby ${{ matrix.ruby_version }})" + runs-on: "ubuntu-latest" + strategy: + fail-fast: false + matrix: + include: + - job_name: "Profile Docs Site" + step_name: "Build and Profile docs site" + script_file: "profile-docs" + ruby_version: "2.7" + - job_name: "Style Check" + step_name: "Run RuboCop" + script_file: "fmt" + ruby_version: "2.7" + steps: + - name: Checkout Repository + uses: actions/checkout@v3 + - name: "Set up Ruby ${{ matrix.ruby_version }}" + uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby_version }} + bundler-cache: true + - name: ${{ matrix.step_name }} + run: bash script/${{ matrix.script_file }} diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 0000000..a24fffd --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,52 @@ +name: Build and deploy Jekyll documentation site + +on: + push: + branches: + - master + +env: + RUBY_VERSION: 2.7 + +jobs: + deploy_docs: + if: "!contains(github.event.commits[0].message, '[ci skip]')" + runs-on: 'ubuntu-latest' + env: + BUNDLE_PATH: "vendor/bundle" + BUNDLE_JOBS: 4 + BUNDLE_RETRY: 3 + steps: + - uses: actions/checkout@v3 + - uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ env.RUBY_VERSION }} + bundler-cache: true + - name: Clone target branch + run: | + REMOTE_BRANCH="${REMOTE_BRANCH:-gh-pages}" + REMOTE_REPO="https://${GITHUB_ACTOR}:${{ secrets.GITHUB_TOKEN }}@github.com/${GITHUB_REPOSITORY}.git" + + echo "Publishing to ${GITHUB_REPOSITORY} on branch ${REMOTE_BRANCH}" + rm -rf docs/_site/ + git clone --depth=1 --branch="${REMOTE_BRANCH}" --single-branch --no-checkout \ + "${REMOTE_REPO}" docs/_site/ + - name: Build site + run: bundle exec jekyll build --source docs --destination docs/_site --verbose --trace + env: + # For jekyll-github-metadata + JEKYLL_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Deploy to GitHub Pages + run: | + SOURCE_COMMIT="$(git log -1 --pretty="%an: %B" "$GITHUB_SHA")" + pushd docs/_site &>/dev/null + : > .nojekyll + + git add --all + git -c user.name="${GITHUB_ACTOR}" -c user.email="${GITHUB_ACTOR}@users.noreply.github.com" \ + commit --quiet \ + --message "Deploy docs from ${GITHUB_SHA}" \ + --message "$SOURCE_COMMIT" + git push + + popd &>/dev/null diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..3585815 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,34 @@ +name: Release Gem + +on: + push: + branches: + - master + - "*-stable" + paths: + - "lib/**/version.rb" + +jobs: + release: + if: "github.repository_owner == 'jekyll'" + name: "Release Gem (Ruby ${{ matrix.ruby_version }})" + runs-on: "ubuntu-latest" + strategy: + fail-fast: true + matrix: + ruby_version: + - 2.7 + steps: + - name: Checkout Repository + uses: actions/checkout@v3 + - name: "Set up Ruby ${{ matrix.ruby_version }}" + uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby_version }} + bundler-cache: true + - name: Build and Publish Gem + uses: ashmaroli/release-gem@dist + with: + gemspec_name: jekyll + env: + GEM_HOST_API_KEY: ${{ secrets.RUBYGEMS_GEM_PUSH_API_KEY }} diff --git a/.github/workflows/spelling.yml b/.github/workflows/spelling.yml new file mode 100644 index 0000000..91849de --- /dev/null +++ b/.github/workflows/spelling.yml @@ -0,0 +1,39 @@ +name: Spell Check +on: + push: + branches: + - master + - "*-stable" + # Switch from `pull_request_target` event to reduce distraction from comments + # regarding errors reported in unmodified files. + pull_request: + branches: + - master + - "*-stable" + +jobs: + spelling: + name: Spell Check + permissions: + contents: read + pull-requests: read + actions: read + outputs: + followup: ${{ steps.spelling.outputs.followup }} + runs-on: ubuntu-latest + if: "contains(github.event_name, 'pull_request') || github.event_name == 'push'" + concurrency: + group: spelling-${{ github.event.pull_request.number || github.ref }} + # note: If you use only_check_changed_files, you do not want cancel-in-progress + cancel-in-progress: true + steps: + - name: check-spelling + id: spelling + uses: check-spelling/check-spelling@v0.0.20 + with: + # This workflow runs in response to both `push` and `pull_request`, if there's an open `pull_request` in the same repository for a given branch, there's no reason to spend resources checking both the `push` and the `pull_request`, so this flag tells the action while running for the `push` to find the `pull_request` and stop working early: + suppress_push_for_open_pull_request: 1 + # The action will manage checking out the repository itself instead of requiring the workflow to use `actions/checkout...`: + checkout: true + # If running without `: write`, posting a comment won't work, and for security `: write` permissions are left to a distinct (optional) job, here we skip trying to post a comment: + post_comment: 0 diff --git a/.github/workflows/third-party.yml b/.github/workflows/third-party.yml new file mode 100644 index 0000000..288f24d --- /dev/null +++ b/.github/workflows/third-party.yml @@ -0,0 +1,41 @@ +name: Third-Party Repository Profiling + +on: + push: + branches: + - master + pull_request: + branches: + - master +jobs: + build_n_profile: + if: "!contains(github.event.commits[0].message, '[ci skip]')" + runs-on: 'ubuntu-latest' + env: + BUNDLE_GEMFILE: "sandbox/Gemfile" + BUNDLE_PATH: "vendor/bundle" + BUNDLE_JOBS: 4 + BUNDLE_RETRY: 3 + steps: + - name: Checkout Jekyll + uses: actions/checkout@v3 + with: + fetch-depth: 5 + path: jekyll + - name: Checkout Third-Party Repository + uses: actions/checkout@v3 + with: + repository: ashmaroli/tomjoht.github.io + path: sandbox + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: 2.7 + bundler-cache: true + - name: Run Jekyll Build 3 times + run: | + bundle exec jekyll build -s sandbox -d sandbox/_site --trace + bundle exec jekyll build -s sandbox -d sandbox/_site --trace + bundle exec jekyll build -s sandbox -d sandbox/_site --trace + - name: Memory Analysis of Jekyll Build + run: bundle exec ruby jekyll/.github/workflows/actions/memprof.rb sandbox diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0ef83cf --- /dev/null +++ b/.gitignore @@ -0,0 +1,28 @@ +# Jekyll +_site/ +*-cache/ +.jekyll-metadata + +# Ruby +.bundle/ +.byebug_history +.ruby-gemset +.ruby-version +*.gem +Gemfile.lock + +# Files +.analysis +.DS_Store +*.swp +*~ + +# Folders +/vendor +bbin/ +bin/ +coverage +gh-pages/ +pkg/ +test/dest +tmp/* diff --git a/.jrubyrc b/.jrubyrc new file mode 100644 index 0000000..39aa437 --- /dev/null +++ b/.jrubyrc @@ -0,0 +1,3 @@ +backtrace.mask=true +backtrace.color=true +backtrace.style=mri diff --git a/.rubocop.yml b/.rubocop.yml new file mode 100644 index 0000000..8e31145 --- /dev/null +++ b/.rubocop.yml @@ -0,0 +1,474 @@ +--- +inherit_from: .rubocop_todo.yml + +require: + - rubocop-minitest + - rubocop-performance + - rubocop-rake + - rubocop-rspec + - ./rubocop/jekyll + +Jekyll/NoPutsAllowed: + Exclude: + - rake/*.rake + +AllCops: + TargetRubyVersion: 2.7 + Include: + - lib/**/*.rb + - test/**/*.rb + Exclude: + - bin/**/* + - exe/**/* + - benchmark/**/* + - script/**/* + - vendor/**/* + - tmp/**/* + +Gemspec/DeprecatedAttributeAssignment: + Enabled: true +Gemspec/RequireMFA: + Enabled: false + +Layout/BeginEndAlignment: + Enabled: true +Layout/EmptyComment: + Enabled: false +Layout/EmptyLinesAroundAttributeAccessor: + Enabled: true +Layout/EndAlignment: + Severity: error +Layout/FirstArrayElementIndentation: + EnforcedStyle: consistent +Layout/FirstHashElementIndentation: + EnforcedStyle: consistent +Layout/HashAlignment: + EnforcedHashRocketStyle: table +Layout/IndentationWidth: + Severity: error +Layout/LineContinuationLeadingSpace: + Enabled: true +Layout/LineContinuationSpacing: + Enabled: true +Layout/LineEndStringConcatenationIndentation: + Enabled: true +Layout/LineLength: + Exclude: + - !ruby/regexp /features\/.*.rb/ + - Rakefile + - rake/*.rake + - Gemfile + Max: 100 + Severity: warning +Layout/MultilineMethodCallIndentation: + EnforcedStyle: indented +Layout/MultilineOperationIndentation: + EnforcedStyle: indented +Layout/SpaceAroundMethodCallOperator: + Enabled: true +Layout/SpaceBeforeBrackets: + Enabled: true +Layout/SpaceInsideHashLiteralBraces: + Enabled: true + Exclude: + - test/**/*.rb + +Lint/AmbiguousAssignment: + Enabled: true +Lint/AmbiguousOperatorPrecedence: + Enabled: true +Lint/AmbiguousRange: + Enabled: true +Lint/BinaryOperatorWithIdenticalOperands: + Enabled: true +Lint/ConstantDefinitionInBlock: + Enabled: true + Exclude: + - test/**/*.rb +Lint/ConstantOverwrittenInRescue: + Enabled: true +Lint/DeprecatedConstants: + Enabled: true +Lint/DeprecatedOpenSSLConstant: + Enabled: true +Lint/DuplicateBranch: + Enabled: true +Lint/DuplicateElsifCondition: + Enabled: true +Lint/DuplicateRegexpCharacterClassElement: + Enabled: true +Lint/DuplicateRequire: + Enabled: true +Lint/DuplicateRescueException: + Enabled: true +Lint/EmptyBlock: + Enabled: true +Lint/EmptyClass: + Enabled: true +Lint/EmptyConditionalBody: + Enabled: true +Lint/EmptyFile: + Enabled: true +Lint/FloatComparison: + Enabled: true +Lint/HashCompareByIdentity: + Enabled: true +Lint/IdentityComparison: + Enabled: true +Lint/LambdaWithoutLiteralBlock: + Enabled: true +Lint/MissingSuper: + Enabled: false +Lint/MixedRegexpCaptureTypes: + Enabled: false +Lint/NestedPercentLiteral: + Exclude: + - test/test_site.rb +Lint/NoReturnInBeginEndBlocks: + Enabled: true +Lint/NumberedParameterAssignment: + Enabled: true +Lint/OrAssignmentToConstant: + Enabled: true +Lint/OutOfRangeRegexpRef: + Enabled: true +Lint/RaiseException: + Enabled: true +Lint/RedundantDirGlobSort: + Enabled: true +Lint/RedundantSafeNavigation: + Enabled: true +Lint/RequireRangeParentheses: + Enabled: true +Lint/RequireRelativeSelfPath: + Enabled: true +Lint/SelfAssignment: + Enabled: true +Lint/StructNewOverride: + Enabled: true +Lint/SymbolConversion: + Enabled: true +Lint/ToEnumArguments: + Enabled: false +Lint/TopLevelReturnWithArgument: + Enabled: true +Lint/TrailingCommaInAttributeDeclaration: + Enabled: true +Lint/TripleQuotes: + Enabled: true +Lint/UnexpectedBlockArity: + Enabled: true +Lint/UnmodifiedReduceAccumulator: + Enabled: true +Lint/UnreachableCode: + Severity: error +Lint/UnreachableLoop: + Enabled: true +Lint/UselessMethodDefinition: + Enabled: true +Lint/UselessTimes: + Enabled: true +Lint/Void: + Exclude: + - lib/jekyll/site.rb + +Metrics/AbcSize: + Max: 23 +Metrics/BlockLength: + Exclude: + - test/**/*.rb + - lib/jekyll/configuration.rb + - rake/*.rake +Metrics/ClassLength: + Exclude: + - !ruby/regexp /features\/.*.rb$/ + - !ruby/regexp /test\/.*.rb$/ + - lib/jekyll/document.rb + - lib/jekyll/site.rb + - lib/jekyll/commands/serve.rb + - lib/jekyll/configuration.rb + Max: 240 +Metrics/CyclomaticComplexity: + Exclude: + - lib/jekyll/utils.rb + - lib/jekyll/commands/serve.rb + Max: 11 +Metrics/MethodLength: + CountComments: false + Max: 20 + Severity: error +Metrics/ModuleLength: + Exclude: + - lib/jekyll/filters.rb + Max: 240 +Metrics/ParameterLists: + Max: 4 +Metrics/PerceivedComplexity: + Max: 13 + +Minitest/AssertEmptyLiteral: + Enabled: false +Minitest/AssertInDelta: + Enabled: true +Minitest/AssertionInLifecycleHook: + Enabled: true +Minitest/AssertKindOf: + Enabled: true +Minitest/AssertOutput: + Enabled: true +Minitest/AssertPathExists: + Enabled: true +Minitest/AssertSilent: + Enabled: true +Minitest/AssertWithExpectedArgument: + Enabled: true +Minitest/LiteralAsActualArgument: + Enabled: true +Minitest/TestMethodName: + Enabled: false +Minitest/MultipleAssertions: + Enabled: true +Minitest/RefuteInDelta: + Enabled: true +Minitest/RefuteKindOf: + Enabled: true +Minitest/RefutePathExists: + Enabled: true +Minitest/UnreachableAssertion: + Enabled: true +Minitest/UnspecifiedException: + Enabled: true + +Naming/FileName: + Enabled: false +Naming/HeredocDelimiterNaming: + Exclude: + - test/**/*.rb +Naming/MemoizedInstanceVariableName: + Exclude: + - lib/jekyll/convertible.rb + - lib/jekyll/drops/site_drop.rb + - lib/jekyll/drops/unified_payload_drop.rb + - lib/jekyll/page_without_a_file.rb + +Performance/AncestorsInclude: + Enabled: false +Performance/ArraySemiInfiniteRangeSlice: + Enabled: true +Performance/BigDecimalWithNumericArgument: + Enabled: true +Performance/BlockGivenWithExplicitBlock: + Enabled: true +Performance/ChainArrayAllocation: + Enabled: true +Performance/CollectionLiteralInLoop: + Enabled: true +Performance/ConstantRegexp: + Enabled: true +Performance/MapCompact: + Enabled: true +Performance/MethodObjectAsBlock: + Enabled: true +Performance/RedundantEqualityComparisonBlock: + Enabled: false +Performance/RedundantSortBlock: + Enabled: true +Performance/RedundantSplitRegexpArgument: + Enabled: true +Performance/RedundantStringChars: + Enabled: true +Performance/ReverseFirst: + Enabled: true +Performance/SortReverse: + Enabled: false +Performance/Squeeze: + Enabled: true +Performance/StringIdentifierArgument: + Enabled: true +Performance/StringInclude: + Enabled: true + Exclude: + - lib/jekyll/utils/platforms.rb +Performance/Sum: + Enabled: true + +Security/CompoundHash: + Enabled: true +Security/IoMethods: + Enabled: true +Security/MarshalLoad: + Exclude: + - !ruby/regexp /test\/.*.rb$/ + - lib/jekyll/regenerator.rb +Security/YAMLLoad: + Exclude: + - !ruby/regexp /features\/.*.rb/ + - !ruby/regexp /test\/.*.rb$/ + +Style/AccessModifierDeclarations: + Enabled: false +Style/AccessorGrouping: + Enabled: true +Style/Alias: + EnforcedStyle: prefer_alias_method +Style/AndOr: + Severity: error +Style/ArgumentsForwarding: + Enabled: false +Style/ArrayCoercion: + Enabled: true +Style/BisectedAttrAccessor: + Enabled: true +Style/CaseLikeIf: + Enabled: true +Style/StringChars: + Enabled: true +Style/ClassAndModuleChildren: + Exclude: + - test/**/*.rb +Style/ClassEqualityComparison: + Enabled: true +Style/CollectionCompact: + Enabled: true +Style/CombinableLoops: + Enabled: true +Style/DocumentDynamicEvalDefinition: + Enabled: true +Style/Documentation: + Enabled: false +Style/DoubleNegation: + Enabled: false +Style/EmptyHeredoc: + Enabled: true +Style/EndlessMethod: + Enabled: true +Style/ExplicitBlockArgument: + Enabled: false +Style/ExponentialNotation: + Enabled: true +Style/EnvHome: + Enabled: true +Style/FetchEnvVar: + Enabled: false +Style/FileRead: + Enabled: false +Style/FormatStringToken: + Exclude: + - lib/jekyll/utils/ansi.rb + - lib/jekyll/liquid_renderer/table.rb + - lib/jekyll/profiler.rb +Style/FrozenStringLiteralComment: + EnforcedStyle: always +Style/FileWrite: + Enabled: true +Style/GlobalStdStream: + Enabled: true +Style/GuardClause: + Enabled: false +Style/HashAsLastArrayItem: + Enabled: true +Style/HashConversion: + Enabled: true +Style/HashEachMethods: + Enabled: true +Style/HashExcept: + Enabled: true +Style/HashLikeCase: + Enabled: true +Style/HashSyntax: + EnforcedStyle: hash_rockets + Severity: error +Style/HashTransformKeys: + Enabled: false +Style/HashTransformValues: + Enabled: true +Style/IfWithBooleanLiteralBranches: + Enabled: true +Style/KeywordParametersOrder: + Enabled: true +Style/MagicCommentFormat: + Enabled: true +Style/MapCompactWithConditionalBlock: + Enabled: true +Style/MapToHash: + Enabled: true +Style/MixinUsage: + Exclude: + - test/helper.rb +Style/ModuleFunction: + Enabled: false +Style/MultilineTernaryOperator: + Severity: error +Style/NegatedIfElseCondition: + Enabled: true +Style/NestedFileDirname: + Enabled: true +Style/NilLambda: + Enabled: true +Style/OptionalBooleanParameter: + Enabled: true + Exclude: + - lib/jekyll/log_adapter.rb +Style/PercentLiteralDelimiters: + PreferredDelimiters: + "%Q": "{}" + "%W": () + "%q": "{}" + "%r": "!!" + "%s": () + "%w": () + "%x": () +Style/QuotedSymbols: + Enabled: true +Style/RedundantArgument: + Enabled: true +Style/RedundantAssignment: + Enabled: true +Style/RedundantFetchBlock: + Enabled: false +Style/RedundantFileExtensionInRequire: + Enabled: true +Style/RedundantInitialize: + Enabled: true + Exclude: + - lib/jekyll/plugin.rb +Style/RedundantRegexpCharacterClass: + Enabled: true +Style/RedundantRegexpEscape: + Enabled: true +Style/RedundantSelfAssignment: + Enabled: true +Style/RedundantSelfAssignmentBranch: + Enabled: true +Style/RegexpLiteral: + EnforcedStyle: percent_r +Style/RescueModifier: + Enabled: false +Style/SafeNavigation: + Exclude: + - lib/jekyll/document.rb +Style/SignalException: + EnforcedStyle: only_raise +Style/SingleArgumentDig: + Enabled: true +Style/SlicingWithRange: + Enabled: false +Style/SoleNestedConditional: + Enabled: true +Style/StringConcatenation: + Enabled: true + Exclude: + - lib/jekyll/commands/*.rb + - test/**/*.rb +Style/StringLiterals: + EnforcedStyle: double_quotes +Style/StringLiteralsInInterpolation: + EnforcedStyle: double_quotes +Style/SwapValues: + Enabled: true +Style/SymbolArray: + EnforcedStyle: brackets +Style/TrailingCommaInArrayLiteral: + EnforcedStyleForMultiline: consistent_comma +Style/TrailingCommaInHashLiteral: + EnforcedStyleForMultiline: consistent_comma diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml new file mode 100644 index 0000000..f7477b8 --- /dev/null +++ b/.rubocop_todo.yml @@ -0,0 +1,25 @@ +# This configuration was generated by +# `rubocop --auto-gen-config --auto-gen-only-exclude` +# on 2022-04-06 10:48:47 UTC using RuboCop version 1.26.1. +# The point is for the user to remove these configuration records +# one by one as the offenses are removed from the code base. +# Note that changes in the inspected code, or installation of new +# versions of RuboCop, may require this file to be generated again. + +# Offense count: 1 +# This cop supports safe auto-correction (--auto-correct). +Performance/BindCall: + Exclude: + - 'test/helper.rb' + +# Offense count: 1 +Style/CombinableLoops: + Exclude: + - 'lib/jekyll/tags/post_url.rb' + +# Offense count: 1 +# Configuration parameters: AllowedMethods. +# AllowedMethods: respond_to_missing? +Style/OptionalBooleanParameter: + Exclude: + - 'lib/jekyll/log_adapter.rb' diff --git a/Earthfile b/Earthfile new file mode 100644 index 0000000..2abed46 --- /dev/null +++ b/Earthfile @@ -0,0 +1,49 @@ +FROM alpine + +# Run locally: `earthly +all` to run full CI process +all: + BUILD --build-arg RUBY=3.0 +test + BUILD --build-arg RUBY=2.7 +test + BUILD --build-arg RUBY=2.5 +test + BUILD --build-arg RUBY=jruby:9.2.14.0 +test + BUILD style-check + BUILD profile-docs + +# Run locally: `earthly +test` +# Run with specific version: `earthly --build-arg RUBY=2.5 +test` +test: + FROM +deps + RUN script/test + RUN script/cucumber + RUN script/default-site + +style-check: + FROM +deps + RUN script/fmt + +profile-docs: + FROM +deps + RUN bundle install --jobs 4 + RUN script/profile-docs + RUN script/memprof + +# Install dependencies and copy in source +# used in above steps +deps: + ARG RUBY=3.0 + IF case $RUBY in jruby*) ;; *) false; esac + FROM $RUBY + ENV JRUBY_OPTS="--dev -J-XX:+TieredCompilation -J-XX:TieredStopAtLevel=1 -J-XX:CompileThreshold=10 -J-XX:ReservedCodeCacheSize=128M" + ELSE + FROM ruby:$RUBY + END + WORKDIR /src + RUN apt-get update && apt-get install nodejs dnsutils git make coreutils g++ build-essential -y + RUN gem install bundler + RUN gem install sassc -v '2.4.0' --source 'https://rubygems.org/' + COPY Gemfile . + COPY jekyll.gemspec . + COPY lib/jekyll/version.rb lib/jekyll/version.rb + COPY test test + RUN bundle install --jobs 4 + COPY . . \ No newline at end of file diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..e6ad84f --- /dev/null +++ b/Gemfile @@ -0,0 +1,105 @@ +# frozen_string_literal: true + +source "https://rubygems.org" +gemspec :name => "jekyll" + +gem "rake", "~> 13.0" + +group :development do + gem "launchy", "~> 2.3" + gem "pry" + + gem "pry-byebug" unless RUBY_ENGINE == "jruby" +end + +# + +group :test do + gem "cucumber", RUBY_VERSION >= "2.5" ? "~> 5.1.2" : "~> 4.1" + gem "httpclient" + gem "jekyll_test_plugin" + gem "jekyll_test_plugin_malicious" + gem "memory_profiler" + gem "nokogiri", "~> 1.7" + gem "rspec" + gem "rspec-mocks" + gem "rubocop", "~> 1.37.0" + gem "rubocop-minitest" + gem "rubocop-performance" + gem "rubocop-rake" + gem "rubocop-rspec" + gem "test-dependency-theme", :path => File.expand_path("test/fixtures/test-dependency-theme", __dir__) + gem "test-theme", :path => File.expand_path("test/fixtures/test-theme", __dir__) + gem "test-theme-skinny", :path => File.expand_path("test/fixtures/test-theme-skinny", __dir__) + gem "test-theme-symlink", :path => File.expand_path("test/fixtures/test-theme-symlink", __dir__) + gem "test-theme-w-empty-data", :path => File.expand_path("test/fixtures/test-theme-w-empty-data", __dir__) + + if RUBY_ENGINE == "jruby" + gem "http_parser.rb", "~> 0.6.0" + gem "jruby-openssl" + end +end + +# + +group :test_legacy do + gem "test-unit" if RUBY_PLATFORM =~ %r!cygwin! + + gem "minitest" + gem "minitest-profile" + gem "minitest-reporters" + gem "shoulda" + gem "simplecov" +end + +# + +group :benchmark do + if ENV["BENCHMARK"] + gem "benchmark-ips" + gem "rbtrace" + gem "ruby-prof" + gem "stackprof" + end +end + +# + +group :jekyll_optional_dependencies do + gem "jekyll-coffeescript" + gem "jekyll-docs", :path => "../docs" if Dir.exist?("../docs") && ENV["JEKYLL_VERSION"] + gem "jekyll-feed", "~> 0.9" + gem "jekyll-gist" + gem "jekyll-paginate" + gem "jekyll-redirect-from" + gem "kramdown-syntax-coderay" + gem "matrix" + gem "mime-types", "~> 3.0" + gem "rdoc", "~> 6.0" + gem "tomlrb" + + platforms :ruby, :mswin, :mingw, :x64_mingw do + gem "classifier-reborn", "~> 2.2" + gem "liquid-c", "~> 4.0" + gem "yajl-ruby", "~> 1.4" + end + + # Windows and JRuby does not include zoneinfo files, so bundle the tzinfo-data gem + # and associated library + platforms :jruby, :mswin, :mingw, :x64_mingw do + gem "tzinfo", ENV["TZINFO_VERSION"] if ENV["TZINFO_VERSION"] + gem "tzinfo-data" + end +end + +# + +group :site do + gem "html-proofer", "~> 3.4" if ENV["PROOF"] + + gem "jekyll-avatar" + gem "jekyll-mentions" + gem "jekyll-seo-tag" + gem "jekyll-sitemap" + gem "jemoji" +end diff --git a/History.markdown b/History.markdown new file mode 100644 index 0000000..0f660a7 --- /dev/null +++ b/History.markdown @@ -0,0 +1,3956 @@ +## 4.3.1 / 2022-10-26 + +### Bug Fixes + + * Respect user-defined name attribute in documents (#9167) + * Revert "Incrementally rebuild when a data file is changed" (#9170) + +### Documentation + + * Release post for v4.3.1 (#9171) + +## 4.3.0 / 2022-10-20 + +### Minor Enhancements + + * Add webrick as a dependency (#8524) + * Regenerate supported mime types (#8542) + * Update include tag to be more permissive (#8618) + * Optimize `Jekyll::Utils.parse_date` (#8425) + * Update rubocop from 1.12 to 1.18 and min ruby from 2.4 to 2.5 (#8741) + * Always hide cache-dir contents from Git (#8798) + * Remove the warning about auto-regeneration on Windows (#8821) + * Propagate _data folder from theme (#8815) + * Support both tzinfo v1 and v2 along with non-half hour offsets. (#8880) + * Run vendor-mimes to update mime.types (#8940) + * Expose collection static files via `site.static_files` (#8961) + * Expose `basename` from `document.rb` as `name` to Liquid templates (#8761) + * Allow Configurable Converters on CSV (#8858) + * Introduce `theme` drop to expose theme-gem details (#9129) + * Relax version constraint to allow Rouge 4.x (#9134) + * Incrementally rebuild when a data file is changed (#8771) + * Support jekyll-sass-converter 3.x (#9132) + +### Bug Fixes + + * fix: pin rubocop to 1.12 due to error with ruby 2.4 (#8651) + * Load Jekyll plugins from BUNDLE_GEMFILE location (#8585) + * fix(security): CVE-2021-28834 (#8680) + * Inject livereload script using `location.protocol` instead of `http:` (#8718) + * Respect collections_dir config within include tag (#8756) + * Fix regression in Convertible module from v4.2.0 (#8786) + * Revert #7253: "Don't reset site.url to localhost:4000 by default" (#8620) + * Improve readability of CI logs (#8877) + * Fix deprecation message for missing doc method (#8960) + * Fix response header for content served via `jekyll serve` (#8965) + * Trigger livereload in sites without pages (#8337) + * Only enable BOM encoding option on UTF encodings (#8363) + * Ensure theme config is a `Jekyll::Configuration` object (#8988) + * Remove misleading totals row from `--profile` table (#9039) + * Unlock Psych dependency (#9135) + * Fix false positive conflicts for static files in a collection (#9141) + +### Development Fixes + + * style: enable new cops (#8538) + * Allow dependabot to keep github actions up-to-date (#8540) + * Update actions/cache requirement to v2.1.3 (#8543) + * Pin rubocop version (#8564) + * style: add rubocop 1.9 cops (#8567) + * Cross Version Testing Locally and Faster CI (#8610) + * Use official Ruby setup GH action (#8614) + * Spell check action for markdown documentation (#8675) + * Update expect to cover docs/_posts (#8677) + * Bump check-spelling/check-spelling from 0.0.18 to 0.0.19 (#8740) + * Enable Rubocop accessor grouping, fix existing offenses (#8293) + * Tags:Highlight: Decomposed HTMLLegacy formatter (#8623) + * Relax Rubocop Dependency (#8831) + * Add a workflow to build gems consistently (#8830) + * Fix random test failures in TestExcerpt #to_liquid (#8884) + * Lock gem `psych` to `v3.x` (#8918) + * Fix typo in Bug Report template (#8951) + * Check symlink outside site_source without Pathutil (#9015) + * Stop testing with Rubies older than 2.7 on non-Windows (#8955) + * Bump actions/checkout from 2 to 3 (#8986) + * Remove git.io shortlinks from repo (#9045) + * Bump rubocop to 1.32 (#9093) + * Bump RuboCop to `1.36.x` (#9125) + * Use check-spelling/check-spelling@v0.0.20 (#9111) + * Disable pending cops when running rubocop (#9136) + * Relax RDoc version dependency (#9142) + +### Documentation + + * typo - do instead of don't (#8518) + * Document support for TSV files consistently (#8488) + * Add a disclaimer to tutorials involving Ruby code (#8525) + * Improve documentation on developing generators (#8527) + * Fixes typo in layouts_dir documentation (#8532) + * Fix i.e. typos in collections.md (#8529) + * Remove GitHub Pages content which is in GitHub docs (#8533) + * Step By Step Instructions Review (#8399) + * Fix typo in migrating from 3.0 to 4.0 page (#8572) + * Fix for important missing step in macOS Installation Docs: Add the Homebrew gems directory to the PATH (#8496) + * Use latest Jekyll-action configuration (#8579) + * docs: troubleshoot macOS with ARM64 architecture (#8560) + * docs: add overview of .jekyll-cache dir (#8648) + * docs: clarify where .jekyll-metadata comes from (#8646) + * Razorops CI/CD added (#8656) + * Specify default port and host for serve commands in docs (#8624) + * Update third-party.md (#8652) + * Add documentation for Sass configuration options (#8587) + * Add formcarry to forms section (#8471) + * Add step to set SDKROOT (#8478) + * Improve the "Markdown Options" Docs (#8681) + * Add 'webrick' warning note to "Quickstart" Docs (#8727) + * Update windows.md (#8701) + * IRC networks - Libera, Freenode (#8706) + * Improve GitHub Flavored Markdown Docs (#8684) + * Fixing URL in MacOS install for rbenv-doctor (#8693) + * Fix adjective in `troubleshooting.md` document (#8777) + * Goodbye Frank. We'll miss you. 💔 (#8807) + * Update index.html: Grammar fix. (#8803) + * Prefer Libera. Remove Freenode. (#8811) + * Update feature_request.md (#8797) + * Remove AWS Amplify from the showcase (#8812) + * Move Frank to Emeritus Core Team Members (#8813) + * Release post for v4.2.1 (#8818) + * Update CircleCI example (#8829) + * Fix typo (#8835) + * Added docs for running locally (#8852) + * Linting README.markdown (#8900) + * Remove text on GITHUB_TOKEN which is now built-in (#8907) + * Add Security Policy document (#8823) + * Manage repository meta documents consistently (#8908) + * docs: add Layer0 deployment guide (#8915) + * docs: Update README generated by `jekyll new-theme` (#8919) + * Update resources.md (#8925) + * Rewrite documentation on installing plugins (#8921) + * Improve maintainers guide on releasing a new version (#8928) + * Fix link for "CloudSh" (#8934) + * Recommend using `actions/cache` in GitHub Actions documentation (#8948) + * Remove references to EOL hakiri.io service (#8946) + * Release post for v4.2.2 (#8982) + * Document releasing off `*-stable` branches (#8984) + * Update document by fix yaml syntax error (#8991) + * Enhance option's case for Jekyll configuration (#8992) + * Fix typo in `_docs/deployment/manual.md` (#8997) + * Add quiet/verbose options (#8996) + * Update README.markdown re IRC Pointer (#9005) + * Remove Aerobatic (#9007) + * Add Jekyll 3.9.2 release post to 'master' branch (#9013) + * Simplify macOS installation docs (#8993) + * Improve document about Github Actions section (#8853) + * Update permalinks.md (#9017) + * Add clarity to docs on permalinks placeholders and built-ins (#8995) + * Remove Ionic Framework site from showcase (#9057) + * Windows: describe which option to choose (#9049) + * Improve links (http -> https) (#9064) + * Update ruby version for macos guide (#9086) + * Update posts.md (#9151) + * Release post for v4.3.0 (#9157) + +### Site Enhancements + + * Improvements to CSS (#7834) + * Slightly update lang `sh` code-block styling (#8857) + +## 4.2.2 / 2022-03-03 + +### Bug Fixes + + * Lock `http_parser.rb` gem to `v0.6.x` on JRuby. + +### Development Fixes + + * Backport #8830 for v4.2.x: Add a workflow to build gems consistently (#8869) + * Lock `rubocop-performance` to `v1.11.x`. + +## 4.2.1 / 2021-09-27 + +### Bug Fixes + + * Backport #8620 for v4.2.x: Revert #7253: "Don't reset site.url to localhost:4000 by default" (#8808) + * Backport #8756 for v4.2.x: Respect collections_dir config within include tag (#8794) + * Backport #8786 for v4.2.x: Fix regression in Convertible module from v4.2.0 (#8793) + +## 4.2.0 / 2020-12-14 + +### Minor Enhancements + + * Warn on command-line with permalink conflict (#8342) + * Suppress warning issued for redirect pages (#8347) + * Enhance detection of conflicting destination URLs (#8459) + * Add `:post_convert` hook to modify HTML content before layout (#8368) + * Allow triggering `:post_convert` events atomically (#8465) + * Debug reading Page and Layout objects (#8100) + * Do not reset `site.url` to `http://localhost:4000` by default (#7253) + * Add custom debug strings for Jekyll objects (#8473) + * Debug reading data files in a site (#8481) + +### Bug Fixes + + * Replace nested conditional with guard clauses (#8294) + * Fix: security bump (#8349) + * Fix path matching regex in post_url Liquid tag (#8375) + * Enable `Performance/ChainArrayAllocation` cop (#8404) + * Enable Lint/NoReturnInBeginEndBlocks Cop (#8457) + * Generate items from `site.include` list only once (#8463) + * Explicitly return nil after site process phase (#8472) + +### Optimization Fixes + + * Implement custom delegators for drop methods (#8183) + * Handle `nil` argument to `Jekyll.sanitized_path` (#8415) + * Cache `Jekyll.sanitized_path` (#8424) + * Memoize array of drop getter method names (#8421) + * Reduce string allocations from the `link` tag (#8387) + * Optimize parsing of parameters in `include` tag (#8192) + * Stash documents `write?` attribute in a variable (#8389) + * Reduce string allocations from generating doc URLs (#8392) + * Check if site is in incremental mode optimally (#8401) + * Utilize flexibility of `Site#in_dest_dir` (#8403) + * Reduce allocations from rendering item as liquid (#8406) + * Compute relative_path of pages using PathManager (#8408) + * Reduce allocation from `normalize_whitespace` filter (#8400) + * Use `Regexp#match?` when `MatchData` is not required (#8427) + * Check default front matter scope against symbols (#8393) + * Stash frequently used `Drop` setter keys for reuse (#8394) + * Memoize defaults computed for Convertibles (#8451) + * Reduce array allocations from merging categories (#8453) + * Memoize destination of pages, documents and staticfiles (#8458) + * Reduce allocations from computing item property (#8485) + * Optimize `Page#dir` with a private method (#8489) + * Stash attribute hash for Liquid computed for pages (#8497) + +### Development Fixes + + * Update cucumber gem to version 4.1 (#8278) + * Move permalink styles data to constant (#8282) + * Update rubocop gem to 0.87.1 (#8287) + * Update RuboCop to-do file (#8296) + * Fix `rake console` generating LoadError (#8312) + * Configure Performance cops (#8369) + * Update rubocop gem to 0.90.0 (#8313) + * Refactor `Jekyll::Utils::Platforms` (#7236) + * Bump RuboCop to v0.91.x (#8391) + * Add workflow to build and profile third-party repo (#8398) + * Bump RuboCop to v0.92.x + * Update cucumber gem version to 5.1.2 (#8413) + * Fix test suite compatibility with JRuby (#8418) + * chore(deps): bump Rubocop to 0.93.0 (#8430) + * Use Ruby 2.7.1 in GitHub Actions (#8444) + * Test that Liquid expressions are not deeply evaled (#8292) + * Test rendering arbitrary Liquid variables by default (#7414) + * Migrate TravisCI jobs to GitHub Actions (#8492) + +### Documentation + + * Update pointer to special permalink variables for collections (#8274) + * Fix special treatment for 'page 1' in docs of pagination (#8230) + * Add Formcake to forms section (#8283) + * Add a note on the rendering process in the docs (#8291) + * Add refactoring type to PULL_REQUEST_TEMPLATE (#8297) + * Update resources.md (#7864) + * Extra apostrophes in an URL (#8319) + * Clarify target of subordinate clause (#8320) + * Cherry-pick commits from conflicting branch `docs-40` + * Update documentation on third party site (#8352) + * Update default.md with info requested in #8314 (#8353) + * Clarify description of `safe` option (#8354) + * Simplifying the Git post-receive hook-example (#8358) + * Add missing doc for build and serve commands (#8365) + * Docs Review: Getting Started (#8372) + * Add note about rebooting system after installation (#8359) + * Use data file to render table at `/docs/configuration/options/#global-configuration` (#8377) + * Use data file(s) to render table(s) at `/docs/configuration/options/` (#8380) + * Improve maintainability of config option data (#8383) + * Remove CircleCI v1 docs (#8410) + * Remove `NOKOGIRI_USE_SYSTEM_LIBRARIES` from Travis CI docs (#8409) + * Add links to all Jekyll themes on GitHub tagged with #jekyll-theme (#8447) + * Document initializing project Gemfile from scratch (#8450) + * Document installation of additional dependencies for installing Jekyll on Fedora (#8456) + * Improve documentation on Hooks in Jekyll (#8467) + * Build docs site with GitHub Actions (#8201) + * Add link to Assets page from `_sass` section in `_docs/structure.md` (#8486) + +### Site Enhancements + + * Fix rendering of *showcase* images (#8504) + +## 4.1.1 / 2020-06-24 + +### Bug Fixes + + * Disable page excerpts by default (#8222) + * Revert introduction of PageDrop (#8221) + * Don't generate excerpts for non-html pages (#8234) + * Make page excerpts consistent with doc excerpts (#8236) + +### Documentation + + * Replace deprecated 'show' command with 'info' (#8235) + * Change name to ▲Vercel (#8247) + * Add language and examples to describe how to use the configuration op… (#8249) + * Fix missing yaml front matter colon and adjust/add clarifying language. (#8250) + * correct typo (#8261) + * Allow hyperlinks to specific filter documentation (#8231) + * Update link to Netlify step-by-step guide (#8264) + * Fix grammar in documentation section (#8265) + +### Site Enhancements + + * Including correct Sketch website (#8241) + * Release post for v4.1.1 (#8243) + +### Development Fixes + + * Bump RuboCop to v0.85.x (#8223) + * Expect drive letter only on vanilla windows (#8227) + +## 4.1.0 / 2020-05-27 + +### Bug Fixes + + * Memoize `absolute_url` and `relative_url` filters (#7793) + * Fix documentation comment for `Jekyll::Converters::Identity` (#7883) + * Optimize `Jekyll::Filters#item_property` (#7696) + * Allow multiple binary operators in `where_exp` filter (#8047) + * Fix documents custom-ordering logic (#8028) + * Use `layout.path` when rendering the Liquid layout (#8069) + * Reduce array allocations from `StaticFile#path` (#8083) + * Simplify `Jekyll::Renderer#validate_layout` (#8064) + * Add static file's basename to its `url_placeholder` (#7908) + * Clear cached Liquid template scope before render (#7967) + * Cache `URLFilter` results of string inputs per site (#7990) + * Use `platforms` instead of `install_if` in Gemfile (#8140) + * Config include trailing slash (#8113) + * Improve path normalization in liquid_renderer (#8075) + * Switch slugify regex to support more Unicode character groups (#8167) + * Check if entry is a directory once per enumerator (#8177) + * Filter out exclusively excluded entries sooner (#7482) + * Return `relative_url` if site.url is an empty string (#7988) + * Configure kramdown toc_levels as array by default (#8015) + * Reduce `Pathname` objects from front matter defaults (#8067) + * Simplify `Jekyll::Hooks.trigger` logic (#8044) + * Quicker categories for documents without superdirs (#7987) + * Reduce `Jekyll::Renderer` instances during a build (#7570) + * Escape regex characters in paths to match (#8138) + * Provide invokables for common drop query keys (#8165) + * Optimize path sanitization of default front matter (#8154) + * Initialize static files' data hash only if needed (#8188) + * Initialize include-files as Jekyll objects (#8158) + +### Minor Enhancements + + * serve: add support for ECC certificates (#7768) + * Update `item_property` to recognize integers (#7878) + * Include `_config.yml` in a new theme's gemspec (#7865) + * Add an option to easily disable disk-cache (#7928) + * Optimize markdown parsing with Kramdown by reusing the options and parser objects (#8013) + * Add `PageDrop` to provide Liquid templates with data (#7992) + * Optimize `Kramdown::JekyllDocument#to_html` calls (#8041) + * Configure default language for syntax-highlighting (#8035) + * Remove dev dependencies from new theme-gem gemspec (#8042) + * Allow disabling import of theme configuration (#8131) + * Allow excerpts to be generated for `Page` objects (#7642) + * Profile various stages of a site's build process (#6760) + * Add find filters to optimize where-first chains (#8171) + * Make `number_of_words` filter respect CJK characters (#7813) + * Allow extensionless document in a strict site (#7950) + * Add `:slugified_categories` URL placeholder (#8094) + +### Documentation + + * Add dropped 'title: Staff' to the code (#7805) + * Clarify docs for static files in collection (#7812) + * Rephrase the CircleCI v2 section (#7815) + * Update old GitHub wiki URL with new one (#7823) + * Update JekyllConf page with 2019 talks (#7826) + * link for memberships (#7825) + * Doc: minor fix, should be greater or equal to min version (#7856) + * Update third-party.md - Fix broken link (#7857) + * clarify _config.yml/collections type (#7873) + * Replace backticks with HTML tags in data file (#7879) + * add new theme source (#7875) + * fixed grammatical error (it's --> its) (#7887) + * Docs: Clarify organizing pages into subfolders (#7896) + * Disambiguate the placeholder of permalink (#7906) + * docs: add short serve command for livereload (#7919) + * docs: add options for watch and force polling (#7918) + * add install instructions for ArchLinux and openSUSE (#7920) + * Improve index page of Jekyll documentation (#7926) + * Include path in `jekyll new` commands (Usage docs) (#7931) + * Change `affect` to `effect` in the collections docs (#7937) + * Changed deprecated command in themes documentation (#7941) + * Adds some documentation for the `:clean`, `:on_obsolete` hook (#7954) + * docs: fix broken link (#7955) + * Corrected typo (#7975) + * docs: remove watch option in config (#7940) + * Correct a sentence in the documentation (#7978) + * Fix YAML representation of `group_by` result (#7979) + * Move `--baseurl` to build command options (#7985) + * Correct documentation of filters (#7989) + * Document sorting two documents by their `date` (#7870) + * Fix English grammar error (#7994) + * Update 03-front-matter.md (#7996) + * Add Kentico Kontent CMS integration to resources (#8000) + * Update 07-assets.md (#7413) + * Fix file references in Step by Step Tutorial's Assets step (#8007) + * docs: improve highlighting of code blocks (#8017) + * remove leading slash from Sass file location (#8021) + * [Docs] Fix asset link ref in step-by-step tutorial (#8026) + * Corrected command to modify PATH (#8029) + * Corrected command to modify PATH (#8030) + * Docs: Render full contents of just the latest post (#8032) + * docs: improvements for note boxes (#8037) + * Non-deprecated `vendor/bundle` path configuration (#8048) + * Update 09-collections.md (#8060) + * Remove extra paragraph tags (#8063) + * Add default front matter for tutorials collection (#8081) + * Create CSV to table tutorial (#8090) + * Add version badge for Custom Sorting of Documents (#8098) + * Docs: Fix grammar in `_docs/front-matter.md` (#8097) + * Update variables.md (#8106) + * Add help about Gentoo/Linux (#8002) + * Update documentation on third party site (#8122) + * Added Clear Linux (#8132) + * Added note about OS specific installation instructions. (#8135) + * Fix broken URL in the Resources Page on the Documentation Site (#8136) + * Docs: Deploy Jekyll site with GitHub Actions (#8119) + * Clarify `bundle config` in Bundler tutorial (#8150) + * docs: update your-first-plugin.md (#8147) + * Fix typo in documentation on GitHub Actions (#8162) + * Ease discovery of CLI commands (in their entirety) (#8178) + * Remove `sudo` from Travis CI tutorial (#8187) + * Add GitLab Pages to 3rd party list (#8191) + * docs: add 21yunbox for deployment (#8193) + * Improve documentation on tags and categories (#8196) + +### Development Fixes + + * Ci/GitHub actions (#7822) + * Rubocop version upgrade (#7846) + * Split action steps to avoid using `&&` on Windows (#7885) + * Upgrade rake to use version 13 (#7910) + * Update dependency constraint to allow RuboCop v0.76 (#7893) + * Use bash executable consistently (#7909) + * Test with JRuby 9.2.9.0 (#7779) + * Bump RuboCop to v0.79.x (#7970) + * Remove post-install message from gemspec (#7974) + * Attain Ruby 3.0 compatibility (#7948) + * Test `where` filter handling numeric property values (#7821) + * chore(deps): rubocop 0.80.0 (#8012) + * Update unit tests for Kramdown-based converter (#8014) + * Add Visual Studio Code Development Container (#8016) + * chore: simplify require for `Jekyll::VERSION` (#8057) + * Remove version-constraint relaxation for i18n gem (#8055) + * Mirror `spec.homepage` as `metadata["homepage_uri"]` (#8056) + * Bump Ruby versions on Travis builds (#8088) + * chore(ci): cache dependencies (#8168) + +### Site Enhancements + + * Optimize rendering of the documentation site (#8020) + * Utilize `relative_url` filter in documentation site (#8089) + * Render tutorial metadata in documentation site (#8092) + * Improve syntax-highlighting in documentation site (#8079) + * Site: Filter through just the *docs* collection (#8170) + +## 4.0.1 / 2020-05-08 + +### Bug Fixes + + * Prevent console warning with Ruby 2.7 (#8124) + * Clear cached Liquid template scope before render (#8141) + * Add static file's basename to its url_placeholder (#8142) + * Update item_property to recognize integers (#8160) + +### Development Fixes + + * Fix Kramdown converter based tests for v4.0.x (#8143) + +## 3.9.2 / 2022-03-27 + +### Bug Fixes + + * Lock `http_parser.rb` gem to `v0.6.x` on JRuby (#8943) + * Backport #8756 for v3.9.x: Respect collections_dir config within include tag (#8795) + * Backport #8965 for v3.9.x: Fix response header for content served via `jekyll serve` (#8976) + +### Development Fixes + + * Update and fix CI for `3.9-stable` on Ruby 3.x (#8942) + * Fix CI for commits to `3.9-stable` branch (#8788) + +## 3.9.1 / 2021-04-08 + +### Bug Fixes + + * Backport #8618 for v3.9.x: Update include tag to be more permissive (#8629) + +## 3.9.0 / 2020-08-05 + +### Minor Enhancements + + * Allow use of kramdown v2 (#8322) + * Add default language for kramdown syntax highlighting (#8325) + +## 3.8.7 / 2020-05-08 + +### Bug Fixes + + * Prevent console warnings with Ruby 2.7 (#8125) + +## 4.0.0 / 2019-08-19 + +### Major Enhancements + + * Drop ruby 2.3 (#7454) + * Drop support for Ruby 2.1 and 2.2 (#6560) + * Drop support for older versions of Rouge (#6978) + * Drop support for pygments as syntax-highlighter (#7118) + * Drop support for Redcarpet (#6987) + * Drop support for rdiscount (#6988) + * Drop support for `jekyll-watch-1.4.0` and older (#7287) + * Incorporate `relative_url` filter in `link` tag (#6727) + * Upgrade kramdown dependency to v2.x (#7492) + * Upgrade jekyll-sass-converter to v2.x - Sassc + sourcemaps (#7778) + * Upgrade i18n to v1.x (#6931) + * Add `Jekyll::Cache` class to handle caching on disk (#7169) + * Cache converted markdown (#7159) + * Cache: Do not dump undumpable objects (#7190) + * Cache matched defaults sets for given parameters (#6888) + * Ignore cache directory (#7184) + * Add `Site#in_cache_dir` helper method (#7160) + * Remove 'cache_dir' during `jekyll clean` (#7158) + * Cache parsed Liquid templates in memory (#7136) + * Only read layouts from source_dir or theme_dir (#6788) + * Allow custom sorting of collection documents (#7427) + * Always exclude certain paths from being processed (#7188) + * Remove Jekyll::Utils#strip_heredoc in favor of a Ruby > 2.3 built in (#7584) + * Incorporate `relative_url` within `post_url` tag (#7589) + * Remove patch to modify config for kramdown (#7699) + +### Minor Enhancements + + * Enhance `--blank` scaffolding (#7310) + * Use `jekyll-compose` if installed (#6932) + * Disable Liquid via front matter (#6824) + * Configure cache_dir (#7232) + * ISO week date drops (#5981) + * Fix custom 404 page for GitHub pages (#7132) + * Load config file from within current theme-gem (#7304) + * Suggest re-running command with `--trace` on fail (#6551) + * Support for binary operators in where_exp filter (#6998) + * Automatically load `_config.toml` (#7299) + * Add vendor folder to a newly installed site's .gitignore (#6968) + * Output Jekyll Version while debugging (#7173) + * Memoize computing excerpt's relative_path (#6951) + * Skip processing posts that can not be read (#7302) + * Memoize the return value of Site#documents (#7273) + * Cache globbed paths in front matter defaults (#7345) + * Cache computed item property (#7301) + * Cleanup Markdown converter (#7519) + * Do not process Liquid in post excerpt when disabled in front matter (#7146) + * Liquefied link tag (#6269) + * Update item_property to return numbers as numbers instead of strings (#6608) + * Use `.markdown` extension for page templates (#7126) + * Add support for `*.xhtml` files (#6854) + * Allow i18n v0.9.5 and higher (#7044) + * Ignore permission error of /proc/version (#7267) + * Strip extra slashes via `Jekyll.sanitized_path` (#7182) + * Site template: remove default config for markdown (#7285) + * Add a custom inspect string for StaticFile objects (#7422) + * Remind user to include gem in the Gemfile on error (#7476) + * Search Front matter defaults for Page objects with relative_path (#7261) + * Lock use of `tzinfo` gem to v1.x (#7521, #7562) + * Utilize absolute paths of user-provided file paths (#7450) + * Detect `nil` and empty values in objects with `where` filter (#7580) + * Initialize mutations for Drops only if necessary (#7657) + * Reduce Array allocations via Jekyll::Cleaner (#7659) + * Encode and unencode urls only as required (#7654) + * Reduce string allocations with better alternatives (#7643) + * Reduce allocations from Jekyll::Document instances (#7625) + * Add `type` attribute to Document instances (#7406) + * Reduce allocations from where-filter (#7653) + * Memoize SiteDrop#documents to reduce allocations (#7697) + * Add PathManager class to cache interim paths (#7732) + * Remove warnings and fixes for deprecated config (#7440) + * Delegate --profile tabulation to `terminal-table` (#7627) + +### Bug Fixes + + * Security: fix `include` bypass of `EntryFilter#filter` symlink check (#7226) + * Theme gems: ensure directories aren't symlinks (#7419) + * Add call to unused method `validate_options` in `commands/serve.rb` (#7122) + * Check if scope applies to type before given path (#7263) + * Document two methods, simplify one of the methods (#7270) + * Check key in collections only if it isn't "posts" (#7277) + * Interpolate Jekyll::Page subclass on inspection (#7203) + * Measure the no. of times a template gets rendered (#7316) + * Reduce array traversal in Jekyll::Reader (#7157) + * Re-implement handling Liquid blocks in excerpts (#7250) + * Documents should be able to render their date (#7404) + * Fix Interpreter warning from Jekyll::Renderer (#7448) + * Loggers should accept both numbers and symbols (#6967) + * Replace regex arg to :gsub with a string arg (#7189) + * Dont write static files from unrendered collection (#7410) + * Excerpt handling of custom and intermediate tags (#7382) + * Change future post loglevel to warn to help user narrow down issues (#7527) + * Handle files with trailing dots in their basename (#7315) + * Fix unnecessary allocations via StaticFileReader (#7572) + * Don't check if site URL is absolute if it is nil (#7498) + * Avoid unnecessary duplication of pages array (#7272) + * Memoize Site#post_attr_hash (#7276) + * Memoize Document#excerpt_separator (#7569) + * Optimize Document::DATE_FILENAME_MATCHER to match valid filenames (#7292) + * Escape valid special chars in a site's path name (#7568) + * Replace `name` in Page#inspect with relative_path (#7434) + * Log a warning when the slug is empty (#7357) + * Push Markdown link refs to excerpt only as required (#7577) + * Fix broken include_relative usage in excerpt (#7633) + * Initialize and reset glob_cache only as necessary (#7658) + * Revert memoizing Site#docs_to_write and #documents (#7684) + * Backport #7684 for v3.8.x: Revert memoizing Site#docs_to_write and refactor #documents (#7689) + * Backport #7213 and #7633 for v3.8.x: Fix broken include_relative usage in excerpt (#7690) + * Don't read symlinks in site.include in safe mode (#7711) + * Replace `String#=~` with `String#match?` (#7723) + * Update log output for an invalid theme directory (#7679) + * Remove configuration of theme sass files from Core (#7290) + * Actually conditionally include liquid-c (#7792) + * Test number_like regex on stringified property (#7788) + +### Development Fixes + + * Upgrade liquid-c to v4.0 (#7375) + * Bump RuboCop to v0.71.0 (#7687) + * Target Ruby 2.4 syntax (#7583) + * Fix: RuboCop offenses (#7769) + * Use communicative method parameters (#7566) + * Scan `assert_equal` methods and rectify any offenses with a custom RuboCop cop (#7130) + * CI: Test with Ruby 2.6 (#7438) + * CI: Test with Ruby 2.6 on AppVeyor (#7518) + * CI: Update RuboCop config (#7050) + * CI: Add a script to profile docs (#7540) + * CI(Appveyor): shallow clone with 5 last commits (#7312) + * CI: Test with oldest and latest Ruby only (#7412) + * CI: Update excludes for CodeClimate Analyses (#7365) + * CI: Lock Travis to Bundler-1.16.2 (#7144) + * CI: Bump tested version of JRuby to 9.2.7.0 (#7612) + * CI: Do not install docs on updating gems on Travis (#7706) + * Update gemspec (#7425) + * deps: relax version constraint on classifier-reborn gem (#7471) + * deps: update yajl-ruby (#7278) + * deps: bump yajl-ruby to v1.4.0 (#6976) + * Create symlink only if target is accessible (#7429) + * Switch to `:install_if` for wdm gem (#7372) + * Add cucumber feature to test include_relative tag (#7213) + * Small benchmark refactoring (#7211) + * Fix incorrectly passed arguments to assert_equal (#7134) + * fix up refute_equal call (#7133) + * Fix RuboCop offences in test files (#7128) + * Use assert_include (#7093) + * Remember to release docs gem (#7066) + * Useless privates removed (#6768) + * Load Rouge for TestKramdown (#7007) + * Update instructions for releasing docs Gem (#6975) + * We are not using Ruby 2.2 anymore (#6977) + * Remove unnecessary Jekyll::Page constant (#6770) + * Remove unused error class (#6511) + * Add a Cucumber feature for post_url tag (#7586) + * Generate a "TOTAL" row for build-profile table (#7614) + * Refactor Jekyll::Cache (#7532) + * Store list of expected extnames in a constant (#7638) + * Profile allocations from a build session (#7646) + * Update small typo in contributing.md (#7671) + * Remove override to Jekyll::Document#respond_to? (#7695) + * Update TestTags in sync with Rouge v3.4 (#7709) + * Use regexp to filter special entries (#7702) + * Reduce Array objects generated from utility method (#7749) + * Update mime.types (#7756) + * Replace redundant Array#map with Array#each (#7761) + * Reduce allocations by using #each_with_object (#7758) + * Memoize fallback_data for Drop (#7728) + * Use String#end_with? to check if entry is a backup (#7701) + +### Documentation + + * Refactor docs (#7205) + * Add a link to Giraffe Academy's tutorial (#7325) + * Do not advise users to install Jekyll outside of Bundler (#6927) + * Remove documentation for using Redcarpet (#6990) + * Install Docs that Work on MacOS 10.14 (#7561) + * Add Installation Instructions for Ubuntu (#6925) + * Don't prompt for sudo when installing with Ubuntu WSL (#6781) + * Installation instructions for Fedora (#7198) + * Update Windows install docs (#6926) + * List all standard liquid filters (#7333) + * List all static files variables (#7002) + * Improve how to include Rouge stylesheets (#7752) + * Mention CommonMark plugins (#7418) + * Add TSV to list of supported _data files. (#7168) + * How to deploy using pre-push git hook (#7179) + * Hosting with AWS Amplify (#7510) + * CircleCI deployment through CircleCI v2 (#7024) + * GitHub Pages: use themes from other repos (#7112) + * Document page.dir and page.name (#7373) + * Document custom tag blocks (#7359) + * Document converter methods (#7289) + * Document `{{ page.collection }}` (#7430) + * Document Jekyll Filters with YAML data (#7335) + * Document where Jekyll looks for layouts in a site (#7564) + * plugin: liquid tag jekyll-flickr (#6946) + * plugin: jekyll-target-blank (#7046) + * plugin: json-get. (#7086) + * plugin: `jekyll-info` (#7091) + * plugin: jekyll-xml-source (#7114) + * plugin: jekyll-firstimage filter (#7127) + * plugin: CAT (#7011) + * Resources: Statictastic (#7593) + * Resources: Bonsai Search (#7543) + * Resources: Formspark (#7601) + * Resources: Jekpack(#7598) + * Resources: formX (#7536) + * Resources: 99inbound's Jekyll post (#7348) + * Resources: CloudSh (#7497) + * Community: DEV Community's Jekyll tag (#7139) + * Showcase: developer.spotify.com (#7217) + * Showcase: Isomer (#7300) + * Add version number for group_by_exp doc (#6956) + * Updated nginx configuration for custom-404-page documentation (#6994) + * Clarify definition of 'draft' (#7037) + * _drafts need to be contained within the custom collection directory (#6985) + * Updated to supported version (#7031) + * Add Hints for some Improved Travis Config in Doc (#7049) + * Update travis-ci.md to point out "this is an example Gemfile" (#7089) + * Instructions to view theme’s files under Linux (#7095) + * Use a real theme in the example (#7125) + * Update docs about post creation (#7138) + * Initialize upgrading doc for v4.0 (#7140) + * Add version badge for date filters with ordinal (#7162) + * Corrected sample usage of postfiles (#7181) + * Resolve "Unable to locate package ruby2.4" error (#7196) + * Correct stylesheet url in tutorial step 7 (#7210) + * Removes quotes from markdown for assets (#7223) + * Clarified front matter requirement (#7234) + * Explicit location of where to create blog.html (#7241) + * Reference the build command options that allows multiple config files (#7266) + * Add more issue template(s) and pull request template (#7269) + * Suggest sites use OpenSSL instead of GnuTLS for their site's CI (#7010) + * Fix broken Contributors link in README.markdown (#7200) + * Add title tag to item in RSS template (#7282) + * Add link tag to item in RSS template (#7291) + * Remove redundant instruction comment (#7342) + * Textile is only supported through a converter plugin (#7003) + * Add recursive navigation tutorial (#7720) + * Remove installation instructions with Homebrew (#7381) + * Fix dead link and misleading prose (#7383) + * Fix content management section (#7385) + * Apply ruby official guide documents (#7393) + * Fix group_by_exp filter example (#7394) + * Remove alt attribute from a tags (#7407) + * Fix BASH code-block in ubuntu.md (#7420) + * zlib is missing (#7428) + * Fixed unnecessary articles and pronouns (#7466) + * Store SSL key and cert in site source (#7473) + * Fix typo in tutorial for converting existing site (#7524) + * Check if var exists before include tag (#7530) + * Clarify docs on collections regarding the need for front matter (#7538) + * Fix incorrect Windows path in themes.md (#7525) + * Addresses bundle not found. (#7351) + * Update the contribution docs for draft pull requests (#7619) + * Data file section adds TSV (#7640) + * Indicate where the _sass folder is by default (#7644) + * Docs: add version tags to new placeholders (#5981) for permalinks (#7647) + * Solve "GitHub Page build failure" in 10-deployment.md (#7648) + * fix link to Site Source config (#7708) + * Introduce frontmatter in step 2 (#7704) + * Add @ashmaroli to Core Team listing (#7398) + * Link to Tidelift in site's footer (#7377) + * Link to OpenCollective backing (#7378 + * Link to sponsor listing in README (#7405) + * Adjust team page listings (#7395) + * Updates to CODE OF CONDUCT (v1.4.0) (#7105) + * More inclusive writing (#7283) + * Update Ruby version used in Travis-CI example (#7783) + * Documentation for binary operators in where_exp (#7786) + * Adding SmartForms as Forms service (#7794) + +### Site Enhancements + + * Better Performance (#7388) + * Add some minor improvements to image loading in Showcase page (#7214) + * Simplify assigning classname to docs' aside-links (#7609) + * Simplify couple of includes in the docs site (#7607) + * Avoid generating empty classnames (#7610) + * Minimize rendering count (#7343) + +### Release + + * Jekyll v4.0 release (#7782) + * Release post for v4.0.0 beta1 (#7716) + * Release post for v4.0.0.pre.alpha1 (#7574) + * Release post for v3.8.0 (#6849) + * Release post for v3.6.3, v3.7.4 and v3.8.4 (#7259) + * Post: v4.0 development (#6934) + +## 3.8.6 / 2019-07-02 + +### Bug Fixes + + * Update log output for an invalid theme directory (#7734) + * Memoize `SiteDrop#documents` to reduce allocations (#7722) + * Excerpt handling of custom and intermediate tags (#7467) + * Escape valid special chars in a site's path name (#7573) + * Revert memoizing `Site#docs_to_write` and refactor `#documents` (#7689) + * Fix broken `include_relative` usage in excerpt (#7690) + * Install platform-specific gems as required (3c06609406) + +### Security Fixes + + * Theme gems: ensure directories aren't symlinks (#7424) + +## 3.8.5 / 2018-11-04 + +### Bug Fixes + + * Re-implement handling Liquid blocks in excerpts (#7250) + +## 3.8.4 / 2018-09-18 + +### Bug Fixes + + * 3.8.x: security: fix `include` bypass of `EntryFilter#filter` symlink check (#7228) + +## 3.8.3 / 2018-06-05 + +### Bug Fixes + + * Fix --unpublished not affecting collection documents (#7027) + +## 3.8.2 / 2018-05-18 + +### Development Fixes + + * Update rubocop version (#7016) + +### Bug Fixes + + * Add whitespace control to LIQUID_TAG_REGEX (#7015) + +## 3.8.1 / 2018-05-01 + +### Bug Fixes + + * Fix rendering Liquid constructs in excerpts (#6945) + * Liquify documents unless published == false (#6959) + +## 3.8.0 / 2018-04-19 + +### Development Fixes + + * move duplicate code to a single private method (#6593) + * Test against Ruby 2.5 on AppVeyor (#6668) + * Replace simple regex with a native Ruby method (#6732) + * Codeclimate: exclude livereload.js (#6776) + * Add a cucumber feature to test link tag (#6777) + * Fix theme gem feature (#6784) + * Replace simple regex with equivalent Ruby methods (#6736) + * Rewrite `script/rubyprof` as a Ruby script (#6813) + * Add debug output to theme rendering (#5195) + * fix minitest deprecation warning in test (#6839) + * Memoize `Site#site_data` (#6809) + * Memoize document output extension (#6814) + * Access document permalink attribute efficiently (#6740) + * Minimize array allocations in the `where` filter (#6860) + * Bump JRuby (#6878) + * Assert existence of <collection>.files (#6907) + * Bump RuboCop to 0.54.x (#6915) + * Regenerate unconditionally unless its an incremental build (#6917) + * Centralize require statements (#6910) + * Bump to RuboCop 0.55 (#6929) + * Refactor private method `HighlightBlock#parse_options` (#6822) + +### Minor Enhancements + + * Two massive performance improvements for large sites (#6730) + * Cache the list of documents to be written (#6741) + * Allow Jekyll Doctor to detect stray posts dir (#6681) + * Excerpt relative-path should match its path (#6597) + * Remind user to resolve conflict in `jekyll new` with `--force` (#6801) + * Memoize helper methods in site-cleaner (#6808) + * Compute document's relative_path faster (#6767) + * Create a single instance of PostReader per site (#6759) + * Allow date filters to output ordinal days (#6773) + * Change regex to sanitize and normalize filenames passed to LiquidRenderer (#6610) + * Allow passing :strict_variables and :strict_filters options to Liquid's renderer (#6726) + * Debug writing files during the build process (#6696) + * Improve regex usage in `Tags::IncludeTag` (#6848) + * Improve comment included in the starter index.md (#6916) + * Store and retrieve converter instances for Jekyll::Filters via a hash (#6856) + * Implement a cache within the `where` filter (#6868) + * Store regexp in a constant (#6887) + * Optimize computing filename in LiquidRenderer (#6841) + +### Documentation + + * Adding the jekyll-algolia plugin to the list of plugins (#6737) + * Added Premonition plugin to list of plugins (#6750) + * Add document on releasing a new version (#6745) + * Mention Talkyard, a new commenting system for Jekyll and others. (#6752) + * Add 'jekyll-fontello' to plugins (#6757) + * Install dh-autoreconf on Windows (#6765) + * Fix common typos (#6764) + * Fix documentation for `{{ page.excerpt }}` (#6779) + * Update docs on permalink configuration (#6775) + * Propose fix some typos (#6785) + * Say hello to Jekyll's New Lead Developer (#6790) + * Add reference to Liquid to plugin docs (#6794) + * Draft a release post for v3.7.3 (#6803) + * add missing step for gem-based theme conversion (#6802) + * Update windows.md to explain an issue with jekyll new. (#6838) + * Add Bundler Installation Instructions (#6828) + * Docs: describe difference between tags and categories (#6882) + * Add `jekyll-random` plugin to docs (#6833) + * Fixed typo in description of categories and tags (#6896) + * Add missing ul-tag (#6897) + * doc: add liquid tag plugin jekyll-onebox for html previews (#6898) + * Add `jekyll-w2m` to plugins (#6855) + * Fix tutorials navigation HTML (#6919) + * add Arch Linux installation troubleshoot (#6782) + * Docs: Install Jekyll on macOS (#6881) + * Fix CodeClimate badges [ci skip] (#6930) + * Update index.md (#6933) + +### Site Enhancements + + * Remove links to Gists (#6751) + * Always load Google Fonts over HTTPS (#6792) + * always load analytics.js over HTTPS (#6807) + +### Bug Fixes + + * Append appropriate closing tag to Liquid block in an excerpt ### -minor (#6724) + * Bypass rendering via Liquid unless required (#6735) + * Delegated methods after `private` keyword are meant to be private (#6819) + * Improve handling non-default collection documents rendering and writing (#6795) + * Fix passing multiline params to include tag when using the variable syntax (#6858) + * `include_relative` tag should find related documents in collections gathered within custom `collections_dir` (#6818) + * Handle liquid tags in excerpts robustly (#6891) + * Allow front matter defaults to be applied properly to documents gathered under custom `collections_dir` (#6885) + +## 3.7.4 / 2018-09-07 + +### Bug Fixes + + * Security: fix `include` bypass of EntryFilter#filter symlink check (#7224) + +## 3.7.3 / 2018-02-25 + +### Bug Fixes + + * Do not hardcode locale unless certainly necessary (#6791) + +## 3.7.2 / 2018-01-25 + +### Development Fixes + + * CI: Test against Ruby 2.5.0 (#6664) + * Bump rdoc to 6.0 (#6600) + * Lint file and bump theme dependencies (#6698) + * Write a Rubocop Cop to ensure no `#p` or `#puts` calls get committed to master. (#6615) + * Remove redgreen gem (#6720) + +### Site Enhancements + + * Display latest version in header (#6676) + * Update version in `config.yml` via YAML load / dump (#6677) + +### Documentation + + * Fix: Add note about posts in context of collections_dir (#6680) + * Update deploy-script in documentation (#6666) + * Add note about naming of collections_dir (#6703) + * Update installation.md (#6694) + * Add `jekyll-html` to plugins. (#6654) + * Update plugins.md (#6716) + * Release v3.7.1 (#6695) + +### Bug Fixes + + * inform that symlinks are not allowed in safe mode (#6670) + * Glob scope path only if configured with a pattern (#6692) + * Add gem "wdm" to all newly generated Gemfiles (#6711) + * Fix timezone inconsistencies between different ruby version (#6697) + * Refactor collections_dir feature for consistency (#6685) + +### Minor Enhancements + + * Require external library only if necessary (#6596) + +## 3.7.0 / 2018-01-02 + +### Minor Enhancements + + * Add LiveReload functionality to Jekyll. (#5142) + * Add Utils::Internet.connected? to determine whether host machine has internet connection. (#5870) + * 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) + * Add an option to configure kramdown warning output (#6554) + * Add `json` extension to list of directory indices (#6550) + * Dependency: Bump jekyll-watch to 2.0 (#6589) + * Remove paginate check (#6606) + * update classifier-reborn to 2.2.0 (#6631) + * Switch to an actively-maintained TOML parser. (#6652) + * Do not coerce layout paths in theme-gem to the source directory (#6603) + +### Bug Fixes + + * 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) + * Register reload hooks in Server#process (#6605) + * Memoize path to metadata file (#6602) + * Use `require_relative` to load Jekyll classes (#6609) + +### 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) + * Add configuration for first-timers bot (#6431) + * 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) + * Clean up Rubocop config (#6495) + * 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) + * 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) + * 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) + * 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) + * Add PageWithoutAFile class from jekyll plugins (#6556) + * Cleanup LiveReloadReactor (#6607) + +### Documentation + + * 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) + * Filtering Posts with categories, tags, or other variables (#6399) + * Fixes formatting on pre-formatted text. (#6405) + * 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) + * CoC violation correspondents (#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) + * 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) + * Remove link to severely outdated asset plugin (#6613) + * Default time zone depends upon server (#6617) + * Add `disqus-for-jekyll` to plugins. (#6618) + * Update "Requirements" for Ruby version (#6623) + * Fix: Update link to i18n_filter plugin (#6638) + * Correct WordPress capitalization (#6645) + * Add Tweetsert, Stickyposts, Paginate::Content (#6651) + * Post: Jekyll 3.7.0 released (#6634) + +### Site Enhancements + + * 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) + * 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) + * Add jekyll-category-pages plugin (#6632) + * Improve docs styling for code to be run in shell (#6641) + * Fix permalink icon markup in news-item layout (#6639) + +## 3.6.3 / 2018-09-18 + +### Bug Fixes + + * 3.6.x: security: fix `include` bypass of `EntryFilter#filter` symlink check (#7229) + +## 3.6.2 / 2017-10-21 + +### Development Fixes + + * Update Rubocop to 0.51.0 (#6444) + * Add test for layout as string (#6445) + +### Bug Fixes + + * Problematic UTF+bom files (#6322) + * Always treat `data.layout` as a string (#6442) + +## 3.6.1 / 2017-10-20 + +### Documentation + + * Doc y_day in docs/permalinks (#6244) + * Update frontmatter.md (#6371) + * Elaborate on excluding items from processing (#6136) + * Style lists in tables (#6379) + * Remove duplicate "available" (#6380) + +### Development Fixes + + * Bump rubocop to use `v0.50.x` (#6368) + +## 3.6.0 / 2017-09-21 + +### Minor Enhancements + + * Ignore final newline in folded YAML string (#6054) + * Add URL checks to Doctor (#5760) + * Fix serving files that clash with directories (#6222) (#6231) + * Bump supported Ruby version to `>= 2.1.0` (#6220) + * set `LiquidError#template_name` for errors in included file (#6206) + * Access custom config array throughout session (#6200) + * Add support for Rouge 2, in addition to Rouge 1 (#5919) + * Allow `yield` to logger methods & bail early on no-op messages (#6315) + * Update mime-types. (#6336) + * Use a Schwartzian transform with custom sorting (#6342) + * Alias `Drop#invoke_drop` to `Drop#[]` (#6338) + +### Bug Fixes + + * `Deprecator`: fix typo for `--serve` command (#6229) + * `Reader#read_directories`: guard against an entry not being a directory (#6226) + * kramdown: symbolize keys in-place (#6247) + * Call `to_s` on site.url before attempting to concatenate strings (#6253) + * Enforce Style/FrozenStringLiteralComment (#6265) + * Update theme-template README to note 'assets' directory (#6257) + * Memoize the return value of `Document#url` (#6266) + * delegate `StaticFile#to_json` to `StaticFile#to_liquid` (#6273) + * Fix `Drop#key?` so it can handle a nil argument (#6281) + * Guard against type error in absolute url (#6280) + * Mutable drops should fallback to their own methods when a mutation isn't present (#6350) + * Skip adding binary files as posts (#6344) + * Don't break if bundler is not installed (#6377) + +### Documentation + + * Fix a typo in `custom-404-page.md` (#6218) + * Docs: fix links to issues in History.markdown (#6255) + * Update deprecated gems key to plugins. (#6262) + * Fixes minor typo in post text (#6283) + * Execute build command using bundle. (#6274) + * name unification - buddy details (#6317) + * name unification - application index (#6318) + * trim and relocate plugin info across docs (#6311) + * update Jekyll's README (#6321) + * add SUPPORT file for GitHub (#6324) + * Rename CODE_OF_CONDUCT to show in banner (#6325) + * Docs : illustrate page.id for a collection's document (#6329) + * Docs: post's date can be overridden in front matter (#6334) + * Docs: `site.url` behavior on development and production environments (#6270) + * Fix typo in site.url section of variables.md :-[ (#6337) + * Docs: updates (#6343) + * Fix precedence docs (#6346) + * add note to contributing docs about `script/console` (#6349) + * Docs: Fix permalink example (#6375) + +### Site Enhancements + + * Adding DevKit helpers (#6225) + * Customizing url in collection elements clarified (#6264) + * Plugins is the new gems (#6326) + +### Development Fixes + + * Strip unnecessary leading whitespace in template (#6228) + * Users should be installing patch versions. (#6198) + * Fix tests (#6240) + * Define path with `__dir__` (#6087) + * exit site.process sooner (#6239) + * make flakey test more robust (#6277) + * Add a quick test for DataReader (#6284) + * script/backport-pr: commit message no longer includes the `#` (#6289) + * Add Add CODEOWNERS file to help automate reviews. (#6320) + * Fix builds on codeclimate (#6333) + * Bump rubies on Travis (#6366) + +## 3.5.2 / 2017-08-12 + +### Bug Fixes + + * Backport #6281 for v3.5.x: Fix `Drop#key?` so it can handle a nil argument (#6288) + * Backport #6280 for v3.5.x: Guard against type error in `absolute_url` (#6287) + * Backport #6266 for v3.5.x: Memoize the return value of `Document#url` (#6301) + * Backport #6273 for v3.5.x: delegate `StaticFile#to_json` to `StaticFile#to_liquid` (#6302) + * Backport #6226 for v3.5.x: `Reader#read_directories`: guard against an entry not being a directory (#6304) + * Backport #6247 for v3.5.x: kramdown: symbolize keys in-place (#6303) + +## 3.5.1 / 2017-07-17 + +### Minor Enhancements + + * Use Warn for deprecation messages (#6192) + * site template: Use plugins key instead of gems (#6045) + +### Bug Fixes + + * Backward compatibilize URLFilters module (#6163) + * Static files contain front matter default keys when `to_liquid`'d (#6162) + * Always normalize the result of the `relative_url` filter (#6185) + +### Documentation + + * Update reference to trouble with OS X/macOS (#6139) + * added BibSonomy plugin (#6143) + * add plugins for multiple page pagination (#6055) + * Update minimum Ruby version in installation.md (#6164) + * 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) + * Small correction in documentation for includes (#6193) + * Fix docs site page margin (#6214) + +### Development Fixes + + * Add jekyll doctor to GitHub Issue Template (#6169) + * Test with Ruby 2.4.1-1 on AppVeyor (#6176) + * set minimum requirement for jekyll-feed (#6184) + +## 3.5.0 / 2017-06-18 + +### Minor Enhancements + + * Upgrade to Liquid v4 (#4362) + * Convert StaticFile liquid representation to a Drop & add front matter defaults support to StaticFiles (#5871) + * Add support for Tab-Separated Values data files (`*.tsv`) (#5985) + * Specify version constraint in subcommand error message. (#5974) + * Add a template for custom 404 page (#5945) + * Require `runtime_dependencies` of a Gem-based theme from its `.gemspec` file (#5914) + * Don't raise an error if URL contains a colon (#5889) + * Date filters should never raise an exception (#5722) + * add `plugins` config key as replacement for `gems` (#5130) + * create configuration from options only once in the boot process (#5487) + * Add option to fail a build with front matter syntax errors (#5832) + * Disable default layouts for documents with a `layout: none` declaration (#5933) + * In `jekyll new`, make copied site template user-writable (#6072) + * Add top-level `layout` liquid variable to Documents (#6073) + * Address reading non-binary static files in themes (#5918) + * Allow filters to sort & select based on subvalues (#5622) + * Add strip_index filter (#6075) + +### Documentation + + * Install troubleshooting on Ubuntu (#5817) + * Add Termux section on troubleshooting (#5837) + * fix ial css classes in theme doc (#5876) + * Update installation.md (#5880) + * Update Aerobatic docs (#5883) + * Add note to collections doc on hard-coded collections. (#5882) + * Makes uri_escape template docs more specific. (#5887) + * Remove duplicate footnote_nr from default config (#5891) + * Fixed tutorial for publishing gem to include repo. (#5900) + * update broken links (#5905) + * Fix typo in contribution information (#5910) + * update plugin repo URL to reflect repo move (#5916) + * Update exclude array in configuration.md (#5947) + * Fixed path in "Improve this page" link in Tutorials section (#5951) + * Corrected permalink (#5949) + * Included more details about adding defaults to static files (#5971) + * Create buddyworks (#5962) + * added (buddyworks) to ci list (#5965) + * Add a tutorial on serving custom Error 404 page (#5946) + * add custom 404 to tutorial navigation (#5978) + * Add link to order of interpretation tutorial in Tutorials nav (#5952) + * Document Jekyll's Philosophy (#5792) + * Require Ruby > 2.1.0 (#5983) + * Fix broken link (#5994) + * Default options for script/proof (#5995) + * Mention Bash on Ubuntu on Windows (#5960) + * Document `--unpublished` flag introduced in 91e9ecf (#5959) + * Update upgrading.md to mention usage of `bundle update` (#5604) + * Fix missing quotation mark (#6002) + * New tutorial: Convert an HTML site to Jekyll (#5881) + * Revamp Permalink section (#5912) + * Fixup tutorial on creating theme from existing HTML templates (#6006) + * Standardise on "URLs" without apostrophe in docs (#6018) + * Added txtpen in tutorial (#6021) + * fix typo using past participle (#6026) + * changed formatting to fit the style of the documentation (#6027) + * doc fix typo word usage (#6028) + * corrected reference to layout in index.md (#6032) + * (Minor) Update MathJax CDN (#6013) + * Add MvvmCross to samples (#6035) + * Update travis-ci.md to correct procedure (#6043) + * fix sentence in documentation (#6048) + * rephrase a sentence in posts.md to be more direct (#6049) + * Compress Website Sass output (#6009) + * doc correct spelling error (#6050) + * adjusted date-format in sitemap (#6053) + * Typo fix (welcomed change -> welcome change). (#6070) + * Fixed documentation inconsistency (#6068) + * Add own plugin -> Jekyll Brand Social Wall (#6064) + * Added plugin jekyll-analytics (#6042) + * Use more precise language when explaining links (#6078) + * Update plugins.md (#6088) + * windows 10 tutorial (#6100) + * Explain how to override theme styles (#6107) + * updated Bash on Ubuntu on Windows link in tutorial (#6111) + * Fix wording in `_docs/templates.md` links section (#6114) + * Update windows.md (#6115) + * Added windows to docs.yml (#6109) + * Be more specific on what to upload (#6119) + * Remove Blank Newlines from "Jekyll on Windows" Page (#6126) + * Link the troubleshooting page in the quickstart page (#6134) + * add documentation about the "pinned" label (#6147) + * docs(JekyllOnWindows): Add a new Installation way (#6141) + * corrected windows.md (#6149) + * Refine documentation for Windows (#6153) + +### Development Fixes + + * Rubocop: add missing comma (#5835) + * Appease classifier-reborn (#5934) + * Allow releases & development on `*-stable` branches (#5926) + * Add script/backport-pr (#5925) + * Prefer .yaml over .toml (#5966) + * Fix Appveyor with DST-aware cucumber steps (#5961) + * Use Rubocop v0.47.1 till we're ready for v0.48 (#5989) + * Test against Ruby 2.4.0 (#5687) + * rubocop: lib/jekyll/renderer.rb complexity fixes (#5052) + * Use yajl-ruby 1.2.2 (now with 2.4 support) (#6007) + * Bump Rubocop to v0.48 (#5997) + * doc use example.com (#6031) + * fix typo (#6040) + * Fix CI (#6044) + * Remove `ruby RUBY_VERSION` from generated Gemfile (#5803) + * Test if hidden collections output a document with a future date (#6103) + * Add test for uri_escape on reserved characters (#6086) + * Allow you to specify the rouge version via an environment variable for testing (#6138) + * Bump Rubocop to 0.49.1 (#6093) + * Lock nokogiri to 1.7.x for Ruby 2.1 (#6140) + +### Site Enhancements + + * Corrected date for version 3.4.0 (#5842) + * Add the correct year to the 3.4.0 release date (#5858) + * Add documentation about order of interpretation (#5834) + * Documentation on how to build navigation (#5698) + * Navigation has been moved out from docs (#5927) + * Make links in sidebar for current page more prominent (#5820) + * Update normalize.css to v6.0.0 (#6008) + * Docs: rename `gems` to `plugins` (#6082) + * plugins -> gems (#6110) + * Document difference between cgi_escape and uri_escape #5970 (#6081) + +### 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) + * Allow abbreviated post dates (#5920) + * Remove dependency on include from default about.md (#5903) + * Allow colons in `uri_escape` filter (#5957) + * Re-surface missing public methods in `Jekyll::Document` (#5975) + * absolute_url should not mangle URL if called more than once (#5789) + * patch URLFilters to prevent `//` (#6058) + * add test to ensure variables work in `where_exp` condition (#5315) + * Read explicitly included dot-files in collections. (#6092) + * Default `baseurl` to `nil` instead of empty string (#6137) + * Filters#time helper: Duplicate time before calling #localtime. (#5996) + +## 3.4.5 / 2017-06-30 + + * Backport #6185 for v3.4.x: Always normalize the result of the `relative_url` filter (#6186) + +## 3.4.4 / 2017-06-17 + + * Backport #6137 for v3.4.x: Default `baseurl` to `nil` instead of empty string (#6146) + +## 3.4.3 / 2017-03-21 + + * Backport #5957 for v3.4.x: Allow colons in `uri_escape` filter (#5968) + +## 3.4.2 / 2017-03-09 + + * Backport #5871 for v3.4.x: Convert StaticFile liquid representation to a Drop & add front matter defaults support to StaticFiles (#5940) + +## 3.4.1 / 2017-03-02 + + * Backport #5920 for v3.4.x: Allow abbreviated post dates (#5924) + +## 3.4.0 / 2017-01-27 + +### Minor Enhancements + + * Add connector param to `array_to_sentence_string` filter (#5597) + * Adds `group_by_exp` filter (#5513) + * Use Addressable instead of URI to decode (#5726) + * throw IncludeTagError if error occurs in included file (#5767) + * Write Jekyll::Utils::Exec.run for running shell commands. (#5640) + * Use the current year for the LICENSE of theme (#5712) + * Update License (#5713) + +### Bug Fixes + + * Escaped regular expressions when using `post_url`. (#5605) + * fix date parsing in file names to be stricter (#5609) + * Add a module to re-define `ENV["TZ"]` in Windows (#5612) + * Use each instead of map to actually return nothing (#5668) + * include: fix 'no implicit conversion of nil to String' (#5750) + * Don't include the theme's includes_path if it is nil. (#5780) + * test double slash when input = '/' (#5542) + * use logger.info for related posts (#5822) + +### Site Enhancements + + * Use only the used Font Awesome icons. (#5530) + * Switch to `https` when possible. (#5611) + * Update `_font-awesome.scss` to move .woff file before .ttf (#5614) + * Update documentation on updating FontAwesome Iconset (#5655) + * Use defaults for docs and news-items (#5744) + * Sort gems in `docs/_config.yml` (#5746) + * Add missing class (#5791) + * Improve template docs (#5694) + +### Development Fixes + + * clean unit-test names in `test/test_tags.rb` (#5608) + * Add cucumber feature to test for bonafide theme gems (#5384) + * Use `assert_nil` instead of `assert_equal nil` (#5652) + * Rubocop -a on lib/jekyll (#5666) + * Bump to rake 12.0 (#5670) + * Rubocop Gemfile (#5671) + * update Classifier-Reborn to 2.1.0 (#5711) + * Rubocop: fix Rakefile and gemspec (#5745) + * Use `assert_nil` (#5725) + * Sort gems in `jekyll.gemspec` (#5746) + * Rubocop: Require consistent comma in multiline literals (#5761) + * Bump rubocop (#5765) + * New rubocop security checks (#5768) + * test/helper: fix flaky plugin path test by removing calls to Dir.chdir without a block (#5779) + * Use latest jemoji gem (#5782) + * Bump htmlproofer (#5781) + * Bump rubies we test against (#5784) + * Bump rdoc to v5.0 (#5797) + * Bump codeclimate-test-reporter to v1.0.5 (#5798) + +### Documentation + + * Improve quickstart docs (#5689) + * Add Jekyll-Post to list of plugins (#5705) + * Add jekyll-numbered-headings (#5688) + * Docs: move permalinks from documents into config (#5544) + * Improve collections docs (#5691) + * Fix #5730: add gcc and make to the list of requirements (#5731) + * Remove instructions to install Jekyll 2 on Windows (#5582) + * Fix example URL inconsistency (#5592) + * Replace backticks within HTML blocks with HTML tags (#5435) + * Add jekyll-migrate-permalink (#5600) + * Fix bad config YAML in collections example (#5587) + * Bring documentation on 'Directory Structure' up-to-date (#5573) + * Fixed typo (#5632) + * use backticks for Gemfile for consistency since in the next sentence … (#5641) + * Update Core team list in the README file (#5643) + * Improve Permalinks documentation. (#5653) + * Fix typo in Variables doc page (#5657) + * Fix a couple of typos in the docs (#5658) + * Update windows.md (#5683) + * Improve permalinks docs (#5693) + * Document --unpublished build option (#5720) + * Improve pages docs (#5692) + * Added new includes.md topic to docs (#5696) + * Replace a dead link with a web-archived one (#5738) + * Remove duplicate paragraph. (#5740) + * Addition of a sample "typical post" (#5473) + * Fix a minor grammatical mistake on themes' document ### -dev (#5748) + * Correct comments in data_reader.rb (#5621) + * Add jekyll-pre-commit to plugins list (#5752) + * Update quickstart.md (#5758) + * Correct minor typo (#5764) + * Fix a markdown link to look properly on the web (#5769) + * 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) + * Got that diaper money? (#5810) + * Sort content by popularity using Google Analytics (#5812) + * Rework CI doc to include multiple providers. (#5815) + * Improve theme docs (#5690) + * Add mention of classifier-reborn for LSI (#5811) + * Added note about --blank flag (#5802) + * Fixed inaccuracy in "Built-in permalink styles" docs (#5819) + +## 3.3.1 / 2016-11-14 + +### Minor Enhancements + + * Collapse `gsub` for performance (#5494) + * URL: warn if key doesn't exist in url drop (#5524) + +### Bug Fixes + + * Fix typo in `theme_template` README (#5472) + * Do not swallow all exceptions on render (#5495) + * Site template: fixed `_config.yml` comment typo (#5511) + * `jekyll new-theme` should specify Jekyll as a runtime dependency for the theme (#5457) + * Be much more specific about ignoring specific vendored directories. (#5564) + * Only warn about auto-regeneration bug on Bash On Windows. (#5464) + * Allow permalink template to have underscores (#5572) + +### Site Enhancements + + * Documentation: `link` Liquid tag (#5449) + * Updating install instruction link for Jekyll 3 on Windows (#5475) + * Update normalize.css to v5.0.0 (#5471) + * Add jekyll-data to the list of plugins (#5491) + * Add info about checking version + updating (#5497) + * Add jekyll-include-absolute-plugin to list of third-party plugins (#5492) + * Remove jekyll-hook from deployment methods (#5502) + * Update deployment-methods.md (#5504) + * Ubuntu users should install ruby2.3-dev (#5512) + * Remove Glynn as deployment option (#5519) + * Fix broken forum link (#5466) + * Move documentation to docs folder (#5459) + * Fix broken links in CONTRIBUTING (#5533) + * Update documentation on jekyllrb.com (#5540) + * Fix HTML rendering (#5536) + * Remove outdated deployment information (#5557) + * no more invalid US-ASCII on lines 30 and 97 (#5520) + * Add permalinks to docs in '/maintaining/' (#5532) + * Add jekyll-pinboard to list of third-party plugins (#5514) + * Fix formatting in 2-to-3.md (#5507) + * Add two plugins to the plugins page (#5493) + * Use site.baseurl before link and post_url tags (#5559) + * Fix link to jekyll-pinboard plugin (#5570) + * mention `docs` folder as a way to deploy on GitHub Pages (#5571) + +### Development Fixes + + * fix rubocop errors on testing with Rubocop 0.44 (#5489) + * script/test: add missing whitespace (#5479) + * Restrict Rubocop version (#5496) + * include a hashbang for all benchmark scripts & make them executable (#5505) + * Update source in script/proof (#5538) + * Collections.feature: conditional steps to have it pass on Windows (#5546) + * Fix tests to get script/test to pass on Windows (#5526) + +## 3.3.0 / 2016-10-06 + +### Minor Enhancements + + * Colorize interpolated output in logger.info (#5239) + * Site template: exclude Gemfile and Gemfile.lock in site config (#5293) + * Fix #5233: Increase our ability to detect Windows. (#5235) + * update gitignore template to ignore theme gems built by user (#5326) + * Adds ability to link to all files (#5199) + * Exclude vendor by default (#5361) + * Add ThemeAssetsReader which reads assets from a theme (#5364) + * Add bundle install to jekyll new command (#5237) + * Add absolute_url and relative_url filters. (#5399) + * Site template: remove `css/` from new site scaffolding (#5402) + * Site template: Move contents of the index.html page to the 'home' layout (#5420) + * Exclude node_modules by default (#5210) + * Run hooks in priority order. (#5157) + * Add `static_file.name` and `.basename` Liquid attributes (#5264) + * set site.url in dev environment to `http://localhost:4000` (#5431) + * Add support for indented link references on excerpt (#5212) + +### Bug Fixes + + * Use jekyll-feed to generate the default site's RSS feed (#5196) + * Site#configure_theme: do not set theme unless it's a string (#5189) + * Convertible: set self.output in #render_all_layouts and #do_layout (#5337) + * Only complain about `kramdown.coderay` if it is actually in the config (#5380) + * Clarify documentation in theme gem's README template (#5376) + * Allow underscore in highlighter language (#5375) + * Site template: set empty url in config file by default (#5338) + * Site template config: prepend 'jekyll serve' with 'bundle exec' (#5430) + * Don't call `File.utime` for StaticFiles if it's a symlink (#5427) + * Fix handling of non-ASCII characters in new `*_url` filters (#5410) + * Remove autoload of Draft which no longer exists. (#5441) + * Fix issue where Windows drive name is stripped from Jekyll.sanitized_path incorrectly (#5256) + * Fix bug where `post_url` tag matched incorrect post with subdirectory (#4873) + * Fix loading data from subdir with a period in name (#5433) + * Revert Commands::Serve#server_address signature change. (#5456) + +### Site Enhancements + + * Document `to_integer` and `inspect` filters (#5185) + * Fix path in the prompt (#5194) + * need subcommand build (#5190) + * Add the Jekyll Cloudinary plugin (#5183) + * Documentation : `new-theme` command (#5205) + * Document `link` Liquid tag (#5182) + * Remove mention of page for link tag in release post (#5214) + * fixed typo (#5226) + * Add missing comma (#5222) + * Maintain aspect ratio with `height: auto;` (#5254) + * Fix a link in deployment-methods.md (#5244) + * Documentation: improve highlight in `Creating a theme` (#5249) + * Bundler isn't installed by default (#5258) + * Update troubleshooting documentation to include fix for issue with vendored gems (#5271) + * Link `--lsi` option's description to Wikipedia docs on LSI (#5274) + * Document `--profile` option on the configuration page (#5279) + * Update homepage to sync with merge of #5258 (#5287) + * Add post about Jekyll Admin initial release (#5291) + * Replace liquid highlight tag with backticks (#5262) + * Word update (#5294) + * Site documentation section links always point to https://jekyllrb.com (#5281) + * Missing `:site, :post_render` payload documentation on site (#5280) + * Site: exclude README.md and .gitignore (#5304) + * Add link to Staticman (#5224) + * Update url for OpenShift (#5320) + * 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) + * .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) + * Add jekyll-menus to the list of plugins. (#5397) + * macOS and one grammar fix (#5403) + * Add documentation for `relative_url` and `absolute_url` (#5405) + * Bugfix on logo in JSON-LD (#5421) + * Fix Travis.ci documentation (#5413) + * 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) + +### Development Fixes + + * Update appveyor.yml and fix optional deps for Ruby x64 (#5180) + * Improve tests for Jekyll::PluginManager (#5167) + * Update Ruby versions in travis.yml (#5221) + * Avoid installing unnecessary gems for site testing (#5272) + * Proposal: Affinity teams and their captains (#5273) + * Replace duplicate with positive local test in issue template (#5286) + * Update AppVeyor config. (#5240) + * Execute jekyll from clone instead of defined binary when running 'script/default-site' (#5295) + * rubocop: lib/jekyll/document.rb complexity fixes (#5045) + * Proxy a number of Convertible methods to Renderer (#5308) + * Run executable for Cucumber via Ruby instead of Shell (#5383) + * Appease Rubocop (#5381) + * remove features' directories on windows with proper access (#5389) + * `site_configuration.feature`: use UTC format in timezone (#5416) + * swallow bundle output from `jekyll new` while in CI (#5408) + * Add .editorconfig (#5412) + +## 3.2.1 / 2016-08-02 + +### Bug Fixes + + * Include theme directories in default gemspec (#5152) + * Fix for symlinked themes (#5156) + * Layout: set `relative_path` without using Pathname (#5164) + +### Development Fixes + + * Add test to build the default site (#5154) + * script/default-site: specify `BUNDLE_GEMFILE` for new site (#5178) + * script/default-site: read Jekyll source from local clone (#5188) + +### Site Enhancements + + * Enable site excerpts (#5150) + * Initial 404 page (#5143) + * Remove the "this feature is unreleased" warning from the theme docs (#5158) + * Future True on GitHub Pages note (#5173) + * Minor updates and corrections (#5177) + * index.html: update instructions to require bundler (#5169) + * docs/quickstart: update instructions to require bundler (#5168) + +## 3.2.0 / 2016-07-26 + +### Minor Enhancements + + * Stop testing with Ruby 2.0.x EOL (#4381) + * Allow collections to have documents that have no file extension (#4545) + * Add size property to `group_by` result (#4557) + * Site Template: Removed unnecessary nesting from `_base.scss` (#4637) + * Adding a debug log statement for skipped future documents. (#4558) + * Site Template: Changed main `<div>` to `<main>` and added accessibility info (#4636) + * Add array support to `where` filter (#4555) + * 'jekyll clean': also remove .sass-cache (#4652) + * Clean up `Tags::PostUrl` a bit, including better errors and date parsing (#4670) + * Use `String#encode` for `xml_escape` filter instead of `CGI.escapeHTML` (#4694) + * Add `show_dir_listing` option for serve command and fix index file names (#4533) + * Site Template: write a Gemfile which is educational to the new site (#4542) + * Site template: add explanation of site variables in the example `_config.yml` (#4704) + * Adds `link` Liquid tag to make generation of URLs easier (#4624) + * Allow static files to be symlinked in unsafe mode or non-prod environments (#4640) + * Add `:after_init` hook & add `Site#config=` to make resetting config easy (#4703) + * DocumentDrop: add `#<=>` which sorts by date (falling back to path) (#4741) + * Add a `where_exp` filter for filtering by expression (#4478) + * Globalize Jekyll's Filters. (#4792) + * Gem-based themes (#4595) + * Allow symlinks if they point to stuff inside `site.source` (#4710) + * Update colorator dependency to v1.x (#4855) + * Move EntryFilter to use Pathutil & fix `glob_include?` (#4859) + * Add 'jekyll new-theme' command to help users get up and running creating a theme (#4848) + * `markdownify` and `smartify` should convert input to string before conversion (#4958) + * Run `Site#generate` for 'jekyll doctor' to catch plugin issues (#5005) + * Add `normalize_whitespace` filter (#4917) + * Move bin/jekyll to exe/jekyll to prevent collision with binstubs (#5014) + * Cleaning up site template & theme updates. (#4922) + * Add fetch method to Drops (#5056) + * Extract tag name to class method (#5063) + * check if relative URL contains a colon (#5069) + * Enable strict (or lax) liquid parsing via a config variable. (#5053) + * Add filter: `to_integer` (#5101) + * watcher: pass site instance to watch plugin (#5109) + * Show liquid warnings. (#5129) + * Add `--baseurl` to `build` subcommand (#5135) + +### Bug Fixes + + * Site Template: Added a default lang attribute (#4633) + * Site template: Escape title and description where it is used in HTML (#4606) + * `Document#date`: drafts which have no date should use source file mtime (#4611) + * `Filters#time`: clone an input Time so as to be non-destructive (#4590) + * Doctor: fix issue where `--config` wasn't a recognized flag (#4598) + * Ensures `related_posts` are only set for a post (#4620) + * `EntryFilter#special?`: ignore filenames which begin with `~` (#4491) + * Cleaner: `keep_files` should only apply to the beginning of paths, not substrings with index > 0 (#3849) + * Use SSLEnable instead of EnableSSL and make URL HTTPS. (#4693) + * convertible: use `Document::YAML_FRONT_MATTER_REGEXP` to parse transformable files (#4786) + * Example in the site template should be IANA-approved example.com (#4793) + * 3.2.x/master: Fix defaults for Documents (posts/collection docs) (#4808) + * Don't rescue LoadError or bundler load errors for Bundler. (#4857) + * `Serve.process` should receive same config as `Build.process` (#4953) + * Prevent reset of page in Liquid payload right before rendering layouts (#5009) + * Add missing fields to ExcerptDrop (#5067) + * Stringify configuration overrides before first use (#5060) + * hooks: move `after_init` hook call at the end of `Site#initialize` (#5106) + * filters: raise error if no input given to date filters (#5127) + * `where_exp` filter should filter posts (#4860) + +### Forward Ports + + * From v3.1.4: Add ExcerptDrop and remove excerpt's ability to refer to itself in Liquid (#4941) + * From v3.1.4: Configuration permalink fix and addition of Configuration.from and sorting `site.collections` by label (#4942) + * From v3.1.4: Fix `{{ layout }}` oddities (proper inheritance & fixing overflow of old data) (#4943) + * From v3.1.5: Sort the results of the `require_all` glob (#4944) + * From v3.1.6: Add ability to render drops as JSON (#4945) + +### Development Fixes + + * Add project maintainer profile links (#4591) + * Fix state leakage in Kramdown test (#4618) + * Unify method for copying special files from repo to site (#4601) + * Refresh the contributing file (#4596) + * change smartify doc from copy/paste of markdownify doc (#4653) + * Update Rake & disable warnings when running tests (#4720) + * Fix many warnings (#4537) + * Don't blindly assume the last system when determining "open" cmd (#4717) + * Fix "locally" typo in contributing documentation (#4756) + * Update Rubocop rules (#4886) + * Flesh out the issue template to be much more detailed (#4849) + * Fixing rubocop offenses in lib/jekyll/cleaner.rb (#4892) + * Update `jekyll/commands*` to pass rubocop rules (#4888) + * Clean up many test files to pass Rubocop rules (#4902) + * Rubocop cleanup for some utils and further test files (#4916) + * Rubocop: Low hanging fruit (#4936) + * Rubocop: `Drop` changes from v3.1 forward-ports (#4949) + * Rubocop: cleanup for misc files (#4946) + * Rubocop: Stevenson (#4951) + * Rubocop: lib/jekyll/entry_filter.rb (#4950) + * Rubocop: `test/*` (#4947) + * Rubocop: features (#4934) + * Rubocop: Liquid renderer (#4933) + * Rubocop: converters (#4931) + * Rubocop: Site Drop (#4948) + * Rubocop: tags (#4938) + * Rubocop: Readers (#4932) + * rubocop: jekyll/lib/frontmatter_defaults.rb (#4974) + * rubocop: features/step_definitions.rb (#4956) + * Rubocop theme and url jekyll libs (#4959) + * Rubocop jekyll.rb (#4966) + * Rubocop: use %r for all regular expressions. (#4979) + * Cleanup and make misc files compliant with Rubocop. (#4940) + * Rubocop: jekyll/lib/site.rb (#4973) + * Add timings for each scenario in cucumber & print worst offenders (#4908) + * rubocop: jekyll/lib/filters.rb (#4993) + * Fix rubocop offenses in exe/jekyll (#5017) + * Rubocop: lib/jekyll/command.rb (#5018) + * rubocop: lib/jekyll/static_file.rb (#5019) + * rubocop: lib/jekyll/utils.rb (#5026) + * rubocop: lib/jekyll/regenerator.rb (#5025) + * rubocop: lib/jekyll/configuration.rb (#5024) + * rubocop: lib/jekyll/renderer.rb style fixes (#5032) + * rubocop: lib/jekyll/convertible.rb style fixes (#5031) + * rubocop: lib/jekyll/document.rb style fixes (#5030) + * Remove ruby-head from Travis matrix & fix jruby failures (#5015) + * Remove useless statement from Configuration test (#5065) + * Change baseurl to example.com for some test cases (#5068) + * use activesupport < 5 if we are on a Ruby < 2.2.2 (#5100) + * Internal documentation for working on Jekyll (#5011) + * rubocop: lib/jekyll/collection.rb (#5022) + * tests: Typo fixes. (#5114) + * Normalize yml files. (#5116) + * Whitespace cleanup. (#5113) + * Add AppVeyor support. (#5115) + * appveyor.yml: drop Ruby 2.0.0. (#5119) + * Fix indentation in benchmark (#5124) + * Style fixes for Rubocop 0.42.0 - var == 0 becomes var.zero? - when defining method_missing, also define respond_to_missing? (#5137) + +### Site Enhancements + + * Add jekyll-seo-tag, jekyll-avatar, and jekyll-sitemap to the site (#4553) + * Add Google search query to /docs/help/ (#4589) + * Upgrading, documentation (#4597) + * Add 'view source' entry (#4602) + * Add jekyll-video-embed to list of third-party plugins. (#4621) + * Adding Aerobatic to list of deployment options (#4630) + * Update documentation: HTMLProofer CLI command (#4641) + * Document that subdirectories of `_posts` are no longer categories (#4639) + * Update continuous-integration docs with sudo: false information (#4628) + * Blog post on refreshed contributing file and new affinity teams (#4645) + * Fixes typo on collections (#4647) + * Documentation: future option also works for collections (#4682) + * Additional package needed for Fedora 23 Workspace (#4685) + * Fix typo on Chocolatey name in Windows documentation (#4686) + * Use the correct URL, Fixes #4698 (#4699) + * Add jekyll-paspagon plugin (#4700) + * Bold-italicize note in assets documentation about needing front matter (#4706) + * Highlight the `script/` calls in the Contributing documentation (#4712) + * Add Hawkins to the list of third-party plugins (#4755) + * Fix a typo in pagination doc (#4763) + * Switch second GitHub Pages link to HTTPS (#4760) + * Explain data file format requirements more clearly in documentation (#4781) + * Add jekyll-i18n_tags to list of third-party plugins (#4775) + * Remove Leonard Lamprecht's website from Sites page (#4771) + * Updates documentation for collections to include `date` property (#4769) + * Added an explicit rerun note to configuration.md, defaults section (#4734) + * Update Rack-Jekyll Heroku deployment blog post url (#4789) + * Added missing single quote on rsync client side command (#4813) + * Organize Form Platforms-as-a-Service into unified list & add FormSpree.io (#4754) + * Fixed typo on Configuration page (#4804) + * Update FormKeep URL on the Resources doc (#4844) + * site: use liquid & reduce some whitespace noise (#4854) + * Add jekyll-breadcrumbs to list of third-party plugins (#4874) + * Added Pug converter to list of third-party plugins (#4872) + * Add jekyll-ideal-image-slider to list of third-party plugins (#4863) + * Add Jekyll Tips and the Cheatsheet to the list of resources (#4887) + * Removed extra `</p>` from `site/_docs/permalinks.md` (#4890) + * Add pubstorm deployment instructions to docs (#4881) + * Corrected pagination docs for hidden: true feature (#4903) + * Remove a Broken Link for Refheap Plugin (#4971) + * Instructions on how to install github-gem on Windows (#4975) + * Minor tweak to fix missing apostrophe (#4962) + * Instructions on how to install github-gem on Windows (v2) (#4977) + * Fix inaccurate HTTP response header field name (#4976) + * Add post about GSoC project (#4980) + * Link to the HTML page instead of Markdown (#4985) + * Update normalize.css to v4.0.0. (#4989) + * Add jekyll-tags-list-plugin to list of third-party plugins (#5000) + * Windows docs: Command needs to be called from blog path (#5006) + * Update text to be consistent with example (#5010) + * Update template links to point to core Liquid site (#5012) + * Add generator-jekyllized to third-party plugins (#5027) + * Add Jekyll Art Gallery generator plugin to list of third-party plugins (#5043) + * Add Formingo to the list of Jekyll form SaaS (#5054) + * Highlight help nav item when navigated to. (#5058) + * Update normalize.css to v4.2.0. (#5096) + * Updates html-proofer code (#5098) + * Jekyll Community (#5097) + * Typo in documentation file templates.md (#5117) + * Slightly, improve PNG compression. (#5112) + * docs: add jekyll-maps plugin reference (#5123) + * docs: fix link to plugins page source (#5122) + * Update the configuration docs to match the code (#5131) + * Removed confusing word repetition. (#5139) + * Add a note about script/fmt (#5138) + +## 3.1.6 / 2016-05-19 + +### Bug Fixes + + * Add ability to `jsonify` Drops such that, e.g. `site | jsonify`, works. (#4914) + +## 3.1.5 / 2016-05-18 + +### Bug Fixes + + * Sort the results of the `require_all` glob (affects Linux only). (#4912) + +## 3.1.4 / 2016-05-18 + +### Bug Fixes + + * Add `ExcerptDrop` and remove excerpt's ability to refer to itself in Liquid (#4907) + * Configuration permalink fix where `collections.posts.permalink` inherits properly from `permalink` only when it doesn't exist (#4910) + * Add `Configuration.from` to make it easier to build configs from just a hash + * Sorting `site.collections` in Liquid by label (#4910) + * Fix bug where `layout` in Liquid would inherit from previously-rendered layouts' metadatas (#4909) + * Fix bug where `layout` in Liquid would override in the wrong direction (more-specific layouts' data were overwritten by their parent layouts' data; this has now been reversed) (#4909) + +## 3.1.3 / 2016-04-18 + + * Fix defaults for Documents to lookup defaults based on `relative_path` instead of `url` (#4807) + * Use SSLEnable instead of EnableSSL and make URL HTTPS (WEBrick) (#4693) + +## 3.1.2 / 2016-02-19 + +### Minor Enhancements + + * Include `.rubocop.yml` in Gem (#4437) + * `LiquidRenderer#parse`: parse with line numbers. (#4452) + * Add consistency to the no-subcommand deprecation message (#4505) + +### Bug Fixes + + * Fix syntax highlighting in kramdown by making `@config` accessible in the Markdown converter. (#4428) + * `Jekyll.sanitized_path`: sanitizing a questionable path should handle tildes (#4492) + * Fix `titleize` so already capitalized words are not dropped (#4525) + * Permalinks which end in a slash should always output HTML (#4546) + +### Development Fixes + + * Require at least cucumber version 2.1.0 (#4514) + +### Site Enhancements + + * Add jekyll-toc plugin (#4429) + * Docs: Quickstart - added documentation about the `--force` option (#4410) + * 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) + * 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) + * Update & prune the short list of example sites (#4374) + * Added amp-jekyll plugin to plugins docs (#4517) + * A few grammar fixes (#4512) + * Correct a couple mistakes in structure.md (#4522) + +## 3.1.1 / 2016-01-29 + +### Meta + + * Update the Code of Conduct to the latest version (#4402) + +### Bug Fixes + + * `Page#dir`: ensure it ends in a slash (#4403) + * Add `Utils.merged_file_read_opts` to unify reading & strip the BOM (#4404) + * `Renderer#output_ext`: honor folders when looking for ext (#4401) + +### Development Fixes + + * Suppress stdout in liquid profiling test (#4409) + +## 3.1.0 / 2016-01-23 + +### Minor Enhancements + + * Use `Liquid::Drop`s instead of `Hash`es in `#to_liquid` (#4277) + * Add 'sample' Liquid filter Equivalent to Array#sample functionality (#4223) + * Cache parsed include file to save liquid parsing time. (#4120) + * Slightly speed up url sanitization and handle multiples of ///. (#4168) + * Print debug message when a document is skipped from reading (#4180) + * Include tag should accept multiple variables in the include name (#4183) + * Add `-o` option to serve command which opens server URL (#4144) + * Add CodeClimate platform for better code quality. (#4220) + * General improvements for WEBrick via jekyll serve such as SSL & custom headers (#4224, #4228) + * Add a default charset to content-type on webrick. (#4231) + * Switch `PluginManager` to use `require_with_graceful_fail` for better UX (#4233) + * Allow quoted date in front matter defaults (#4184) + * Add a Jekyll doctor warning for URLs that only differ by case (#3171) + * drops: create one base Drop class which can be set as mutable or not (#4285) + * drops: provide `#to_h` to allow for hash introspection (#4281) + * Shim subcommands with indication of gem possibly required so users know how to use them (#4254) + * Add smartify Liquid filter for SmartyPants (#4323) + * Raise error on empty permalink (#4361) + * Refactor Page#permalink method (#4389) + +### Bug Fixes + + * Pass build options into `clean` command (#4177) + * Allow users to use .htm and .xhtml (XHTML5.) (#4160) + * Prevent Shell Injection. (#4200) + * Convertible should make layout data accessible via `layout` instead of `page` (#4205) + * Avoid using `Dir.glob` with absolute path to allow special characters in the path (#4150) + * Handle empty config files (#4052) + * Rename `@options` so that it does not impact Liquid. (#4173) + * utils/drops: update Drop to support `Utils.deep_merge_hashes` (#4289) + * Make sure jekyll/drops/drop is loaded first. (#4292) + * Convertible/Page/Renderer: use payload hash accessor & setter syntax for backwards-compatibility (#4311) + * Drop: fix hash setter precedence (#4312) + * utils: `has_yaml_header?` should accept files with extraneous spaces (#4290) + * Escape html from site.title and page.title in site template (#4307) + * Allow custom file extensions if defined in `permalink` front matter (#4314) + * 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) + * Extract title from filename successfully when no date. (#4195) + +### Development Fixes + + * `jekyll-docs` should be easily release-able (#4152) + * Allow use of Cucumber 2.1 or greater (#4181) + * Modernize Kramdown for Markdown converter. (#4109) + * Change TestDoctorCommand to JekyllUnitTest... (#4263) + * Create namespaced rake tasks in separate `.rake` files under `lib/tasks` (#4282) + * markdown: refactor for greater readability & efficiency (#3771) + * Fix many Rubocop style errors (#4301) + * Fix spelling of "GitHub" in docs and history (#4322) + * Reorganize and cleanup the Gemfile, shorten required depends. (#4318) + * Remove script/rebund. (#4341) + * Implement codeclimate platform (#4340) + * Remove ObjectSpace dumping and start using inherited, it's faster. (#4342) + * Add script/travis so all people can play with Travis-CI images. (#4338) + * Move Cucumber to using RSpec-Expectations and furthering JRuby support. (#4343) + * Rearrange Cucumber and add some flair. (#4347) + * Remove old FIXME (#4349) + * Clean up the Gemfile (and keep all the necessary dependencies) (#4350) + +### Site Enhancements + + * Add three plugins to directory (#4163) + * Add upgrading docs from 2.x to 3.x (#4157) + * Add `protect_email` to the plugins index. (#4169) + * Add `jekyll-deploy` to list of third-party plugins (#4179) + * Clarify plugin docs (#4154) + * Add Kickster to deployment methods in documentation (#4190) + * Add DavidBurela's tutorial for Windows to Windows docs page (#4210) + * Change GitHub code block to highlight tag to avoid it overlaps parent div (#4121) + * Update FormKeep link to be something more specific to Jekyll (#4243) + * Remove example Roger Chapman site, as the domain doesn't exist (#4249) + * Added configuration options for `draft_posts` to configuration docs (#4251) + * Fix checklist in `_assets.md` (#4259) + * Add Markdown examples to Pages docs (#4275) + * Add jekyll-paginate-category to list of third-party plugins (#4273) + * Add `jekyll-responsive_image` to list of third-party plugins (#4286) + * Add `jekyll-commonmark` to list of third-party plugins (#4299) + * Add documentation for incremental regeneration (#4293) + * Add note about removal of relative permalink support in upgrading docs (#4303) + * Add Pro Tip to use front matter variable to create clean URLs (#4296) + * Fix grammar in the documentation for posts. (#4330) + * Add documentation for smartify Liquid filter (#4333) + * Fixed broken link to blog on using mathjax with jekyll (#4344) + * Documentation: correct reference in Precedence section of Configuration docs (#4355) + * Add @jmcglone's guide to github-pages doc page (#4364) + * Added the Wordpress2Jekyll Wordpress plugin (#4377) + * Add Contentful Extension to list of third-party plugins (#4390) + * Correct Minor spelling error (#4394) + +## 3.0.5 / 2016-04-26 + + * Remove call to `#backwards_compatibilize` in `Configuration.from` (#4846) + +## 3.0.4 / 2016-04-18 + + * Fix defaults for Documents to lookup defaults based on `relative_path` instead of `url` (#4806) + * Configuration: allow users to specify a `collections.posts.permalink` directly without `permalink` clobbering it (#4753) + +## 3.0.3 / 2016-02-08 + +### Bug Fixes + + * Fix extension weirdness with folders (#4493) + * EntryFilter: only include 'excluded' log on excluded files (#4479) + * `Jekyll.sanitized_path`: escape tildes before sanitizing a questionable path (#4468) + * `LiquidRenderer#parse`: parse with line numbers (#4453) + * `Document#<=>`: protect against nil comparison in dates. (#4446) + +## 3.0.2 / 2016-01-20 + +### Bug Fixes + + * Document: throw a useful error when an invalid date is given (#4378) + +## 3.0.1 / 2015-11-17 + +### Bug Fixes + + * Document: only superdirectories of the collection are categories (#4110) + * `Convertible#render_liquid` should use `render!` to cause failure on bad Liquid (#4077) + * Don't generate `.jekyll-metadata` in non-incremental build (#4079) + * Set `highlighter` config val to `kramdown.syntax_highlighter` (#4090) + * Align hooks implementation with documentation (#4104) + * Fix the deprecation warning in the doctor command (#4114) + * Fix case in `:title` and add `:slug` which is downcased (#4100) + +### Development Fixes + + * Fix test warnings when doing rake {test,spec} or script/test (#4078) + +### Site Enhancements + + * Update normalize.css to v3.0.3. (#4085) + * Update Font Awesome to v4.4.0. (#4086) + * Adds a note about installing the jekyll-gist gem to make gist tag work (#4101) + * Align hooks documentation with implementation (#4104) + * Add Jekyll Flickr Plugin to the list of third party plugins (#4111) + * Remove link to now-deleted blog post (#4125) + * Update the liquid syntax in the pagination docs (#4130) + * Add jekyll-language-plugin to plugins.md (#4134) + * Updated to reflect feedback in #4129 (#4137) + * Clarify assets.md based on feedback of #4129 (#4142) + * Re-correct the liquid syntax in the pagination docs (#4140) + +## 3.0.0 / 2015-10-26 + +### Major Enhancements + + * Liquid profiler (i.e. know how fast or slow your templates render) (#3762) + * Incremental regeneration (#3116) + * Add Hooks: a new kind of plugin (#3553) + * Upgrade to Liquid 3.0.0 (#3002) + * `site.posts` is now a Collection instead of an Array (#4055) + * Add basic support for JRuby (commit: 0f4477) + * Drop support for Ruby 1.9.3. (#3235) + * Support Ruby v2.2 (#3234) + * Support RDiscount 2 (#2767) + * Remove most runtime deps (#3323) + * Move to Rouge as default highlighter (#3323) + * Mimic GitHub Pages `.html` extension stripping behavior in WEBrick (#3452) + * Always include file extension on output files (#3490) + * Improved permalinks for pages and collections (#3538) + * Sunset (i.e. remove) Maruku (#3655) + * Remove support for relative permalinks (#3679) + * Iterate over `site.collections` as an array instead of a hash. (#3670) + * Adapt StaticFile for collections, config defaults (#3823) + * Add a Code of Conduct for the Jekyll project (#3925) + * Added permalink time variables (#3990) + * Add `--incremental` flag to enable incremental regen (disabled by default) (#4059) + +### Minor Enhancements + + * Deprecate access to Document#data properties and Collection#docs methods (#4058) + * Sort static files just once, and call `site_payload` once for all collections (#3204) + * Separate `jekyll docs` and optimize external gem handling (#3241) + * Improve `Site#getConverterImpl` and call it `Site#find_converter_instance` (#3240) + * Use relative path for `path` Liquid variable in Documents for consistency (#2908) + * Generalize `Utils#slugify` for any scripts (#3047) + * Added basic microdata to post template in site template (#3189) + * Store log messages in an array of messages. (#3244) + * Allow collection documents to override `output` property in front matter (#3172) + * Keep file modification times between builds for static files (#3220) + * Only downcase mixed-case categories for the URL (#2571) + * Added per post `excerpt_separator` functionality (#3274) + * Allow collections YAML to end with three dots (#3134) + * Add mode parameter to `slugify` Liquid filter (#2918) + * Perf: `Markdown#matches` should avoid regexp (#3321) + * Perf: Use frozen regular expressions for `Utils#slugify` (#3321) + * Split off Textile support into jekyll-textile-converter (#3319) + * Improve the navigation menu alignment in the site template on small screens (#3331) + * Show the regeneration time after the initial generation (#3378) + * Site template: Switch default font to Helvetica Neue (#3376) + * Make the `include` tag a teensy bit faster. (#3391) + * Add `pkill -f jekyll` to ways to kill. (#3397) + * Site template: collapsed, variable-driven font declaration (#3360) + * Site template: Don't always show the scrollbar in code blocks (#3419) + * Site template: Remove undefined `text` class from `p` element (#3440) + * Site template: Optimize text rendering for legibility (#3382) + * Add `draft?` method to identify if Post is a Draft & expose to Liquid (#3456) + * Write regeneration metadata even on full rebuild (#3464) + * Perf: Use `String#end_with?("/")` instead of regexp when checking paths (#3516) + * Docs: document 'ordinal' built-in permalink style (#3532) + * Upgrade liquid-c to 3.x (#3531) + * Use consistent syntax for deprecation warning (#3535) + * Added build --destination and --source flags (#3418) + * Site template: remove unused `page.meta` attribute (#3537) + * Improve the error message when sorting null objects (#3520) + * Added liquid-md5 plugin (#3598) + * Documentation: RR replaced with RSpec Mocks (#3600) + * Documentation: Fix subpath. (#3599) + * Create 'tmp' dir for test_tags if it doesn't exist (#3609) + * Extract reading of data from `Site` to reduce responsibilities. (#3545) + * Removed the word 'Jekyll' a few times from the comments (#3617) + * `bin/jekyll`: with no args, exit with exit code 1 (#3619) + * Incremental build if destination file missing (#3614) + * Static files `mtime` liquid should return a `Time` obj (#3596) + * Use `Jekyll::Post`s for both LSI indexing and lookup. (#3629) + * Add `charset=utf-8` for HTML and XML pages in WEBrick (#3649) + * Set log level to debug when verbose flag is set (#3665) + * Added a mention on the Gemfile to complete the instructions (#3671) + * Perf: Cache `Document#to_liquid` and invalidate where necessary (#3693) + * Perf: `Jekyll::Cleaner#existing_files`: Call `keep_file_regex` and `keep_dirs` only once, not once per iteration (#3696) + * Omit jekyll/jekyll-help from list of resources. (#3698) + * Add basic `jekyll doctor` test to detect fsnotify (OSX) anomalies. (#3704) + * Added talk.jekyllrb.com to "Have questions?" (#3694) + * Performance: Sort files only once (#3707) + * Performance: Marshal metadata (#3706) + * Upgrade highlight wrapper from `div` to `figure` (#3779) + * Upgrade mime-types to `~> 2.6` (#3795) + * Update windows.md with Ruby version info (#3818) + * Make the directory for includes configurable (#3782) + * Rename directory configurations to match `*_dir` convention for consistency (#3782) + * Internal: trigger hooks by owner symbol (#3871) + * Update MIME types from mime-db (#3933) + * Add header to site template `_config.yml` for clarity & direction (#3997) + * Site template: add timezone offset to post date front matter (#4001) + * Make a constant for the regex to find hidden files (#4032) + * Site template: refactor github & twitter icons into includes (#4049) + * Site template: add background to Kramdown Rouge-ified backtick code blocks (#4053) + +### Bug Fixes + + * `post_url`: fix access deprecation warning & fix deprecation msg (#4060) + * Perform jekyll-paginate deprecation warning correctly. (#3580) + * Make permalink parsing consistent with pages (#3014) + * `time()`pre-filter method should accept a `Date` object (#3299) + * Remove unneeded end tag for `link` in site template (#3236) + * Kramdown: Use `enable_coderay` key instead of `use_coderay` (#3237) + * Unescape `Document` output path (#2924) + * Fix nav items alignment when on multiple rows (#3264) + * Highlight: Only Strip Newlines/Carriage Returns, not Spaces (#3278) + * Find variables in front matter defaults by searching with relative file path. (#2774) + * Allow variables (e.g `:categories`) in front matter permalinks (#3320) + * Handle nil URL placeholders in permalinks (#3325) + * Template: Fix nav items alignment when in "burger" mode (#3329) + * Template: Remove `!important` from nav SCSS introduced in #3329 (#3375) + * The `:title` URL placeholder for collections should be the filename slug. (#3383) + * Trim the generate time diff to just 3 places past the decimal place (#3415) + * The highlight tag should only clip the newlines before and after the *entire* block, not in between (#3401) + * highlight: fix problem with linenos and rouge. (#3436) + * `Site#read_data_file`: read CSV's with proper file encoding (#3455) + * Ignore `.jekyll-metadata` in site template (#3496) + * Template: Point documentation link to the documentation pages (#3502) + * Removed the trailing slash from the example `/blog` baseurl comment (#3485) + * Clear the regenerator cache every time we process (#3592) + * Readd (bring back) minitest-profile (#3628) + * Add WOFF2 font MIME type to Jekyll server MIME types (#3647) + * Be smarter about extracting the extname in `StaticFile` (#3632) + * Process metadata for all dependencies (#3608) + * Show error message if the front matter on a page/post is invalid. (#3643) + * Upgrade redcarpet to 3.2 (Security fix: OSVDB-120415) (#3652) + * Create #mock_expects that goes directly to RSpec Mocks. (#3658) + * Open `.jekyll-metadata` in binary mode to read binary Marshal data (#3713) + * Incremental regeneration: handle deleted, renamed, and moved dependencies (#3717) + * Fix typo on line 19 of pagination.md (#3760) + * Fix it so that 'blog.html' matches 'blog.html' (#3732) + * Remove occasionally-problematic `ensure` in `LiquidRenderer` (#3811) + * Fixed an unclear code comment in site template SCSS (#3837) + * Fix reading of binary metadata file (#3845) + * Remove var collision with site template header menu iteration variable (#3838) + * Change non-existent `hl_linenos` to `hl_lines` to allow passthrough in safe mode (#3787) + * Add missing flag to disable the watcher (#3820) + * Update CI guide to include more direct explanations of the flow (#3891) + * Set `future` to `false` in the default config (#3892) + * filters: `where` should compare stringified versions of input & comparator (#3935) + * Read build options for `jekyll clean` command (#3828) + * Fix #3970: Use Gem::Version to compare versions, not `>`. + * Abort if no subcommand. Fixes confusing message. (#3992) + * Whole-post excerpts should match the post content (#4004) + * Change default font weight to 400 to fix bold/strong text issues (#4050) + * Document: Only auto-generate the excerpt if it's not overridden (#4062) + * Utils: `deep_merge_hashes` should also merge `default_proc` (45f69bb) + * Defaults: compare paths in `applies_path?` as `String`s to avoid confusion (7b81f00) + +### Development Fixes + + * Remove loader.rb and "modernize" `script/test`. (#3574) + * Improve the grammar in the documentation (#3233) + * Update the LICENSE text to match the MIT license exactly (#3253) + * Update rake task `site:publish` to fix minor bugs. (#3254) + * Switch to shields.io for the README badges. (#3255) + * Use `FileList` instead of `Dir.glob` in `site:publish` rake task (#3261) + * Fix test script to be platform-independent (#3279) + * Instead of symlinking `/tmp`, create and symlink a local `tmp` in the tests (#3258) + * Fix some spacing (#3312) + * Fix comment typo in `lib/jekyll/frontmatter_defaults.rb` (#3322) + * Move all `regenerate?` checking to `Regenerator` (#3326) + * Factor out a `read_data_file` call to keep things clean (#3380) + * Proof the site with CircleCI. (#3427) + * Update LICENSE to 2015. (#3477) + * Upgrade tests to use Minitest (#3492) + * Remove trailing whitespace (#3497) + * Use `fixture_site` for Document tests (#3511) + * Remove adapters deprecation warning (#3529) + * Minor fixes to `url.rb` to follow GitHub style guide (#3544) + * Minor changes to resolve deprecation warnings (#3547) + * Convert remaining textile test documents to markdown (#3528) + * Migrate the tests to use rspec-mocks (#3552) + * Remove `activesupport` (#3612) + * Added tests for `Jekyll:StaticFile` (#3633) + * Force minitest version to 5.5.1 (#3657) + * Update the way cucumber accesses Minitest assertions (#3678) + * Add `script/rubyprof` to generate cachegrind callgraphs (#3692) + * Upgrade cucumber to 2.x (#3795) + * Update Kramdown. (#3853) + * Updated the scripts shebang for portability (#3858) + * Update JRuby testing to 9K ([3ab386f](https://github.com/jekyll/jekyll/commit/3ab386f1b096be25a24fe038fc70fd0fb08d545d)) + * Organize dependencies into dev and test groups. (#3852) + * Contributing.md should refer to `script/cucumber` (#3894) + * Update contributing documentation to reflect workflow updates (#3895) + * Add script to vendor mime types (#3933) + * Ignore .bundle dir in SimpleCov (#4033) + +### Site Enhancements + + * Add 'info' labels to certain notes in collections docs (#3601) + * Remove extra spaces, make the last sentence less awkward in permalink docs (#3603) + * Update the permalinks documentation to reflect the updates for 3.0 (#3556) + * Add blog post announcing Jekyll Help (#3523) + * Add Jekyll Talk to Help page on site (#3518) + * Change Ajax pagination resource link to use HTTPS (#3570) + * Fixing the default host on docs (#3229) + * Add `jekyll-thumbnail-filter` to list of third-party plugins (#2790) + * Add link to 'Adding Ajax pagination to Jekyll' to Resources page (#3186) + * Add a Resources link to tutorial on building dynamic navbars (#3185) + * Semantic structure improvements to the post and page layouts (#3251) + * Add new AsciiDoc plugin to list of third-party plugins. (#3277) + * Specify that all transformable collection documents must contain front matter (#3271) + * Assorted accessibility fixes (#3256) + * Update configuration docs to mention `keep_files` for `destination` (#3288, #3296) + * Break when we successfully generate nav link to save CPU cycles. (#3291) + * Update usage docs to mention `keep_files` and a warning about `destination` cleaning (#3295) + * Add logic to automatically generate the `next_section` and `prev_section` navigation items (#3292) + * Some small fixes for the Plugins TOC. (#3306) + * Added versioning comment to configuration file (#3314) + * Add `jekyll-minifier` to list of third-party plugins (#3333) + * Add blog post about the Jekyll meet-up (#3332) + * Use `highlight` Liquid tag instead of the four-space tabs for code (#3336) + * 3.0.0.beta1 release post (#3346) + * Add `twa` to the list of third-party plugins (#3384) + * Remove extra spaces (#3388) + * Fix small grammar errors on a couple pages (#3396) + * Fix typo on Templates docs page (#3420) + * s/three/four for plugin type list (#3424) + * Release jekyllrb.com as a locally-compiled site. (#3426) + * Add a jekyllrb.com/help page which elucidates places from which to get help (#3428) + * Remove extraneous dash on Plugins doc page which caused a formatting error (#3431) + * Fix broken link to Jordan Thornquest's website. (#3438) + * Change the link to an extension (#3457) + * Fix Twitter link on the help page (#3466) + * Fix wording in code snippet highlighting section (#3475) + * Add a `/` to `paginate_path` in the Pagination documentation (#3479) + * Add a link on all the docs pages to "Improve this page". (#3510) + * Add jekyll-auto-image generator to the list of third-party plugins (#3489) + * Replace link to the proposed `picture` element spec (#3530) + * Add front matter date formatting information (#3469) + * Improve consistency and clarity of plugins options note (#3546) + * Add permalink warning to pagination docs (#3551) + * Fix grammar in Collections docs API stability warning (#3560) + * Restructure `excerpt_separator` documentation for clarity (#3550) + * Fix accidental line break in collections docs (#3585) + * Add information about the `.jekyll-metadata` file (#3597) + * Document addition of variable parameters to an include (#3581) + * Add `jekyll-files` to the list of third-party plugins. (#3586) + * Define the `install` step in the CI example `.travis.yml` (#3622) + * Expand collections documentation. (#3638) + * Add the "warning" note label to excluding `vendor` in the CI docs page (#3623) + * Upgrade pieces of the Upgrading guide for Jekyll 3 (#3607) + * Showing how to access specific data items (#3468) + * Clarify pagination works from within HTML files (#3467) + * Add note to `excerpt_separator` documentation that it can be set globally (#3667) + * Fix some names on Troubleshooting page (#3683) + * Add `remote_file_content` tag plugin to list of third-party plugins (#3691) + * Update the Redcarpet version on the Configuration page. (#3743) + * Update the link in the welcome post to point to Jekyll Talk (#3745) + * Update link for navbars with data attributes tutorial (#3728) + * Add `jekyll-asciinema` to list of third-party plugins (#3750) + * Update pagination example to be agnostic to first pagination dir (#3763) + * Detailed instructions for rsync deployment method (#3848) + * Add Jekyll Portfolio Generator to list of plugins (#3883) + * Add `site.html_files` to variables docs (#3880) + * Add Static Publisher tool to list of deployment methods (#3865) + * Fix a few typos. (#3897) + * Add `jekyll-youtube` to the list of third-party plugins (#3931) + * Add Views Router plugin (#3950) + * Update install docs (Core dependencies, Windows reqs, etc) (#3769) + * Use Jekyll Feed for jekyllrb.com (#3736) + * Add jekyll-umlauts to plugins.md ($3966) + * Troubleshooting: fix broken link, add other mac-specific info (#3968) + * Add a new site for learning purposes (#3917) + * Added documentation for Jekyll environment variables (#3989) + * Fix broken configuration documentation page (#3994) + * Add troubleshooting docs for installing on El Capitan (#3999) + * Add Lazy Tweet Embedding to the list of third-party plugins (#4015) + * Add installation instructions for 2 of 3 options for plugins (#4013) + * Add alternative jekyll gem installation instructions (#4018) + * Fix a few typos and formatting problems. (#4022) + * Fix pretty permalink example (#4029) + * Note that `_config.yml` is not reloaded during regeneration (#4034) + * Apply code block figure syntax to blocks in CONTRIBUTING (#4046) + * Add jekyll-smartify to the list of third-party plugins (#3572) + +## 2.5.3 / 2014-12-22 + +### Bug Fixes + + * When checking a Markdown extname, include position of the `.` (#3147) + * Fix `jsonify` Liquid filter handling of boolean values (#3154) + * Add comma to value of `viewport` meta tag (#3170) + * Set the link type for the RSS feed to `application/rss+xml` (#3176) + * Refactor `#as_liquid` (#3158) + +### Development Fixes + + * Exclude built-in bundles from being added to coverage report (#3180) + +### Site Enhancements + + * Add @alfredxing to the @jekyll/core team. :tada: (#3218) + * Document the `-q` option for the `build` and `serve` commands (#3149) + * Fix some minor typos/flow fixes in documentation website content (#3165) + * Add `keep_files` to configuration documentation (#3162) + * Repeat warning about cleaning of the `destination` directory (#3161) + * Add jekyll-500px-embed to list of third-party plugins (#3163) + * Simplified platform detection in Gemfile example for Windows (#3177) + * Add the `jekyll-jalali` plugin added to the list of third-party plugins. (#3198) + * Add Table of Contents to Troubleshooting page (#3196) + * Add `inline_highlight` plugin to list of third-party plugins (#3212) + * Add `jekyll-mermaid` plugin to list of third-party plugins (#3222) + +## 2.5.2 / 2014-11-17 + +### Minor Enhancements + + * `post_url` should match `post.name` instead of slugs and dates (#3058) + +### Bug Fixes + + * Fix bundle require for `:jekyll_plugins` (#3119) + * Remove duplicate regexp phrase: `^\A` (#3089) + * Remove duplicate `Conversion error:` message in `Convertible` (#3088) + * Print full conversion error message in `Renderer#convert` (#3090) + +### Site Enhancements + + * Change variable names in Google Analytics script (#3093) + * Mention CSV files in the docs for data files (#3101) + * Add trailing slash to `paginate_path` example. (#3091) + * Get rid of noifniof (`excerpt_separator`) (#3094) + * Sass improvements, around nesting mostly. (#3123) + * Add webmentions.io plugin to the list of third-party plugins (#3127) + * Add Sass mixins and use them. (#2904) + * Slightly compress jekyll-sticker.jpg. (#3133) + * Update gridism and separate out related but custom styles. (#3132) + * Add remote-include plugin to list of third-party plugins (#3136) + +## 2.5.1 / 2014-11-09 + +### Bug Fixes + + * Fix path sanitation bug related to Windows drive names (#3077) + +### Development Fixes + + * Add development time dependencies on minitest and test-unit to gemspec for cygwin (#3064) + * Use Travis's built-in caching. (#3075) + +## 2.5.0 / 2014-11-06 + +### Minor Enhancements + + * Require gems in `:jekyll_plugins` Gemfile group unless `JEKYLL_NO_BUNDLER_REQUIRE` is specified in the environment. (#2865) + * Centralize path sanitation in the `Site` object (#2882) + * Allow placeholders in permalinks (#3031) + * Allow users to specify the log level via `JEKYLL_LOG_LEVEL`. (#3067) + * Fancy Indexing with WEBrick (#3018) + * Allow Enumerables to be used with `where` filter. (#2986) + * Meta descriptions in the site template now use `page.excerpt` if it's available (#2964) + * Change indentation in `head.html` of site template to 2 spaces from 4 (#2973) + * Use a `$content-width` variable instead of a fixed value in the site template CSS (#2972) + * Strip newlines in site template `<meta>` description. (#2982) + * Add link to atom feed in `head` of site template files (#2996) + * Performance optimizations (#2994) + * Use `Hash#each_key` instead of `Hash#keys.each` to speed up iteration over hash keys. (#3017) + * Further minor performance enhancements. (#3022) + * Add 'b' and 's' aliases for build and serve, respectively (#3065) + +### Bug Fixes + + * Fix Rouge's RedCarpet plugin interface integration (#2951) + * Remove `--watch` from the site template blog post since it defaults to watching in in 2.4.0 (#2922) + * Fix code for media query mixin in site template (#2946) + * Allow post URLs to have `.htm` extensions (#2925) + * `Utils.slugify`: Don't create new objects when gsubbing (#2997) + * The jsonify filter should deep-convert to Liquid when given an Array. (#3032) + * Apply `jsonify` filter to Hashes deeply and effectively (#3063) + * Use `127.0.0.1` as default host instead of `0.0.0.0` (#3053) + * In the case that a Gemfile does not exist, ensure Jekyll doesn't fail on requiring the Gemfile group (#3066) + +### Development Fixes + + * Fix a typo in the doc block for `Jekyll::URL.escape_path` (#3052) + * Add integration test for `jekyll new --blank` in TestUnit (#2913) + * Add unit test for `jekyll new --force` logic (#2929) + * Update outdated comment for `Convertible#transform` (#2957) + * Add Hakiri badge to README. (#2953) + * Add some simple benchmarking tools. (#2993) + +### Site Enhancements + + * `NOKOGIRI_USE_SYSTEM_LIBRARIES=true` **decreases** installation time. (#3040) + * Add FormKeep to resources as Jekyll form backend (#3010) + * Fixing a mistake in the name of the new Liquid tag (#2969) + * Update Font Awesome to v4.2.0. (#2898) + * Fix link to #2895 in 2.4.0 release post. (#2899) + * Add Big Footnotes for Kramdown plugin to list of third-party plugins (#2916) + * Remove warning regarding GHP use of singular types for front matter defaults (#2919) + * Fix quote character typo in site documentation for templates (#2917) + * Point Liquid links to Liquid’s GitHub wiki (#2887) + * Add HTTP Basic Auth (.htaccess) plugin to list of third-party plugins (#2931) + * (Minor) Grammar & `_config.yml` filename fixes (#2911) + * Added `mathml.rb` to the list of third-party plugins. (#2937) + * Add `--force_polling` to the list of configuration options (#2943) + * Escape unicode characters in site CSS (#2906) + * Add note about using the github-pages gem via pages.github.com/versions.json (#2939) + * Update usage documentation to reflect 2.4 auto-enabling of `--watch`. (#2954) + * Add `--skip-initial-build` to configuration docs (#2949) + * Fix a minor typo in Templates docs page (#2959) + * Add a ditaa-ditaa plugin under Other section on the Plugins page (#2967) + * Add `build/serve -V` option to configuration documentation (#2948) + * Add 'Jekyll Twitter Plugin' to list of third-party plugins (#2979) + * Docs: Update normalize.css to v3.0.2. (#2981) + * Fix typo in Continuous Integration documentation (#2984) + * Clarify behavior of `:categories` in permalinks (#3011) + +## 2.4.0 / 2014-09-09 + +### Minor Enhancements + + * Support a new `relative_include` tag (#2870) + * Auto-enable watch on 'serve' (#2858) + * Render Liquid in CoffeeScript files (#2830) + * Array Liquid filters: `push`, `pop`, `unshift`, `shift` (#2895) + * Add `:title` to collection URL template fillers (#2864) + * Add support for CSV files in the `_data` directory (#2761) + * Add the `name` variable to collection permalinks (#2799) + * Add `inspect` liquid filter. (#2867) + * Add a `slugify` Liquid filter (#2880) + +### Bug Fixes + + * Use `Jekyll.sanitized_path` when adding static files to Collections (#2849) + * Fix encoding of `main.scss` in site template (#2771) + * Fix orientation bugs in default site template (#2862) + +### Development Fixes + + * Update simplecov gem to 0.9 (#2748) + * Remove `docs/` dir (#2768) + * add class `<< self` idiom to `New` command (#2817) + * Allow Travis to 'parallelize' our tests (#2859) + * Fix test for Liquid rendering in Sass (#2856) + * Fixing "vertycal" typo in site template's `_base.scss` (#2889) + +### Site Enhancements + + * Document the `name` variable for collection permalinks (#2829) + * Adds info about installing jekyll in current dir (#2839) + * Remove deprecated `jekyll-projectlist` plugin from list of third-party plugins (#2742) + * Remove tag plugins that are built in to Jekyll (#2751) + * Add `markdown-writer` package for Atom Editor to list of third-party plugins (#2763) + * Fix typo in site documentation for collections (#2764) + * Fix minor typo on plugins docs page (#2765) + * Replace markdown with HTML in `sass_dir` note on assets page (#2791) + * Fixed "bellow" typo in datafiles docs (#2879) + * Fix code/markdown issue in documentation for variables (#2877) + * Remove Good Include third-party plugin from plugins page (#2881) + * Add some more docs on `include_relative` (#2884) + +## 2.3.0 / 2014-08-10 + +### Minor Enhancements + + * Allow Convertibles to be converted by >= 1 converters (#2704) + * Allow Sass files to be rendered in Liquid, but never place them in layouts. (#2733) + * Add `jekyll help` command (#2707) + * Use `.scss` for `site_template` styles. (#2667) + * Don't require the `scope` key in front matter defaults (#2659) + * No longer set `permalink: pretty` in the `_config.yml` for the site template (#2680) + * Rework site template to utilize Sass (#2687) + * Notify the user when auto-regeneration is disabled. (#2696) + * Allow partial variables in include tag filename argument (#2693) + * Move instances of `Time.parse` into a Utils method (#2682) + * Ignore subfolders in the `_posts` folder (#2705) REVERTS (#2633) + * Front Matter default types should always be pluralized (#2732) + * Read in static files into `collection.files` as `StaticFile`s (#2737) + * Add `sassify` and `scssify` Liquid filters (#2739) + * Replace `classifier` gem with `classifier-reborn` (#2721) + +### Bug Fixes + + * Use only the last extname when multiple converters exist (#2722) + * Call `#to_liquid` before calling `#to_json` in jsonify filter (#2729) + * Use non padded config in `strftime` to avoid parse string twice (#2673) + * Replace deprecated Ruby methods with undeprecated ones (#2664) + * Catch errors when parsing Post `date` front matter value & produce nice error message (#2649) + * Allow static files in Collections (#2615) + * Fixed typo in `Deprecator#gracefully_require` error message (#2694) + * Remove preemptive loading of the 'classifier' gem. (#2697) + * Use case-insensitive checking for the file extensions when loading config files (#2718) + * When Reading Documents, Respect `encoding` Option (#2720) + * Refactor based on jekyll-watch clean-up. (#2716) + * `Document#to_s` should produce just the content of the document (#2731) + +### Development Fixes + + * Only include lib files in the gem (#2671) + * Fix `git diff` command in `proof` script (#2672) + * Make default rake task a multitask so tests run in parallel (#2735) + +### Site Enhancements + + * Use Sass and a Docs Collection (#2651) + * Add `latest_version.txt` file to the site (#2740) + * Be more ambiguous about `page.content`. But more transparent. (#2522) + * Streamlining front matter wording (instead of front-matter/frontmatter) (#2674) + * Add note that source directory cannot be modified in GitHub Pages (#2669) + * Fix links from #2669 to be actual HTML. Whoops. (#2679) + * Add link to `jekyll-slim` in list of third-party plugins (#2689) + * Add Barry Clark's Smashing Magazine tutorial to resources page (#2688) + * Reorganize and update default configuration settings (#2456) + * Fixing indentation in the configuration docs about Redcarpet exts (#2717) + * Use `null` in YAML instead of `nil` in default config list (#2719) + * Fix typo in Continuous Integration docs (#2708) + +## 2.2.0 / 2014-07-29 + +### Minor Enhancements + + * Throw a warning if the specified layout does not exist (#2620) + * Whitelist Pygments options in safe mode (#2642) + +### Bug Fixes + + * Remove unnecessary `Jekyll::Tags::IncludeTag#blank?` method (#2625) + * Categories in the path are ignored (#2633) + +### Development Fixes + + * Refactoring Errors & Requires of Third-Party stuff (#2591) + * Add further tests for categories (#2584) + * Proof site with html-proofer on change (#2605) + * Fix up bug in #2605 which caused proofing the site not to function (#2608) + * Use `bundle exec` in `script/proof` (#2610) + +### Site Enhancements + + * Update Kramdown urls (#2588) + * Add `Jekyll::AutolinkEmail` and `Jekyll::GitMetadata` to the list of third-party plugins (#2596) + * Fix a bunch of broken links in the site (#2601) + * Replace dead links with working links (#2611) + * Add jekyll-hook to deployment methods (#2617) + * Added kramdown-with-pygments plugin to the list of third-party plugins (#2623) + * Update outdated "Extras" page and remove duplicate documentation (#2622) + * Add co2 plugin to list of third-party plugins (#2639) + * Attempt to clarify the way Sass imports happen (#2642) + +## 2.1.1 / 2014-07-01 + +### Bug Fixes + + * Patch read vulnerabilities for data & confirm none for layouts (#2563) + * Update Maruku dependency to allow use of the latest version (#2576) + * Remove conditional assignment from document URL to prevent stale urls (#2575) + +### Site Enhancements + + * Add vertical margin to `highlight` to separate code blocks (#2558) + * Add `html_pages` to Variables docs (#2567) + * Fixed broken link to Permalinks page (#2572) + * Update link to Windows installation guide (#2578) + +## 2.1.0 / 2014-06-28 + +### Minor Enhancements + + * Bump to the latest Liquid version, 2.6.1 (#2495) + * Add support for JSON files in the `_data` directory (#2369) + * Allow subclasses to override `EXCERPT_ATTRIBUTES_FOR_LIQUID` (#2408) + * Add `Jekyll.env` and `jekyll.environment` (the Liquid var) (#2417) + * Use `_config.yaml` or `_config.yml` (`.yml` takes precedence) (#2406) + * Override collection url template (#2418) + * Allow subdirectories in `_data` (#2395) + * Extract Pagination Generator into gem: `jekyll-paginate` (#2455) + * Utilize `date_to_rfc822` filter in site template (#2437) + * Add categories, last build datetime, and generator to site template feed (#2438) + * Configurable, replaceable Logger-compliant logger (#2444) + * Extract `gist` tag into a separate gem (#2469) + * Add `collection` attribute to `Document#to_liquid` to access the document's collection label. (#2436) + * Upgrade listen to `2.7.6 <= x < 3.0.0` (#2492) + * Allow configuration of different Twitter and GitHub usernames in site template (#2485) + * Bump Pygments to v0.6.0 (#2504) + * Front matter defaults for documents in collections (#2419) + * Include files with a url which ends in `/` in the `site.html_pages` list (#2524) + * Make `highlight` tag use `language-` prefix in CSS class (#2511) + * Lookup item property via `item#to_liquid` before `#data` or `#[]` in filters (#2493) + * Skip initial build of site on serve with flag (#2477) + * Add support for `hl_lines` in `highlight` tag (#2532) + * Spike out `--watch` flag into a separate gem (#2550) + +### Bug Fixes + + * Liquid `sort` filter should sort even if one of the values is `nil` (#2345) + * Remove padding on `pre code` in the site template CSS (#2383) + * Set `log_level` earlier to silence info level configuration output (#2393) + * Only list pages which have `title` in site template (#2411) + * Accept `Numeric` values for dates, not `Number` values (#2377) + * Prevent code from overflowing container in site template (#2429) + * Encode URLs in UTF-8 when escaping and unescaping (#2420) + * No Layouts or Liquid for Asset Files (#2431) + * Allow front matter defaults to set post categories (#2373) + * Fix command in subcommand deprecation warning (#2457) + * Keep all parent directories of files/dirs in `keep_files` (#2458) + * When using RedCarpet and Rouge without Rouge installed, fixed erroneous error which stated that redcarpet was missing, not rouge. (#2464) + * Ignore *all* directories and files that merit it on auto-generation (#2459) + * Before copying file, explicitly remove the old one (#2535) + * Merge file system categories with categories from YAML. (#2531) + * Deep merge front matter defaults (#2490) + * Ensure exclude and include arrays are arrays of strings (#2542) + * Allow collections to have dots in their filenames (#2552) + * Collections shouldn't try to read in directories as files (#2552) + * Be quiet very quickly. (#2520) + +### Development Fixes + + * Test Ruby 2.1.2 instead of 2.1.1 (#2374) + * Add test for sorting UTF-8 characters (#2384) + * Use `https` for GitHub links in documentation (#2470) + * Remove coverage reporting with Coveralls (#2494) + * Fix a bit of missing TomDoc to `Jekyll::Commands::Build#build` (#2554) + +### Site Enhancements + + * Set `timezone` to `America/Los_Angeles` (#2394) + * Improve JavaScript in `anchor_links.html` (#2368) + * Remove note on Quickstart page about default markdown converter (#2387) + * Remove broken link in extras.md to a Maruku fork (#2401) + * Update Font Awesome to v4.1.0. (#2410) + * Fix broken link on Installation page to Templates page (#2421) + * Prevent table from extending parent width in permalink style table (#2424) + * Add collections to info about pagination support (#2389) + * Add `jekyll_github_sample` plugin to list of third-party plugins (#2463) + * Clarify documentation around front matter defaults and add details about defaults for collections. (#2439) + * Add Jekyll Project Version Tag to list of third-party plugins (#2468) + * Use `https` for GitHub links across whole site (#2470) + * Add StickerMule + Jekyll post (#2476) + * Add Jekyll Asset Pipeline Reborn to list of third-party plugins (#2479) + * Add link to jekyll-compress-html to list of third-party plugins (#2514) + * Add Piwigo Gallery to list of third-party plugins (#2526) + * Set `show_drafts` to `false` in default configuration listing (#2536) + * Provide an updated link for Windows installation instructions (#2544) + * Remove `url` from configuration docs (#2547) + * Documentation for Continuous Integration for your Jekyll Site (#2432) + +## 2.0.3 / 2014-05-08 + +### Bug Fixes + + * Properly prefix links in site template with URL or baseurl depending upon need. (#2319) + * Update gist tag comments and error message to require username (#2326) + * Fix `permalink` setting in site template (#2331) + * Don't fail if any of the path objects are nil (#2325) + * Instantiate all descendants for converters and generators, not just direct subclasses (#2334) + * Replace all instances of `site.name` with `site.title` in site template (#2324) + * `Jekyll::Filters#time` now accepts UNIX timestamps in string or number form (#2339) + * Use `item_property` for `where` filter so it doesn't break on collections (#2359) + * Rescue errors thrown so `--watch` doesn't fail (#2364) + +### Site Enhancements + + * Add missing "as" to assets docs page (#2337) + * Update docs to reflect new `baseurl` default (#2341) + * Add links to headers who have an ID. (#2342) + * Use symbol instead of HTML number in `upgrading.md` (#2351) + * Fix link to front matter defaults docs (#2353) + * Fix for `History.markdown` in order to fix history page in docs (#2363) + +## 2.0.2 / 2014-05-07 + +### Bug Fixes + + * Correct use of `url` and `baseurl` in the site template. (#2317) + * Default `baseurl` to `""` (#2317) + +### Site Enhancements + + * Correct docs for the `gist` plugin so it always includes the username. (#2314) + * Clarify new (defaults, `where` filter) features in docs (#2316) + +## 2.0.1 / 2014-05-06 + +### Bug Fixes + + * Require `kramdown` gem instead of `maruku` gem + +## 2.0.0 / 2014-05-06 + +### Major Enhancements + + * Add "Collections" feature (#2199) + * Add gem-based plugin whitelist to safe mode (#1657) + * Replace the commander command line parser with a more robust solution for our needs called `mercenary` (#1706) + * Remove support for Ruby 1.8.x (#1780) + * Move to jekyll/jekyll from mojombo/jekyll (#1817) + * Allow custom markdown processors (#1872) + * Provide support for the Rouge syntax highlighter (#1859) + * Provide support for Sass (#1932) + * Provide a 300% improvement when generating sites that use `Post#next` or `Post#previous` (#1983) + * Provide support for CoffeeScript (#1991) + * Replace Maruku with Kramdown as Default Markdown Processor (#1988) + * Expose `site.static_files` to Liquid (#2075) + * Complete redesign of the template site generated by `jekyll new` (#2050) + * Update Listen from 1.x to 2.x (#2097) + * Front matter defaults (#2205) + * Deprecate `relative_permalinks` configuration option (default to `false`) (#2307) + * Exclude files based on prefix as well as `fnmatch?` (#2303) + +### Minor Enhancements + + * Move the EntryFilter class into the Jekyll module to avoid polluting the global namespace (#1800) + * Add `group_by` Liquid filter create lists of items grouped by a common property's value (#1788) + * Add support for Maruku's `fenced_code_blocks` option (#1799) + * Update Redcarpet dependency to ~> 3.0 (#1815) + * Automatically sort all pages by name (#1848) + * Better error message when time is not parseable (#1847) + * Allow `include` tag variable arguments to use filters (#1841) + * `post_url` tag should raise `ArgumentError` for invalid name (#1825) + * Bump dependency `mercenary` to `~> 0.2.0` (#1879) + * Bump dependency `safe_yaml` to `~> 1.0` (#1886) + * Allow sorting of content by custom properties (#1849) + * Add `--quiet` flag to silence output during build and serve (#1898) + * Add a `where` filter to filter arrays based on a key/value pair (#1875) + * Route 404 errors to a custom 404 page in development (#1899) + * Excludes are now relative to the site source (#1916) + * Bring MIME Types file for `jekyll serve` to complete parity with GH Pages servers (#1993) + * Adding Breakpoint to make new site template more responsive (#2038) + * Default to using the UTF-8 encoding when reading files. (#2031) + * Update Redcarpet dependency to ~> 3.1 (#2044) + * Remove support for Ruby 1.9.2 (#2045) + * Add `.mkdown` as valid Markdown extension (#2048) + * Add `index.xml` to the list of WEBrick directory index files (#2041) + * Make the `layouts` config key relative to CWD or to source (#2058) + * Update Kramdown to `~> 1.3` (#1894) + * Remove unnecessary references to `self` (#2090) + * Update to Mercenary v0.3.x (#2085) + * Ship Sass support as a separate gem (#2098) + * Extract core extensions into a Utils module (#2112) + * Refactor CLI & Commands For Greater Happiness (#2143) + * Provide useful error when Pygments returns `nil` and error out (#2148) + * Add support for unpublished drafts (#2164) + * Add `force_polling` option to the `serve` command (#2165) + * Clean up the `<head>` in the site template (#2186) + * Permit YAML blocks to end with three dots to better conform with the YAML spec (#2110) + * Use `File.exist?` instead of deprecated `File.exists?` (#2214) + * Require newline after start of front matter header (#2211) + * Add the ability for pages to be marked as `published: false` (#1492) + * Add `Jekyll::LiquidExtensions` with `.lookup_variable` method for easy looking up of variable values in a Liquid context. (#2253) + * Remove literal lang name from class (#2292) + * Return `utf-8` encoding in header for webrick error page response (#2289) + * Make template site easier to customize (#2268) + * Add two-digit year to permalink template option (#2301) + * Add `site.documents` to Liquid payload (list of all docs) (#2295) + * Take into account missing values in the Liquid sort filter (#2299) + +### Bug Fixes + + * Don't allow nil entries when loading posts (#1796) + * Remove the scrollbar that's always displayed in new sites generated from the site template (#1805) + * Add `#path` to required methods in `Jekyll::Convertible` (#1866) + * Default Maruku fenced code blocks to ON for 2.0.0-dev (#1831) + * Change short opts for host and port for `jekyll docs` to be consistent with other subcommands (#1877) + * Fix typos (#1910) + * Lock Maruku at 0.7.0 to prevent bugs caused by Maruku 0.7.1 (#1958) + * Fixes full path leak to source directory when using include tag (#1951) + * Don't generate pages that aren't being published (#1931) + * Use `SafeYAML.load` to avoid conflicts with other projects (#1982) + * Relative posts should never fail to build (#1976) + * Remove executable bits of non executable files (#2056) + * `#path` for a draft is now `_drafts` instead of `_posts` (#2042) + * Patch a couple show-stopping security vulnerabilities (#1946) + * Sanitize paths uniformly, in a Windows-friendly way (#2065, #2109) + * Update gem build steps to work correctly on Windows (#2118) + * Remove obsolete `normalize_options` method call from `bin/jekyll` (#2121) + * Remove `+` characters from Pygments lexer names when adding as a CSS class (#994) + * Remove some code that caused Ruby interpreter warnings (#2178) + * Only strip the drive name if it begins the string (#2175) + * Remove default post with invalid date from site template (#2200) + * Fix `Post#url` and `Page#url` escape (#1568) + * Strip newlines from the `{% highlight %}` block content (#1823) + * Load in `rouge` only when it's been requested as the highlighter (#2189) + * Convert input to string before XML escaping (`xml_escape` liquid filter) (#2244) + * Modify configuration key for Collections and reset properly. (#2238) + * Avoid duplicated output using `highlight` tag (#2264) + * Only use Jekyll.logger for output (#2307) + * Close the file descriptor in `has_yaml_header?` (#2310) + * Add `output` to `Document` liquid output hash (#2309) + +### Development Fixes + + * Add a link to the site in the README.md file (#1795) + * Add in History and site changes from `v1-stable` branch (#1836) + * Testing additions on the Excerpt class (#1893) + * Fix the `highlight` tag feature (#1859) + * Test Jekyll under Ruby 2.1.0 (#1900) + * Add script/cibuild for fun and profit (#1912) + * Use `Forwardable` for delegation between `Excerpt` and `Post` (#1927) + * Rename `read_things` to `read_content` (#1928) + * Add `script/branding` script for ASCII art lovin' (#1936) + * Update the README to reflect the repo move (#1943) + * Add the project vision to the README (#1935) + * Speed up Travis CI builds by using Rebund (#1985) + * Use Yarp as a Gem proxy for Travis CI (#1984) + * Remove Yarp as a Gem proxy for Travis CI (#2004) + * Move the reading of layouts into its own class (#2020) + * Test Sass import (#2009) + * Switch Maruku and Kramdown in lists of Runtime vs. Development dependencies (#2049) + * Clean up the gemspec for the project (#2095) + * Add Japanese translation of README and CONTRIBUTING docs. (#2081) + * Re-align the tables in Cucumber (#2108) + * Trim trailing spaces and convert tabs to spaces (#2122) + * Fix the failing Travis scenarios due to Cucumber issues (#2155) + * Wrap `bundle install` in `travis_retry` to retry when RubyGems fails (#2160) + * Refactor tags and categories (#1639) + * Extract plugin management into its own class (#2197) + * Add missing tests for `Command` (#2216) + * Update `rr` link in CONTRIBUTING doc (#2247) + * Streamline Cucumber execution of `jekyll` subcommands (#2258) + * Refactor `Commands::Serve`. (#2269) + * Refactor `highlight` tag (#2154) + * Update `Util` hash functions with latest from Rails (#2273) + * Workaround for Travis bug (#2290) + +### Site Enhancements + + * Document Kramdown's GFM parser option (#1791) + * Move CSS to includes & update normalize.css to v2.1.3 (#1787) + * Minify CSS only in production (#1803) + * Fix broken link to installation of Ruby on Mountain Lion blog post on Troubleshooting docs page (#1797) + * Fix issues with 1.4.1 release blog post (#1804) + * Add note about deploying to OpenShift (#1812) + * Collect all Windows-related docs onto one page (#1818) + * Fixed typo in datafiles doc page (#1854) + * Clarify how to access `site` in docs (#1864) + * Add closing `<code>` tag to `context.registers[:site]` note (#1867) + * Fix link to @mojombo's site source (#1897) + * Add `paginate: nil` to default configuration in docs (#1896) + * Add link to our License in the site footer (#1889) + * Add a charset note in "Writing Posts" doc page (#1902) + * Disallow selection of path and prompt in bash examples + * Add jekyll-compass to the plugin list (#1923) + * Add note in Posts docs about stripping `<p>` tags from excerpt (#1933) + * Add additional info about the new exclude behavior (#1938) + * Linkify 'awesome contributors' to point to the contributors graph on GitHub (#1940) + * Update `docs/sites.md` link to GitHub Training materials (#1949) + * Update `master` with the release info from 1.4.3 (#1947) + * Define docs nav in datafile (#1953) + * Clarify the docs around the naming convention for posts (#1971) + * Add missing `next` and `previous` docs for post layouts and templates (#1970) + * Add note to `Writing posts` page about how to strip html from excerpt (#1962) + * Add `jekyll-humanize` plugin to plugin list (#1998) + * Add `jekyll-font-awesome` plugin to plugin list (#1999) + * Add `sublime-jekyll` to list of Editor plugins (#2001) + * Add `vim-jekyll` to the list of Editor plugins (#2005) + * Fix non-semantic nesting of `p` tags in `news_item` layout (#2013) + * Document destination folder cleaning (#2016) + * Updated instructions for NearlyFreeSpeech.NET installation (#2015) + * Update link to rack-jekyll on "Deployment Methods" page (#2047) + * Fix typo in /docs/configuration (#2073) + * Fix count in docs for `site.static_files` (#2077) + * Update configuration docs to indicate utf-8 is the default for 2.0.0 and ASCII for 1.9.3 (#2074) + * Add info about unreleased feature to the site (#2061) + * Add whitespace to liquid example in GitHub Pages docs (#2084) + * Clarify the way Sass and CoffeeScript files are read in and output (#2067) + * Add lyche gallery tag plugin link to list of plugins (#2094) + * Add Jekyll Pages Directory plugin to list of plugins (#2096) + * Update Configuration docs page with new markdown extension (#2102) + * Add `jekyll-image-set` to the list of third-party plugins (#2105) + * Losslessly compress images (#2128) + * Update normalize.css to 3.0.0 (#2126) + * Update modernizr to v2.7.1 (#2129) + * Add `jekyll-ordinal` to list of third-party plugins (#2150) + * Add `jekyll_figure` to list of third-party plugins (#2158) + * Clarify the documentation for safe mode (#2163) + * Some HTML tidying (#2130) + * Remove modernizr and use html5shiv.js directly for IE less than v9 (#2131) + * Remove unused images (#2187) + * Use `array_to_sentence_string` filter when outputting news item categories (#2191) + * Add link to Help repo in primary navigation bar (#2177) + * Switch to using an ico file for the shortcut icon (#2193) + * Use numbers to specify font weights and only bring in font weights used (#2185) + * Add a link to the list of all tz database time zones (#1824) + * Clean-up and improve documentation `feed.xml` (#2192) + * Remove duplicate entry in list of third-party plugins (#2206) + * Reduce the whitespace in the favicon. (#2213) + * Add `jekyll-page-collections` to list of third-party plugins (#2215) + * Add a cross-reference about `post_url` (#2243) + * Add `jekyll-live-tiles` to list of third-party plugins (#2250) + * Fixed broken link to GitHub training material site source (#2257) + * Update link to help repo, now called `jekyll-help` (#2277) + * Fix capitalization of 'Jekyll' on Deployment Methods page (#2291) + * Include plugins by sonnym in list of third-party plugins (#2297) + * Add deprecated articles keeper filter to list of third-party plugins (#2300) + * Simplify and improve our CSS. (#2127) + * Use black text color for the mobile navbar (#2306) + * Use the built in date filter and `site.time` for the copyright year. (#2305) + * Update html5shiv to v3.7.2 (#2304) + * Add 2.0.0 release post (#2298) + * Add docs for custom markdown processors (#2298) + * Add docs for `where` and `group_by` Liquid filters (#2298) + * Remove notes in docs for unreleased features (#2309) + +## 1.5.1 / 2014-03-27 + +### Bug Fixes + + * Only strip the drive name if it begins the string (#2176) + +## 1.5.0 / 2014-03-24 + +### Minor Enhancements + + * Loosen `safe_yaml` dependency to `~> 1.0` (#2167) + * Bump `safe_yaml` dependency to `~> 1.0.0` (#1942) + +### Bug Fixes + + * Fix issue where filesystem traversal restriction broke Windows (#2167) + * Lock `maruku` at `0.7.0` (#2167) + +### Development Fixes + + * Lock `cucumber` at `1.3.11` (#2167) + +## 1.4.3 / 2014-01-13 + +### Bug Fixes + + * Patch show-stopping security vulnerabilities (#1944) + +## 1.4.2 / 2013-12-16 + +### Bug Fixes + + * Turn on Maruku fenced code blocks by default (#1830) + +## 1.4.1 / 2013-12-09 + +### Bug Fixes + + * Don't allow nil entries when loading posts (#1796) + +## 1.4.0 / 2013-12-07 + +### Major Enhancements + + * Add support for TOML config files (#1765) + +### Minor Enhancements + + * Sort plugins as a way to establish a load order (#1682) + * Update Maruku to 0.7.0 (#1775) + +### Bug Fixes + + * Add a space between two words in a Pagination warning message (#1769) + * Upgrade `toml` gem to `v0.1.0` to maintain compat with Ruby 1.8.7 (#1778) + +### Development Fixes + + * Remove some whitespace in the code (#1755) + * Remove some duplication in the reading of posts and drafts (#1779) + +### Site Enhancements + + * Fixed case of a word in the Jekyll v1.3.0 release post (#1762) + * Fixed the mime type for the favicon (#1772) + +## 1.3.1 / 2013-11-26 + +### Minor Enhancements + + * Add a `--prefix` option to passthrough for the importers (#1669) + * Push the paginator plugin lower in the plugin priority order so other plugins run before it (#1759) + +### Bug Fixes + + * Fix the include tag when ran in a loop (#1726) + * Fix errors when using `--watch` on 1.8.7 (#1730) + * Specify where the include is called from if an included file is missing (#1746) + +### Development Fixes + + * Extract `Site#filter_entries` into its own object (#1697) + * Enable Travis' bundle caching (#1734) + * Remove trailing whitespace in some files (#1736) + * Fix a duplicate test name (#1754) + +### Site Enhancements + + * Update link to example Rakefile to point to specific commit (#1741) + * Fix drafts docs to indicate that draft time is based on file modification time, not `Time.now` (#1695) + * Add `jekyll-monthly-archive-plugin` and `jekyll-category-archive-plugin` to list of third-party plugins (#1693) + * Add `jekyll-asset-path-plugin` to list of third-party plugins (#1670) + * Add `emoji-for-jekyll` to list of third-part plugins (#1708) + * Fix previous section link on plugins page to point to pagination page (#1707) + * Add `org-mode` converter plugin to third-party plugins (#1711) + * Point "Blog migrations" page to http://import.jekyllrb.com (#1732) + * Add docs for `post_url` when posts are in subdirectories (#1718) + * Update the docs to point to `example.com` (#1448) + +## 1.3.0 / 2013-11-04 + +### Major Enhancements + + * Add support for adding data as YAML files under a site's `_data` directory (#1003) + * Allow variables to be used with `include` tags (#1495) + * Allow using gems for plugin management (#1557) + +### Minor Enhancements + + * Decrease the specificity in the site template CSS (#1574) + * Add `encoding` configuration option (#1449) + * Provide better error handling for Jekyll's custom Liquid tags (#1514) + * If an included file causes a Liquid error, add the path to the include file that caused the error to the error message (#1596) + * If a layout causes a Liquid error, change the error message so that we know it comes from the layout (#1601) + * Update Kramdown dependency to `~> 1.2` (#1610) + * Update `safe_yaml` dependency to `~> 0.9.7` (#1602) + * Allow layouts to be in subfolders like includes (#1622) + * Switch to listen for site watching while serving (#1589) + * Add a `json` liquid filter to be used in sites (#1651) + * Point people to the migration docs when the `jekyll-import` gem is missing (#1662) + +### Bug Fixes + + * Fix up matching against source and destination when the two locations are similar (#1556) + * Fix the missing `pathname` require in certain cases (#1255) + * Use `+` instead of `Array#concat` when building `Post` attribute list (#1571) + * Print server address when launching a server (#1586) + * Downgrade to Maruku `~> 0.6.0` in order to avoid changes in rendering (#1598) + * Fix error with failing include tag when variable was file name (#1613) + * Downcase lexers before passing them to pygments (#1615) + * Capitalize the short verbose switch because it conflicts with the built-in Commander switch (#1660) + * Fix compatibility with 1.8.x (#1665) + * Fix an error with the new file watching code due to library version incompatibilities (#1687) + +### Development Fixes + + * Add coverage reporting with Coveralls (#1539) + * Refactor the Liquid `include` tag (#1490) + * Update launchy dependency to `~> 2.3` (#1608) + * Update rr dependency to `~> 1.1` (#1604) + * Update cucumber dependency to `~> 1.3` (#1607) + * Update coveralls dependency to `~> 0.7.0` (#1606) + * Update rake dependency to `~> 10.1` (#1603) + * Clean up `site.rb` comments to be more concise/uniform (#1616) + * Use the master branch for the build badge in the readme (#1636) + * Refactor Site#render (#1638) + * Remove duplication in command line options (#1637) + * Add tests for all the coderay options (#1543) + * Improve some of the Cucumber test code (#1493) + * Improve comparisons of timestamps by ignoring the seconds (#1582) + +### Site Enhancements + + * Fix params for `JekyllImport::WordPress.process` arguments (#1554) + * Add `jekyll-suggested-tweet` to list of third-party plugins (#1555) + * Link to Liquid's docs for tags and filters (#1553) + * Add note about installing Xcode on the Mac in the Installation docs (#1561) + * Simplify/generalize pagination docs (#1577) + * Add documentation for the new data sources feature (#1503) + * Add more information on how to create generators (#1590, #1592) + * Improve the instructions for mimicking GitHub Flavored Markdown (#1614) + * Add `jekyll-import` warning note of missing dependencies (#1626) + * Fix grammar in the Usage section (#1635) + * Add documentation for the use of gems as plugins (#1656) + * Document the existence of a few additional plugins (#1405) + * Document that the `date_to_string` always returns a two digit day (#1663) + * Fix navigation in the "Working with Drafts" page (#1667) + * Fix an error with the data documentation (#1691) + +## 1.2.1 / 2013-09-14 + +### Minor Enhancements + + * Print better messages for detached server. Mute output on detach. (#1518) + * Disable reverse lookup when running `jekyll serve` (#1363) + * Upgrade RedCarpet dependency to `~> 2.3.0` (#1515) + * Upgrade to Liquid `>= 2.5.2, < 2.6` (#1536) + +### Bug Fixes + + * Fix file discrepancy in gemspec (#1522) + * Force rendering of Include tag (#1525) + +### Development Fixes + + * Add a rake task to generate a new release post (#1404) + * Mute LSI output in tests (#1531) + * Update contributor documentation (#1537) + +### Site Enhancements + + * Fix a couple of validation errors on the site (#1511) + * Make navigation menus reusable (#1507) + * Fix link to History page from Release v1.2.0 notes post. + * Fix markup in History file for command line options (#1512) + * Expand 1.2 release post title to 1.2.0 (#1516) + +## 1.2.0 / 2013-09-06 + +### Major Enhancements + + * Disable automatically-generated excerpts when `excerpt_separator` is `""`. (#1386) + * Add checking for URL conflicts when running `jekyll doctor` (#1389) + +### Minor Enhancements + + * Catch and fix invalid `paginate` values (#1390) + * Remove superfluous `div.container` from the default html template for `jekyll new` (#1315) + * Add `-D` short-form switch for the drafts option (#1394) + * Update the links in the site template for Twitter and GitHub (#1400) + * Update dummy email address to example.com domain (#1408) + * Update normalize.css to v2.1.2 and minify; add rake task to update normalize.css with greater ease. (#1430) + * Add the ability to detach the server ran by `jekyll serve` from it's controlling terminal (#1443) + * Improve permalink generation for URLs with special characters (#944) + * Expose the current Jekyll version to posts and pages via a new `jekyll.version` variable (#1481) + +### Bug Fixes + + * Markdown extension matching matches only exact matches (#1382) + * Fixed NoMethodError when message passed to `Stevenson#message` is nil (#1388) + * Use binary mode when writing file (#1364) + * Fix 'undefined method `encoding` for "mailto"' errors w/ Ruby 1.8 and Kramdown > 0.14.0 (#1397) + * Do not force the permalink to be a dir if it ends on .html (#963) + * When a Liquid Exception is caught, show the full path rel. to site source (#1415) + * Properly read in the config options when serving the docs locally (#1444) + * Fixed `--layouts` option for `build` and `serve` commands (#1458) + * Remove kramdown as a runtime dependency since it's optional (#1498) + * Provide proper error handling for invalid file names in the include tag (#1494) + +### Development Fixes + + * Remove redundant argument to Jekyll::Commands::New#scaffold_post_content (#1356) + * Add new dependencies to the README (#1360) + * Fix link to contributing page in README (#1424) + * Update TomDoc in Pager#initialize to match params (#1441) + * Refactor `Site#cleanup` into `Jekyll::Site::Cleaner` class (#1429) + * Several other small minor refactorings (#1341) + * Ignore `_site` in jekyllrb.com deploy (#1480) + * Add Gem version and dependency badge to README (#1497) + +### Site Enhancements + + * Add info about new releases (#1353) + * Update plugin list with jekyll-rss plugin (#1354) + * Update the site list page with Ruby's official site (#1358) + * Add `jekyll-ditaa` to list of third-party plugins (#1370) + * Add `postfiles` to list of third-party plugins (#1373) + * For internal links, use full path including trailing `/` (#1411) + * Use curly apostrophes in the docs (#1419) + * Update the docs for Redcarpet in Jekyll (#1418) + * Add `pluralize` and `reading_time` filters to docs (#1439) + * Fix markup for the Kramdown options (#1445) + * Fix typos in the History file (#1454) + * Add trailing slash to site's post URL (#1462) + * Clarify that `--config` will take multiple files (#1474) + * Fix docs/templates.md private gist example (#1477) + * Use `site.repository` for Jekyll's GitHub URL (#1463) + * Add `jekyll-pageless-redirects` to list of third-party plugins (#1486) + * Clarify that `date_to_xmlschema` returns an ISO 8601 string (#1488) + * Add `jekyll-good-include` to list of third-party plugins (#1491) + * XML escape the blog post title in our feed (#1501) + * Add `jekyll-toc-generator` to list of third-party plugins (#1506) + +## 1.1.2 / 2013-07-25 + +### Bug Fixes + + * Require Liquid 2.5.1 (#1349) + +## 1.1.1 / 2013-07-24 + +### Minor Enhancements + + * Remove superfluous `table` selector from main.css in `jekyll new` template (#1328) + * Abort with non-zero exit codes (#1338) + +### Bug Fixes + + * Fix up the rendering of excerpts (#1339) + +### Site Enhancements + + * Add Jekyll Image Tag to the plugins list (#1306) + * Remove erroneous statement that `site.pages` are sorted alphabetically. + * Add info about the `_drafts` directory to the directory structure docs (#1320) + * Improve the layout of the plugin listing by organizing it into categories (#1310) + * Add generator-jekyllrb and grunt-jekyll to plugins page (#1330) + * Mention Kramdown as option for markdown parser on Extras page (#1318) + * Update Quick-Start page to include reminder that all requirements must be installed (#1327) + * Change filename in `include` example to an HTML file so as not to indicate that Jekyll will automatically convert them. (#1303) + * Add an RSS feed for commits to Jekyll (#1343) + +## 1.1.0 / 2013-07-14 + +### Major Enhancements + + * Add `docs` subcommand to read Jekyll's docs when offline. (#1046) + * Support passing parameters to templates in `include` tag (#1204) + * Add support for Liquid tags to post excerpts (#1302) + +### Minor Enhancements + + * Search the hierarchy of pagination path up to site root to determine template page for pagination. (#1198) + * Add the ability to generate a new Jekyll site without a template (#1171) + * Use redcarpet as the default markdown engine in newly generated sites (#1245, #1247) + * Add `redcarpet` as a runtime dependency so `jekyll build` works out-of-the-box for new sites. (#1247) + * In the generated site, remove files that will be replaced by a directory (#1118) + * Fail loudly if a user-specified configuration file doesn't exist (#1098) + * Allow for all options for Kramdown HTML Converter (#1201) + +### Bug Fixes + + * Fix pagination in subdirectories. (#1198) + * Fix an issue with directories and permalinks that have a plus sign (+) in them (#1215) + * Provide better error reporting when generating sites (#1253) + * Latest posts first in non-LSI `related_posts` (#1271) + +### Development Fixes + + * Merge the theme and layout Cucumber steps into one step (#1151) + * Restrict activesupport dependency to pre-4.0.0 to maintain compatibility with `<= 1.9.2` + * Include/exclude deprecation handling simplification (#1284) + * Convert README to Markdown. (#1267) + * Refactor Jekyll::Site (#1144) + +### Site Enhancements + + * Add "News" section for release notes, along with an RSS feed (#1093, #1285, #1286) + * Add "History" page. + * Restructured docs sections to include "Meta" section. + * Add message to "Templates" page that specifies that Python must be installed in order to use Pygments. (#1182) + * Update link to the official Maruku repo (#1175) + * Add documentation about `paginate_path` to "Templates" page in docs (#1129) + * Give the quick-start guide its own page (#1191) + * Update ProTip on Installation page in docs to point to all the info about Pygments and the 'highlight' tag. (#1196) + * Run `site/img` through ImageOptim (thanks @qrush!) (#1208) + * Added Jade Converter to `site/docs/plugins` (#1210) + * Fix location of docs pages in Contributing pages (#1214) + * Add ReadInXMinutes plugin to the plugin list (#1222) + * Remove plugins from the plugin list that have equivalents in Jekyll proper (#1223) + * Add jekyll-assets to the plugin list (#1225) + * Add jekyll-pandoc-multiple-formats to the plugin list (#1229) + * Remove dead link to "Using Git to maintain your blog" (#1227) + * Tidy up the third-party plugins listing (#1228) + * Update contributor information (#1192) + * Update URL of article about Blogger migration (#1242) + * Specify that RedCarpet is the default for new Jekyll sites on Quickstart page (#1247) + * Added `site.pages` to Variables page in docs (#1251) + * Add Youku and Tudou Embed link on Plugins page. (#1250) + * Add note that `gist` tag supports private gists. (#1248) + * Add `jekyll-timeago` to list of third-party plugins. (#1260) + * Add `jekyll-swfobject` to list of third-party plugins. (#1263) + * Add `jekyll-picture-tag` to list of third-party plugins. (#1280) + * Update the GitHub Pages documentation regarding relative URLs (#1291) + * Update the S3 deployment documentation (#1294) + * Add suggestion for Xcode CLT install to troubleshooting page in docs (#1296) + * Add 'Working with drafts' page to docs (#1289) + * Add information about time zones to the documentation for a page's date (#1304) + +## 1.0.3 / 2013-06-07 + +### Minor Enhancements + + * Add support to gist tag for private gists. (#1189) + * Fail loudly when Maruku errors out (#1190) + * Move the building of related posts into their own class (#1057) + * Removed trailing spaces in several places throughout the code (#1116) + * Add a `--force` option to `jekyll new` (#1115) + * Convert IDs in the site template to classes (#1170) + +### Bug Fixes + + * Fix typo in Stevenson constant "ERROR". (#1166) + * Rename Jekyll::Logger to Jekyll::Stevenson to fix inheritance issue (#1106) + * Exit with a non-zero exit code when dealing with a Liquid error (#1121) + * Make the `exclude` and `include` options backwards compatible with versions of Jekyll prior to 1.0 (#1114) + * Fix pagination on Windows (#1063) + * Fix the application of Pygments' Generic Output style to Go code (#1156) + +### Site Enhancements + + * Add a Pro Tip to docs about front matter variables being optional (#1147) + * Add changelog to site as History page in /docs/ (#1065) + * Add note to Upgrading page about new config options in 1.0.x (#1146) + * Documentation for `date_to_rfc822` and `uri_escape` (#1142) + * Documentation highlight boxes shouldn't show scrollbars if not necessary (#1123) + * Add link to jekyll-minibundle in the doc's plugins list (#1035) + * Quick patch for importers documentation + * Fix prefix for WordpressDotCom importer in docs (#1107) + * Add jekyll-contentblocks plugin to docs (#1068) + * Make code bits in notes look more natural, more readable (#1089) + * Fix logic for `relative_permalinks` instructions on Upgrading page (#1101) + * Add docs for post excerpt (#1072) + * Add docs for gist tag (#1072) + * Add docs indicating that Pygments does not need to be installed separately (#1099, #1119) + * Update the migrator docs to be current (#1136) + * Add the Jekyll Gallery Plugin to the plugin list (#1143) + +### Development Fixes + + * Use Jekyll.logger instead of Jekyll::Stevenson to log things (#1149) + * Fix pesky Cucumber infinite loop (#1139) + * Do not write posts with timezones in Cucumber tests (#1124) + * Use ISO formatted dates in Cucumber features (#1150) + +## 1.0.2 / 2013-05-12 + +### Major Enhancements + + * Add `jekyll doctor` command to check site for any known compatibility problems (#1081) + * Backwards-compatibilize relative permalinks (#1081) + +### Minor Enhancements + + * Add a `data-lang="<lang>"` attribute to Redcarpet code blocks (#1066) + * Deprecate old config `server_port`, match to `port` if `port` isn't set (#1084) + * Update pygments.rb version to 0.5.0 (#1061) + * Update Kramdown version to 1.0.2 (#1067) + +### Bug Fixes + + * Fix issue when categories are numbers (#1078) + * Catching that Redcarpet gem isn't installed (#1059) + +### Site Enhancements + + * Add documentation about `relative_permalinks` (#1081) + * Remove pygments-installation instructions, as pygments.rb is bundled with it (#1079) + * Move pages to be Pages for realz (#985) + * Updated links to Liquid documentation (#1073) + +## 1.0.1 / 2013-05-08 + +### Minor Enhancements + + * Do not force use of `toc_token` when using `generate_tok` in RDiscount (#1048) + * Add newer `language-` class name prefix to code blocks (#1037) + * Commander error message now preferred over process abort with incorrect args (#1040) + +### Bug Fixes + + * Make Redcarpet respect the pygments configuration option (#1053) + * Fix the index build with LSI (#1045) + * Don't print deprecation warning when no arguments are specified. (#1041) + * Add missing `</div>` to site template used by `new` subcommand, fixed typos in code (#1032) + +### Site Enhancements + + * Changed https to http in the GitHub Pages link (#1051) + * Remove CSS cruft, fix typos, fix HTML errors (#1028) + * Removing manual install of Pip and Distribute (#1025) + * Updated URL for Markdown references plugin (#1022) + +### Development Fixes + + * Markdownify history file (#1027) + * Update links on README to point to new jekyllrb.com (#1018) + +## 1.0.0 / 2013-05-06 + +### Major Enhancements + + * Add `jekyll new` subcommand: generate a Jekyll scaffold (#764) + * Refactored Jekyll commands into subcommands: build, serve, and migrate. (#690) + * Removed importers/migrators from main project, migrated to jekyll-import sub-gem (#793) + * Added ability to render drafts in `_drafts` folder via command line (#833) + * Add ordinal date permalink style (/:categories/:year/:y_day/:title.html) (#928) + +### Minor Enhancements + + * Site template HTML5-ified (#964) + * Use post's directory path when matching for the `post_url` tag (#998) + * Loosen dependency on Pygments so it's only required when it's needed (#1015) + * Parse strings into Time objects for date-related Liquid filters (#1014) + * Tell the user if there is no subcommand specified (#1008) + * Freak out if the destination of `jekyll new` exists and is non-empty (#981) + * Add `timezone` configuration option for compilation (#957) + * Add deprecation messages for pre-1.0 CLI options (#959) + * Refactor and colorize logging (#959) + * Refactor Markdown parsing (#955) + * Added application/vnd.apple.pkpass to mime.types served by WEBrick (#907) + * Move template site to default markdown renderer (#961) + * Expose new attribute to Liquid via `page`: `page.path` (#951) + * Accept multiple config files from command line (#945) + * Add page variable to liquid custom tags and blocks (#413) + * Add `paginator.previous_page_path` and `paginator.next_page_path` (#942) + * Backwards compatibility for 'auto' (#821, #934) + * Added date_to_rfc822 used on RSS feeds (#892) + * Upgrade version of pygments.rb to 0.4.2 (#927) + * Added short month (e.g. "Sep") to permalink style options for posts (#890) + * Expose site.baseurl to Liquid templates (#869) + * Adds excerpt attribute to posts which contains first paragraph of content (#837) + * Accept custom configuration file via CLI (#863) + * Load in GitHub Pages MIME Types on `jekyll serve` (#847, #871) + * Improve debuggability of error message for a malformed highlight tag (#785) + * Allow symlinked files in unsafe mode (#824) + * Add 'gist' Liquid tag to core (#822, #861) + * New format of Jekyll output (#795) + * Reinstate `--limit_posts` and `--future` switches (#788) + * Remove ambiguity from command descriptions (#815) + * Fix SafeYAML Warnings (#807) + * Relaxed Kramdown version to 0.14 (#808) + * Aliased `jekyll server` to `jekyll serve`. (#792) + * Updated gem versions for Kramdown, Rake, Shoulda, Cucumber, and RedCarpet. (#744) + * Refactored Jekyll subcommands into Jekyll::Commands submodule, which now contains them (#768) + * Rescue from import errors in Wordpress.com migrator (#671) + * Massively accelerate LSI performance (#664) + * Truncate post slugs when importing from Tumblr (#496) + * Add glob support to include, exclude option (#743) + * Layout of Page or Post defaults to 'page' or 'post', respectively (#580) REPEALED by (#977) + * "Keep files" feature (#685) + * Output full path & name for files that don't parse (#745) + * Add source and destination directory protection (#535) + * Better YAML error message (#718) + * Bug Fixes + * Paginate in subdirectories properly (#1016) + * Ensure post and page URLs have a leading slash (#992) + * Catch all exceptions, not just StandardError descendents (#1007) + * Bullet-proof `limit_posts` option (#1004) + * Read in YAML as UTF-8 to accept non-ASCII chars (#836) + * Fix the CLI option `--plugins` to actually accept dirs and files (#993) + * Allow 'excerpt' in front matter to override the extracted excerpt (#946) + * Fix cascade problem with site.baseurl, site.port and site.host. (#935) + * Filter out directories with valid post names (#875) + * Fix symlinked static files not being correctly built in unsafe mode (#909) + * Fix integration with directory_watcher 1.4.x (#916) + * Accepting strings as arguments to jekyll-import command (#910) + * Force usage of older directory_watcher gem as 1.5 is broken (#883) + * Ensure all Post categories are downcase (#842, #872) + * Force encoding of the rdiscount TOC to UTF8 to avoid conversion errors (#555) + * Patch for multibyte URI problem with `jekyll serve` (#723) + * Order plugin execution by priority (#864) + * Fixed Page#dir and Page#url for edge cases (#536) + * Fix broken `post_url` with posts with a time in their front matter (#831) + * Look for plugins under the source directory (#654) + * Tumblr Migrator: finds `_posts` dir correctly, fixes truncation of long post names (#775) + * Force Categories to be Strings (#767) + * Safe YAML plugin to prevent vulnerability (#777) + * Add SVG support to Jekyll/WEBrick. (#407, #406) + * Prevent custom destination from causing continuous regen on watch (#528, #820, #862) + +### Site Enhancements + + * Responsify (#860) + * Fix spelling, punctuation and phrasal errors (#989) + * Update quickstart instructions with `new` command (#966) + * Add docs for page.excerpt (#956) + * Add docs for page.path (#951) + * Clean up site docs to prepare for 1.0 release (#918) + * Bring site into master branch with better preview/deploy (#709) + * Redesigned site (#583) + +### Development Fixes + + * Exclude Cucumber 1.2.4, which causes tests to fail in 1.9.2 (#938) + * Added "features:html" rake task for debugging purposes, cleaned up Cucumber profiles (#832) + * Explicitly require HTTPS rubygems source in Gemfile (#826) + * Changed Ruby version for development to 1.9.3-p374 from p362 (#801) + * Including a link to the GitHub Ruby style guide in CONTRIBUTING.md (#806) + * Added script/bootstrap (#776) + * Running Simplecov under 2 conditions: ENV(COVERAGE)=true and with Ruby version of greater than 1.9 (#771) + * Switch to Simplecov for coverage report (#765) + +## 0.12.1 / 2013-02-19 + +### Minor Enhancements + + * Update Kramdown version to 0.14.1 (#744) + * Test Enhancements + * Update Rake version to 10.0.3 (#744) + * Update Shoulda version to 3.3.2 (#744) + * Update Redcarpet version to 2.2.2 (#744) + +## 0.12.0 / 2012-12-22 + +### Minor Enhancements + + * Add ability to explicitly specify included files (#261) + * Add `--default-mimetype` option (#279) + * Allow setting of RedCloth options (#284) + * Add `post_url` Liquid tag for internal post linking (#369) + * Allow multiple plugin dirs to be specified (#438) + * Inline TOC token support for RDiscount (#333) + * Add the option to specify the paginated url format (#342) + * Swap out albino for pygments.rb (#569) + * Support Redcarpet 2 and fenced code blocks (#619) + * Better reporting of Liquid errors (#624) + * Bug Fixes + * Allow some special characters in highlight names + * URL escape category names in URL generation (#360) + * Fix error with `limit_posts` (#442) + * Properly select dotfile during directory scan (#363, #431, #377) + * Allow setting of Kramdown `smart_quotes` (#482) + * Ensure front matter is at start of file (#562) + +## 0.11.2 / 2011-12-27 + + * Bug Fixes + * Fix gemspec + +## 0.11.1 / 2011-12-27 + + * Bug Fixes + * Fix extra blank line in highlight blocks (#409) + * Update dependencies + +## 0.11.0 / 2011-07-10 + +### Major Enhancements + + * Add command line importer functionality (#253) + * Add Redcarpet Markdown support (#318) + * Make markdown/textile extensions configurable (#312) + * Add `markdownify` filter + +### Minor Enhancements + + * Switch to Albino gem + * Bundler support + * Use English library to avoid hoops (#292) + * Add Posterous importer (#254) + * Fixes for Wordpress importer (#274, #252, #271) + * Better error message for invalid post date (#291) + * Print formatted fatal exceptions to stdout on build failure + * Add Tumblr importer (#323) + * Add Enki importer (#320) + * Bug Fixes + * Secure additional path exploits + +## 0.10.0 / 2010-12-16 + + * Bug Fixes + * Add `--no-server` option. + +## 0.9.0 / 2010-12-15 + +### Minor Enhancements + + * Use OptionParser's `[no-]` functionality for better boolean parsing. + * Add Drupal migrator (#245) + * Complain about YAML and Liquid errors (#249) + * Remove orphaned files during regeneration (#247) + * Add Marley migrator (#28) + +## 0.8.0 / 2010-11-22 + +### Minor Enhancements + + * Add wordpress.com importer (#207) + * Add `--limit-posts` cli option (#212) + * Add `uri_escape` filter (#234) + * Add `--base-url` cli option (#235) + * Improve MT migrator (#238) + * Add kramdown support (#239) + * Bug Fixes + * Fixed filename basename generation (#208) + * Set mode to UTF8 on Sequel connections (#237) + * Prevent `_includes` dir from being a symlink + +## 0.7.0 / 2010-08-24 + +### Minor Enhancements + + * Add support for rdiscount extensions (#173) + * Bug Fixes + * Highlight should not be able to render local files + * The site configuration may not always provide a 'time' setting (#184) + +## 0.6.2 / 2010-06-25 + + * Bug Fixes + * Fix Rakefile 'release' task (tag pushing was missing origin) + * Ensure that RedCloth is loaded when textilize filter is used (#183) + * Expand source, destination, and plugin paths (#180) + * Fix `page.url` to include full relative path (#181) + +## 0.6.1 / 2010-06-24 + + * Bug Fixes + * Fix Markdown Pygments prefix and suffix (#178) + +## 0.6.0 / 2010-06-23 + +### Major Enhancements + + * Proper plugin system (#19, #100) + * Add safe mode so unsafe converters/generators can be added + * Maruku is now the only processor dependency installed by default. Other processors will be lazy-loaded when necessary (and prompt the user to install them when necessary) (#57) + +### Minor Enhancements + + * Inclusion/exclusion of future dated posts (#59) + * Generation for a specific time (#59) + * Allocate `site.time` on render not per site_payload invocation (#59) + * Pages now present in the site payload and can be used through the `site.pages` and `site.html_pages` variables + * Generate phase added to site#process and pagination is now a generator + * Switch to RakeGem for build/test process + * Only regenerate static files when they have changed (#142) + * Allow arbitrary options to Pygments (#31) + * Allow URL to be set via command line option (#147) + * Bug Fixes + * Render highlighted code for non markdown/textile pages (#116) + * Fix highlighting on Ruby 1.9 (#65) + * Fix extension munging when pretty permalinks are enabled (#64) + * Stop sorting categories (#33) + * Preserve generated attributes over front matter (#119) + * Fix source directory binding using `Dir.pwd` (#75) + +## 0.5.7 / 2010-01-12 + +### Minor Enhancements + + * Allow overriding of post date in the front matter (#62, #38) + * Bug Fixes + * Categories isn't always an array (#73) + * Empty tags causes error in read_posts (#84) + * Fix pagination to adhere to read/render/write paradigm + * Test Enhancement + * Cucumber features no longer use site.posts.first where a better alternative is available + +## 0.5.6 / 2010-01-08 + + * Bug Fixes + * Require redcloth >= 4.2.1 in tests (#92) + * Don't break on triple dashes in front matter (#93) + +### Minor Enhancements + + * Allow .mkd as markdown extension + * Use $stdout/err instead of constants (#99) + * Properly wrap code blocks (#91) + * Add javascript mime type for webrick (#98) + +## 0.5.5 / 2010-01-08 + + * Bug Fixes + * Fix pagination % 0 bug (#78) + * Ensure all posts are processed first (#71) + * After this point I will no longer be giving credit in the history; that is what the commit log is for. + +## 0.5.4 / 2009-08-23 + + * Bug Fixes + * Do not allow symlinks (security vulnerability) + +## 0.5.3 / 2009-07-14 + + * Bug Fixes + * Solving the permalink bug where non-html files wouldn't work (@jeffrydegrande) + +## 0.5.2 / 2009-06-24 + + * Enhancements + * Added --paginate option to the executable along with a paginator object for the payload (@calavera) + * Upgraded RedCloth to 4.2.1, which makes `<notextile>` tags work once again. + * Configuration options set in config.yml are now available through the site payload (@vilcans) + * Posts can now have an empty front matter or none at all (@ bahuvrihi) + * Bug Fixes + * Fixing Ruby 1.9 issue that requires `#to_s` on the err object (@Chrononaut) + * Fixes for pagination and ordering posts on the same day (@ujh) + * Made pages respect permalinks style and permalinks in yml front matter (@eugenebolshakov) + * Index.html file should always have index.html permalink (@eugenebolshakov) + * Added trailing slash to pretty permalink style so Apache is happy (@eugenebolshakov) + * Bad markdown processor in config fails sooner and with better message (@ gcnovus) + * Allow CRLFs in front matter (@juretta) + * Added Date#xmlschema for Ruby versions < 1.9 + +## 0.5.1 / 2009-05-06 + +### Major Enhancements + + * Next/previous posts in site payload (@pantulis, @tomo) + * Permalink templating system + * Moved most of the README out to the GitHub wiki + * Exclude option in configuration so specified files won't be brought over with generated site (@duritong) + * Bug Fixes + * Making sure config.yaml references are all gone, using only config.yml + * Fixed syntax highlighting breaking for UTF-8 code (@henrik) + * Worked around RDiscount bug that prevents Markdown from getting parsed after highlight (@henrik) + * CGI escaped post titles (@Chrononaut) + +## 0.5.0 / 2009-04-07 + +### Minor Enhancements + + * Ability to set post categories via YAML (@qrush) + * Ability to set prevent a post from publishing via YAML (@qrush) + * Add textilize filter (@willcodeforfoo) + * Add 'pretty' permalink style for wordpress-like urls (@dysinger) + * Made it possible to enter categories from YAML as an array (@Chrononaut) + * Ignore Emacs autosave files (@Chrononaut) + * Bug Fixes + * Use block syntax of popen4 to ensure that subprocesses are properly disposed (@jqr) + * Close open4 streams to prevent zombies (@rtomayko) + * Only query required fields from the WP Database (@ariejan) + * Prevent `_posts` from being copied to the destination directory (@bdimcheff) + * Refactors + * Factored the filtering code into a method (@Chrononaut) + * Fix tests and convert to Shoulda (@qrush, @technicalpickles) + * Add Cucumber acceptance test suite (@qrush, @technicalpickles) + +## 0.4.1 + +### Minor Enhancements + + * Changed date format on wordpress converter (zeropadding) (@dysinger) + * Bug Fixes + * Add Jekyll binary as executable to gemspec (@dysinger) + +## 0.4.0 / 2009-02-03 + +### Major Enhancements + + * Switch to Jeweler for packaging tasks + +### Minor Enhancements + + * Type importer (@codeslinger) + * `site.topics` accessor (@baz) + * Add `array_to_sentence_string` filter (@mchung) + * Add a converter for textpattern (@PerfectlyNormal) + * Add a working Mephisto / MySQL converter (@ivey) + * Allowing .htaccess files to be copied over into the generated site (@briandoll) + * Add option to not put file date in permalink URL (@mreid) + * Add line number capabilities to highlight blocks (@jcon) + * Bug Fixes + * Fix permalink behavior (@cavalle) + * Fixed an issue with pygments, markdown, and newlines (@zpinter) + * Ampersands need to be escaped (@pufuwozu, @ap) + * Test and fix the site.categories hash (@zzot) + * Fix site payload available to files (@matrix9180) + +## 0.3.0 / 2008-12-24 + +### Major Enhancements + + * Added `--server` option to start a simple WEBrick server on destination directory (@johnreilly and @mchung) + +### Minor Enhancements + + * Added post categories based on directories containing `_posts` (@mreid) + * Added post topics based on directories underneath `_posts` + * Added new date filter that shows the full month name (@mreid) + * Merge Post's front matter into its to_liquid payload (@remi) + * Restrict includes to regular files underneath `_includes` + * Bug Fixes + * Change YAML delimiter matcher so as to not chew up 2nd level markdown headers (@mreid) + * Fix bug that meant page data (such as the date) was not available in templates (@mreid) + * Properly reject directories in `_layouts` + +## 0.2.1 / 2008-12-15 + + * Major Changes + * Use Maruku (pure Ruby) for Markdown by default (@mreid) + * Allow use of RDiscount with `--rdiscount` flag + +### Minor Enhancements + + * Don't load directory_watcher unless it's needed (@pjhyett) + +## 0.2.0 / 2008-12-14 + + * Major Changes + * related_posts is now found in `site.related_posts` + +## 0.1.6 / 2008-12-13 + + * Major Features + * Include files in `_includes` with `{% include x.textile %}` + +## 0.1.5 / 2008-12-12 + +### Major Enhancements + + * Code highlighting with Pygments if `--pygments` is specified + * Disable true LSI by default, enable with `--lsi` + +### Minor Enhancements + + * Output informative message if RDiscount is not available (@JackDanger) + * Bug Fixes + * Prevent Jekyll from picking up the output directory as a source (@JackDanger) + * Skip `related_posts` when there is only one post (@JackDanger) + +## 0.1.4 / 2008-12-08 + + * Bug Fixes + * DATA does not work properly with rubygems + +## 0.1.3 / 2008-12-06 + + * Major Features + * Markdown support (@vanpelt) + * Mephisto and CSV converters (@vanpelt) + * Code hilighting (@vanpelt) + * Autobuild + * Bug Fixes + * Accept both `\r\n` and `\n` in YAML header (@vanpelt) + +## 0.1.2 / 2008-11-22 + + * Major Features + * Add a real "related posts" implementation using Classifier + * Command Line Changes + * Allow cli to be called with 0, 1, or 2 args intuiting dir paths if they are omitted + +## 0.1.1 / 2008-11-22 + + * Minor Additions + * Posts now support introspectional data e.g. `{{ page.url }}` + +## 0.1.0 / 2008-11-05 + + * First release + * Converts posts written in Textile + * Converts regular site pages + * Simple copy of binary files + +## 0.0.0 / 2008-10-19 + + * Birthday! diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..91e3192 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2008-present Tom Preston-Werner and Jekyll contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.markdown b/README.markdown new file mode 100644 index 0000000..ad55a2f --- /dev/null +++ b/README.markdown @@ -0,0 +1,83 @@ +# [Jekyll](https://jekyllrb.com/) + +[][ruby-gems] +[][ci-workflow] +[][appveyor] +[](#backers) +[](#sponsors) + +[ruby-gems]: https://rubygems.org/gems/jekyll +[ci-workflow]: https://github.com/jekyll/jekyll/actions?query=workflow%3A%22Continuous+Integration%22+branch%3Amaster +[appveyor]: https://ci.appveyor.com/project/jekyll/jekyll/branch/master + +Jekyll is a simple, blog-aware, static site generator perfect for personal, project, or organization sites. Think of it like a file-based CMS, without all the complexity. Jekyll takes your content, renders Markdown and Liquid templates, and spits out a complete, static website ready to be served by Apache, Nginx or another web server. Jekyll is the engine behind [GitHub Pages](https://pages.github.com), which you can use to host sites right from your GitHub repositories. + +## Philosophy + +Jekyll does what you tell it to do — no more, no less. It doesn't try to outsmart users by making bold assumptions, nor does it burden them with needless complexity and configuration. Put simply, Jekyll gets out of your way and allows you to concentrate on what truly matters: your content. + +See: [https://jekyllrb.com/philosophy](https://jekyllrb.com/philosophy) + +## Getting Started + +* [Install](https://jekyllrb.com/docs/installation/) the gem +* Read up about its [Usage](https://jekyllrb.com/docs/usage/) and [Configuration](https://jekyllrb.com/docs/configuration/) +* Take a gander at some existing [Sites](https://github.com/jekyll/jekyll/wiki/sites) +* [Fork](https://github.com/jekyll/jekyll/fork) and [Contribute](https://jekyllrb.com/docs/contributing/) your own modifications +* Have questions? Check out our official forum community [Jekyll Talk](https://talk.jekyllrb.com/) and [`#jekyll` Channel on Libera IRC](https://libera.chat) + +## Diving In + +* [Migrate](https://import.jekyllrb.com/docs/home/) from your previous system +* Learn how [Front Matter](https://jekyllrb.com/docs/front-matter/) works +* Put information on your site with [Variables](https://jekyllrb.com/docs/variables/) +* Customize the [Permalinks](https://jekyllrb.com/docs/permalinks/) your posts are generated with +* Use the built-in [Liquid Extensions](https://jekyllrb.com/docs/templates/) to make your life easier +* Use custom [Plugins](https://jekyllrb.com/docs/plugins/) to generate content specific to your site +* Watch [video tutorials from Giraffe Academy](https://jekyllrb.com/tutorials/video-walkthroughs/) + +## Need help? + +If you don't find the answer to your problem in our [docs](https://jekyllrb.com/docs/), or in the [troubleshooting section](https://jekyllrb.com/docs/troubleshooting/), ask the [community](https://jekyllrb.com/docs/community/) for help. + +## Code of Conduct + +In order to have a more open and welcoming community, Jekyll adheres to a +[code of conduct](https://jekyllrb.com/docs/conduct/) adapted from the Ruby on Rails code of +conduct. + +Please adhere to this code of conduct in any interactions you have in the +Jekyll community. It is strictly enforced on all official Jekyll +repositories, websites, and resources. If you encounter someone violating +these terms, please let one of our [core team members](https://jekyllrb.com/team/#core-team) know and we will address it as soon as possible. + +## Credits + +### Sponsors + +Support this project by becoming a sponsor. Your logo will show up in this README with a link to your website. [Become a sponsor!](https://opencollective.com/jekyll#sponsor) +[](https://opencollective.com/jekyll/sponsor/0/website) +[](https://opencollective.com/jekyll/sponsor/1/website) +[](https://opencollective.com/jekyll/sponsor/2/website) +[](https://opencollective.com/jekyll/sponsor/3/website) +[](https://opencollective.com/jekyll/sponsor/4/website) +[](https://opencollective.com/jekyll/sponsor/5/website) +[](https://opencollective.com/jekyll/sponsor/6/website) +[](https://opencollective.com/jekyll/sponsor/7/website) +[](https://opencollective.com/jekyll/sponsor/8/website) +[](https://opencollective.com/jekyll/sponsor/9/website) + +### Contributors + +This project exists thanks to all the people who contribute. +[](../../graphs/contributors) + +### Backers + +Thank you to all our backers! 🙏 [Become a backer](https://opencollective.com/jekyll#backer) + +[](https://opencollective.com/jekyll#backers) + +## License + +See the [LICENSE](https://github.com/jekyll/jekyll/blob/master/LICENSE) file. diff --git a/Rakefile b/Rakefile new file mode 100644 index 0000000..41b707c --- /dev/null +++ b/Rakefile @@ -0,0 +1,163 @@ +# frozen_string_literal: true + +require "rubygems" +require "rake" +require "rdoc" +require "date" +require "yaml" + +$LOAD_PATH.unshift File.expand_path("lib", __dir__) +require "jekyll/version" + +Dir.glob("rake/**.rake").each { |f| import f } + +############################################################################# +# +# Helper functions +# +############################################################################# + +def name + "jekyll" +end + +def version + Jekyll::VERSION +end + +def docs_name + "#{name}-docs" +end + +def docs_folder + "docs" +end + +def gemspec_file + "#{name}.gemspec" +end + +def gem_file + "#{name}-#{Gem::Version.new(version)}.gem" +end + +def normalize_bullets(markdown) + markdown.gsub(%r!\n\s{2}\*{1}!, "\n-") +end + +def linkify_prs(markdown) + markdown.gsub(%r!(?<\!&)#(\d+)!) do |word| + "[#{word}]({{ site.repository }}/issues/#{word.delete("#")})" + end +end + +def linkify(markdown) + linkify_prs(markdown) +end + +def liquid_escape(markdown) + markdown.gsub(%r!(`{[{%].+[}%]}`)!, "{% raw %}\\1{% endraw %}") +end + +def custom_release_header_anchors(markdown) + header_regexp = %r!^(\d{1,2})\.(\d{1,2})\.(\d{1,2}) \/ \d{4}-\d{2}-\d{2}! + section_regexp = %r!^### \w+ \w+$! + markdown.split(%r!^##\s!).map do |release_notes| + _, major, minor, patch = *release_notes.match(header_regexp) + release_notes + .gsub(header_regexp, "\\0\n{: #v\\1-\\2-\\3}") + .gsub(section_regexp) { |section| "#{section}\n{: ##{slugify(section)}-v#{major}-#{minor}-#{patch}}" } + end.join("\n## ") +end + +def slugify(header) + header.delete("#").strip.downcase.gsub(%r!\s+!, "-") +end + +def remove_head_from_history(markdown) + index = markdown =~ %r!^##\s+\d+\.\d+\.\d+! + markdown[index..-1] +end + +def converted_history(markdown) + remove_head_from_history( + custom_release_header_anchors( + liquid_escape( + linkify( + normalize_bullets(markdown) + ) + ) + ) + ) +end + +def siteify_file(file, overrides_front_matter = {}) + abort "You seem to have misplaced your #{file} file. I can haz?" unless File.exist?(file) + title = begin + File.read(file).match(%r!\A# (.*)$!)[1] + rescue NoMethodError + File.basename(file, ".*").downcase.capitalize + end + slug = File.basename(file, ".markdown").downcase + front_matter = { + "title" => title, + "permalink" => "/docs/#{slug}/", + "note" => "This file is autogenerated. Edit /#{file} instead.", + }.merge(overrides_front_matter) + contents = "#{front_matter.to_yaml}---\n\n#{content_for(file)}" + File.write("#{docs_folder}/_docs/#{slug}.md", contents) +end + +def content_for(file) + contents = File.read(file) + case file + when "History.markdown" + converted_history(contents) + else + contents.gsub(%r!\A# .*\n\n?!, "") + end +end + +############################################################################# +# +# Standard tasks +# +############################################################################# + +multitask :default => [:test, :features] + +task :spec => :test +require "rake/testtask" +Rake::TestTask.new(:test) do |test| + test.libs << "lib" << "test" + test.pattern = "test/**/test_*.rb" + test.verbose = true +end + +require "rdoc/task" +Rake::RDocTask.new do |rdoc| + rdoc.rdoc_dir = "rdoc" + rdoc.title = "#{name} #{version}" + rdoc.rdoc_files.include("README*") + rdoc.rdoc_files.include("lib/**/*.rb") +end + +begin + require "cucumber/rake/task" + Cucumber::Rake::Task.new(:features) do |t| + t.profile = "travis" + end + Cucumber::Rake::Task.new(:"features:html", "Run Cucumber features and produce HTML output") do |t| + t.profile = "html_report" + end +rescue LoadError + desc "Cucumber rake task not available" + task :features do + abort "Cucumber rake task is not available. Be sure to install cucumber as a gem or plugin" + end +end + +desc "Open an irb session preloaded with this library" +task :console do + sh "irb -r ./lib/#{name}.rb" +end diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 0000000..af31dd5 --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,47 @@ +version: "{build}" + +clone_depth: 5 + +branches: + only: + - master + - themes + - /.*-stable/ + +build: off + +environment: + BUNDLE_WITHOUT: "benchmark:development" + matrix: + - RUBY_FOLDER_VER: "26" + TZINFO_VERSION: "~> 1.2" + TEST_SUITE: "test" + - RUBY_FOLDER_VER: "26" + TZINFO_VERSION: "~> 2.0" + TEST_SUITE: "test" + - RUBY_FOLDER_VER: "26" + TEST_SUITE: "default-site" + - RUBY_FOLDER_VER: "26" + TEST_SUITE: "profile-docs" + - RUBY_FOLDER_VER: "26" + TEST_SUITE: "memprof" + - RUBY_FOLDER_VER: "26" + TZINFO_VERSION: "~> 1.2" + TEST_SUITE: "cucumber" + - RUBY_FOLDER_VER: "26" + TZINFO_VERSION: "~> 2.0" + TEST_SUITE: "cucumber" + +install: + - SET PATH=C:\Ruby%RUBY_FOLDER_VER%-x64\bin;%PATH% + - bundle install --retry 5 --jobs=%NUMBER_OF_PROCESSORS% --clean --path vendor\bundle + +test_script: + - ruby --version + - gem --version + - bundler --version + - bash ./script/cibuild + +cache: + # If one of the files after the right arrow changes, cache will be invalidated + - 'vendor\bundle -> appveyor.yml,Gemfile,jekyll.gemspec' diff --git a/benchmark/capture-assign.rb b/benchmark/capture-assign.rb new file mode 100644 index 0000000..7a127ad --- /dev/null +++ b/benchmark/capture-assign.rb @@ -0,0 +1,21 @@ +#!/usr/bin/env ruby +require "liquid" +require "benchmark/ips" + +puts "Ruby #{RUBY_VERSION}-p#{RUBY_PATCHLEVEL}" +puts "Liquid #{Liquid::VERSION}" + +template1 = '{% capture foobar %}foo{{ bar }}{% endcapture %}{{ foo }}{{ foobar }}' +template2 = '{% assign foobar = "foo" | append: bar %}{{ foobar }}' + +def render(template) + Liquid::Template.parse(template).render("bar" => "42") +end + +puts render(template1) +puts render(template2) + +Benchmark.ips do |x| + x.report('capture') { render(template1) } + x.report('assign') { render(template2) } +end diff --git a/benchmark/conditional_liquid.rb b/benchmark/conditional_liquid.rb new file mode 100644 index 0000000..49d0827 --- /dev/null +++ b/benchmark/conditional_liquid.rb @@ -0,0 +1,101 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +require "liquid" +require "benchmark/ips" + +# Test if processing content string without any Liquid constructs, via Liquid, +# is slower than checking whether constructs exist ( using `String#include?` ) +# and return-ing the "plaintext" content string as is.. +# +# Ref: https://github.com/jekyll/jekyll/pull/6735 + +# Sample contents +WITHOUT_LIQUID = <<-TEXT.freeze +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce auctor libero at +pharetra tempus. Etiam bibendum magna et metus fermentum, eu cursus lorem +mattis. Curabitur vel dui et lacus rutrum suscipit et eget neque. + +Nullam luctus fermentum est id blandit. Phasellus consectetur ullamcorper +ligula, at finibus eros laoreet id. Etiam sit amet est in libero efficitur +tristique. Ut nec magna augue. Quisque ut fringilla lacus, ac dictum enim. +Aliquam vel ornare mauris. Suspendisse ornare diam tempor nulla facilisis +aliquet. Sed ultrices placerat ultricies. +TEXT + +WITH_LIQUID = <<-LIQUID.freeze +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce auctor libero at +pharetra tempus. {{ author }} et metus fermentum, eu cursus lorem +mattis. Curabitur vel dui et lacus rutrum suscipit et eget neque. + +Nullam luctus fermentum est id blandit. Phasellus consectetur ullamcorper +ligula, {% if author == "Jane Doe" %} at finibus eros laoreet id. {% else %} +Etiam sit amet est in libero efficitur.{% endif %} +tristique. Ut nec magna augue. Quisque ut fringilla lacus, ac dictum enim. +Aliquam vel ornare mauris. Suspendisse ornare diam tempor nulla facilisis +aliquet. Sed ultrices placerat ultricies. +LIQUID + +WITH_JUST_LIQUID_VAR = <<-LIQUID.freeze +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce auctor libero at +pharetra tempus. et metus fermentum, eu cursus lorem, ac dictum enim. +mattis. Curabitur vel dui et lacus rutrum suscipit et {{ title }} neque. + +Nullam luctus fermentum est id blandit. Phasellus consectetur ullamcorper +ligula, at finibus eros laoreet id. Etiam sit amet est in libero efficitur. +tristique. Ut nec magna augue. {{ author }} Quisque ut fringilla lacus +Aliquam vel ornare mauris. Suspendisse ornare diam tempor nulla facilisis +aliquet. Sed ultrices placerat ultricies. +LIQUID + +SUITE = { + :"plain text" => WITHOUT_LIQUID, + :"tags n vars" => WITH_LIQUID, + :"just vars" => WITH_JUST_LIQUID_VAR, +}.freeze + +# Mimic how Jekyll's LiquidRenderer would process a non-static file, with +# some dummy payload +def always_liquid(content) + Liquid::Template.error_mode = :warn + Liquid::Template.parse(content, :line_numbers => true).render( + "author" => "John Doe", + "title" => "FooBar" + ) +end + +# Mimic how the proposed change would first execute a couple of checks and +# proceed to process with Liquid if necessary +def conditional_liquid(content) + return content if content.nil? || content.empty? + return content unless content.include?("{%") || content.include?("{{") + always_liquid(content) +end + +# Test https://github.com/jekyll/jekyll/pull/6735#discussion_r165499868 +# ------------------------------------------------------------------------ +def check_with_regex(content) + !content.to_s.match?(%r!{[{%]!) +end + +def check_with_builtin(content) + content.include?("{%") || content.include?("{{") +end + +SUITE.each do |key, text| + Benchmark.ips do |x| + x.report("regex-check - #{key}") { check_with_regex(text) } + x.report("builtin-check - #{key}") { check_with_builtin(text) } + x.compare! + end +end +# ------------------------------------------------------------------------ + +# Let's roll! +SUITE.each do |key, text| + Benchmark.ips do |x| + x.report("always thru liquid - #{key}") { always_liquid(text) } + x.report("conditional liquid - #{key}") { conditional_liquid(text) } + x.compare! + end +end diff --git a/benchmark/end-with-vs-regexp b/benchmark/end-with-vs-regexp new file mode 100644 index 0000000..2ba5a30 --- /dev/null +++ b/benchmark/end-with-vs-regexp @@ -0,0 +1,16 @@ +#!/usr/bin/env ruby +require 'benchmark/ips' + +Benchmark.ips do |x| + path_without_ending_slash = '/some/very/very/long/path/to/a/file/i/like' + x.report('no slash regexp') { path_without_ending_slash =~ /\/$/ } + x.report('no slash end_with?') { path_without_ending_slash.end_with?("/") } + x.report('no slash [-1, 1]') { path_without_ending_slash[-1, 1] == "/" } +end + +Benchmark.ips do |x| + path_with_ending_slash = '/some/very/very/long/path/to/a/file/i/like/' + x.report('slash regexp') { path_with_ending_slash =~ /\/$/ } + x.report('slash end_with?') { path_with_ending_slash.end_with?("/") } + x.report('slash [-1, 1]') { path_with_ending_slash[-1, 1] == "/" } +end diff --git a/benchmark/file-dir-ensure-trailing-slash b/benchmark/file-dir-ensure-trailing-slash new file mode 100644 index 0000000..aecfc03 --- /dev/null +++ b/benchmark/file-dir-ensure-trailing-slash @@ -0,0 +1,54 @@ +#!/usr/bin/env ruby +require 'benchmark/ips' + +# For this pull request, which changes Page#dir +# https://github.com/jekyll/jekyll/pull/4403 + +FORWARD_SLASH = '/'.freeze + +def pre_pr(url) + url[-1, 1] == FORWARD_SLASH ? url : File.dirname(url) +end + +def pr(url) + if url.end_with?(FORWARD_SLASH) + url + else + url_dir = File.dirname(url) + url_dir.end_with?(FORWARD_SLASH) ? url_dir : "#{url_dir}/" + end +end + +def envygeeks(url) + return url if url.end_with?(FORWARD_SLASH) || url == FORWARD_SLASH + + url = File.dirname(url) + url == FORWARD_SLASH ? url : "#{url}/" +end + +# Just a slash +Benchmark.ips do |x| + path = '/' + x.report("pre_pr:#{path}") { pre_pr(path) } + x.report("pr:#{path}") { pr(path) } + x.report("envygeeks:#{path}") { pr(path) } + x.compare! +end + +# No trailing slash +Benchmark.ips do |x| + path = '/some/very/very/long/path/to/a/file/i/like' + x.report("pre_pr:#{path}") { pre_pr(path) } + x.report("pr:#{path}") { pr(path) } + x.report("envygeeks:#{path}") { pr(path) } + x.compare! +end + +# No trailing slash +Benchmark.ips do |x| + path = '/some/very/very/long/path/to/a/file/i/like/' + x.report("pre_pr:#{path}") { pre_pr(path) } + x.report("pr:#{path}") { pr(path) } + x.report("envygeeks:#{path}") { pr(path) } + x.compare! +end diff --git a/benchmark/find-filter-vs-where-first-filters.rb b/benchmark/find-filter-vs-where-first-filters.rb new file mode 100644 index 0000000..f671c53 --- /dev/null +++ b/benchmark/find-filter-vs-where-first-filters.rb @@ -0,0 +1,49 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +require 'benchmark/ips' +require_relative '../lib/jekyll' + +puts '' +print 'Setting up... ' + +SITE = Jekyll::Site.new( + Jekyll.configuration({ + "source" => File.expand_path("../docs", __dir__), + "destination" => File.expand_path("../docs/_site", __dir__), + "disable_disk_cache" => true, + "quiet" => true, + }) +) + +TEMPLATE_1 = Liquid::Template.parse(<<~HTML) + {%- assign doc = site.documents | where: 'url', '/docs/assets/' | first -%} + {{- doc.title -}} +HTML + +TEMPLATE_2 = Liquid::Template.parse(<<~HTML) + {%- assign doc = site.documents | find: 'url', '/docs/assets/' -%} + {{- doc.title -}} +HTML + +[:reset, :read, :generate].each { |phase| SITE.send(phase) } + +puts 'done.' +puts 'Testing... ' +puts " #{'where + first'.cyan} results in #{TEMPLATE_1.render(SITE.site_payload).inspect.green}" +puts " #{'find'.cyan} results in #{TEMPLATE_2.render(SITE.site_payload).inspect.green}" + +if TEMPLATE_1.render(SITE.site_payload) == TEMPLATE_2.render(SITE.site_payload) + puts 'Success! Proceeding to run benchmarks.'.green + puts '' +else + puts 'Something went wrong. Aborting.'.magenta + puts '' + return +end + +Benchmark.ips do |x| + x.report('where + first') { TEMPLATE_1.render(SITE.site_payload) } + x.report('find') { TEMPLATE_2.render(SITE.site_payload) } + x.compare! +end diff --git a/benchmark/flat-map b/benchmark/flat-map new file mode 100644 index 0000000..18f0cfd --- /dev/null +++ b/benchmark/flat-map @@ -0,0 +1,17 @@ +#!/usr/bin/env ruby +require 'benchmark/ips' + +enum = (0..50).to_a +nested = enum.map { |i| [i] } + +def do_thing(blah) + blah * 1 +end + +Benchmark.ips do |x| + x.report('.map.flatten with nested arrays') { nested.map { |i| do_thing(i) }.flatten(1) } + x.report('.flat_map with nested arrays') { nested.flat_map { |i| do_thing(i) } } + + x.report('.map.flatten with no nested arrays') { enum.map { |i| do_thing(i) }.flatten(1) } + x.report('.flat_map with no nested arrays') { enum.flat_map { |i| do_thing(i) } } +end diff --git a/benchmark/hash-fetch b/benchmark/hash-fetch new file mode 100644 index 0000000..d7427d5 --- /dev/null +++ b/benchmark/hash-fetch @@ -0,0 +1,10 @@ +#!/usr/bin/env ruby +require 'benchmark/ips' + +h = {:bar => 'uco'} + +Benchmark.ips do |x| + x.report('fetch with no block') { h.fetch(:bar, (0..9).to_a) } + x.report('fetch with a block') { h.fetch(:bar) { (0..9).to_a } } + x.report('brackets with an ||') { h[:bar] || (0..9).to_a } +end diff --git a/benchmark/jekyll-sanitize-path b/benchmark/jekyll-sanitize-path new file mode 100644 index 0000000..231de27 --- /dev/null +++ b/benchmark/jekyll-sanitize-path @@ -0,0 +1,46 @@ +#!/usr/bin/env ruby + +require_relative '../lib/jekyll' +require 'benchmark/ips' + +base_directory = Dir.pwd + +Benchmark.ips do |x| + # + # Does not include the base_directory + # + x.report('with no questionable path') do + Jekyll.sanitized_path(base_directory, '') + end + x.report('with a single-part questionable path') do + Jekyll.sanitized_path(base_directory, 'thingy') + end + x.report('with a multi-part questionable path') do + Jekyll.sanitized_path(base_directory, 'thingy/in/my/soup') + end + x.report('with a single-part traversal path') do + Jekyll.sanitized_path(base_directory, '../thingy') + end + x.report('with a multi-part traversal path') do + Jekyll.sanitized_path(base_directory, '../thingy/in/my/../../soup') + end + + # + # Including the base_directory + # + x.report('with the exact same paths') do + Jekyll.sanitized_path(base_directory, base_directory) + end + x.report('with a single-part absolute path including the base_directory') do + Jekyll.sanitized_path(base_directory, File.join(base_directory, 'thingy')) + end + x.report('with a multi-part absolute path including the base_directory') do + Jekyll.sanitized_path(base_directory, File.join(base_directory, 'thingy/in/my/soup')) + end + x.report('with a single-part traversal path including the base_directory') do + Jekyll.sanitized_path(base_directory, File.join(base_directory, 'thingy/..')) + end + x.report('with a multi-part traversal path including the base_directory') do + Jekyll.sanitized_path(base_directory, File.join('thingy/in/my/../../soup')) + end +end diff --git a/benchmark/local-require b/benchmark/local-require new file mode 100644 index 0000000..9633066 --- /dev/null +++ b/benchmark/local-require @@ -0,0 +1,29 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +require 'benchmark/ips' +require 'jekyll' +require 'json' + +DATA = {"foo"=>"bar", "alpha"=>{"beta"=>"gamma"}, "lipsum"=>["lorem", "ipsum", "dolor"]} + +def local_require + require 'json' + JSON.pretty_generate(DATA) +end + +def global_require + JSON.pretty_generate(DATA) +end + +def graceful_require + Jekyll::External.require_with_graceful_fail("json") + JSON.pretty_generate(DATA) +end + +Benchmark.ips do |x| + x.report("local-require") { local_require } + x.report("global-require") { global_require } + x.report("graceful-require") { graceful_require } + x.compare! +end diff --git a/benchmark/native-vs-pathutil-relative b/benchmark/native-vs-pathutil-relative new file mode 100644 index 0000000..c688100 --- /dev/null +++ b/benchmark/native-vs-pathutil-relative @@ -0,0 +1,33 @@ +#!/usr/bin/env ruby + +# ------------------------------------------------------------------- +# Benchmarking changes in https://github.com/jekyll/jekyll/pull/6767 +# ------------------------------------------------------------------- + +require 'benchmark/ips' +require 'pathutil' + +DOC_PATH = File.join(File.expand_path(__dir__), "_puppies", "rover.md") +COL_PATH = File.join(File.expand_path(__dir__), "_puppies") + + +def pathutil_relative + Pathutil.new(DOC_PATH).relative_path_from(COL_PATH).to_s +end + +def native_relative + DOC_PATH.sub("#{COL_PATH}/", "") +end + +if pathutil_relative == native_relative + Benchmark.ips do |x| + x.report("pathutil") { pathutil_relative } + x.report("native") { native_relative } + x.compare! + end +else + print "PATHUTIL: " + puts pathutil_relative + print "NATIVE: " + puts native_relative +end diff --git a/benchmark/parse-date b/benchmark/parse-date new file mode 100644 index 0000000..9d1d1ed --- /dev/null +++ b/benchmark/parse-date @@ -0,0 +1,25 @@ +#!/usr/bin/env ruby + +require_relative '../lib/jekyll' +require 'benchmark/ips' + +date = "2014-08-02 14:43:06 PDT".freeze +time = Time.parse(date) + +Benchmark.ips do |x| + x.report('Time.parse') do + Time.parse(date) + end + + x.report('localtime') do + Time.parse(date).localtime + end + + x.report('localtime parsed') do + time.localtime + end + + x.report('Utils.parse_date') do + Jekyll::Utils.parse_date(date) + end +end diff --git a/benchmark/parse-include-tag-params.rb b/benchmark/parse-include-tag-params.rb new file mode 100644 index 0000000..c6670e0 --- /dev/null +++ b/benchmark/parse-include-tag-params.rb @@ -0,0 +1,91 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# For pull request: https://github.com/jekyll/jekyll/pull/8192 + +require 'benchmark/ips' +require 'bundler/setup' +require 'memory_profiler' +require 'jekyll' + +CONTEXT = {"bar"=>"The quick brown fox"} +MARKUP_1 = %Q(foo=bar lorem="ipsum \\"dolor\\"" alpha='beta \\'gamma\\'').freeze +MARKUP_2 = %Q(foo=bar lorem="ipsum 'dolor'" alpha='beta "gamma"').freeze + +# + +def old_parse_params(markup) + params = {} + + while (match = Jekyll::Tags::IncludeTag::VALID_SYNTAX.match(markup)) + markup = markup[match.end(0)..-1] + + value = if match[2] + match[2].gsub('\\"', '"') + elsif match[3] + match[3].gsub("\\'", "'") + elsif match[4] + CONTEXT[match[4]] + end + + params[match[1]] = value + end + params +end + +def new_parse_params(markup) + params = {} + markup.scan(Jekyll::Tags::IncludeTag::VALID_SYNTAX) do |key, d_quoted, s_quoted, variable| + value = if d_quoted + d_quoted.include?('\\"') ? d_quoted.gsub('\\"', '"') : d_quoted + elsif s_quoted + s_quoted.include?("\\'") ? s_quoted.gsub("\\'", "'") : s_quoted + elsif variable + CONTEXT[variable] + end + + params[key] = value + end + params +end + +# + +def report(label, markup, color) + prof_report = MemoryProfiler.report { yield } + + allocated_memory = prof_report.scale_bytes(prof_report.total_allocated_memsize) + allocated_objects = prof_report.total_allocated + retained_memory = prof_report.scale_bytes(prof_report.total_retained_memsize) + retained_objects = prof_report.total_retained + + puts <<~MSG.send(color) + #{(label + " ").ljust(49, "-")} + + MARKUP: #{markup} + RESULT: #{yield} + + Total allocated: #{allocated_memory} (#{allocated_objects} objects) + Total retained: #{retained_memory} (#{retained_objects} objects) + MSG +end + +report('old w/ escaping', MARKUP_1, :magenta) { old_parse_params(MARKUP_1) } +report('new w/ escaping', MARKUP_1, :cyan) { new_parse_params(MARKUP_1) } + +report('old no escaping', MARKUP_2, :green) { old_parse_params(MARKUP_2) } +report('new no escaping', MARKUP_2, :yellow) { new_parse_params(MARKUP_2) } + +# + +Benchmark.ips do |x| + x.report("old + esc".magenta) { old_parse_params(MARKUP_1) } + x.report("new + esc".cyan) { new_parse_params(MARKUP_1) } + x.compare! +end + +Benchmark.ips do |x| + x.report("old - esc".green) { old_parse_params(MARKUP_2) } + x.report("new - esc".yellow) { new_parse_params(MARKUP_2) } + x.compare! +end diff --git a/benchmark/path-manager.rb b/benchmark/path-manager.rb new file mode 100644 index 0000000..969949f --- /dev/null +++ b/benchmark/path-manager.rb @@ -0,0 +1,65 @@ +# frozen_string_literal: true + +require 'benchmark/ips' +require 'jekyll' + +class FooPage + def initialize(dir:, name:) + @dir = dir + @name = name + end + + def slow_path + File.join(*[@dir, @name].map(&:to_s).reject(&:empty?)).sub(%r!\A/!, "") + end + + def fast_path + Jekyll::PathManager.join(@dir, @name).sub(%r!\A/!, "") + end +end + +nil_page = FooPage.new(:dir => nil, :name => nil) +empty_page = FooPage.new(:dir => "", :name => "") +root_page = FooPage.new(:dir => "", :name => "ipsum.md") +nested_page = FooPage.new(:dir => "lorem", :name => "ipsum.md") +slashed_page = FooPage.new(:dir => "/lorem/", :name => "/ipsum.md") + +if nil_page.slow_path == nil_page.fast_path + Benchmark.ips do |x| + x.report('nil_page slow') { nil_page.slow_path } + x.report('nil_page fast') { nil_page.fast_path } + x.compare! + end +end + +if empty_page.slow_path == empty_page.fast_path + Benchmark.ips do |x| + x.report('empty_page slow') { empty_page.slow_path } + x.report('empty_page fast') { empty_page.fast_path } + x.compare! + end +end + +if root_page.slow_path == root_page.fast_path + Benchmark.ips do |x| + x.report('root_page slow') { root_page.slow_path } + x.report('root_page fast') { root_page.fast_path } + x.compare! + end +end + +if nested_page.slow_path == nested_page.fast_path + Benchmark.ips do |x| + x.report('nested_page slow') { nested_page.slow_path } + x.report('nested_page fast') { nested_page.fast_path } + x.compare! + end +end + +if slashed_page.slow_path == slashed_page.fast_path + Benchmark.ips do |x| + x.report('slashed_page slow') { slashed_page.slow_path } + x.report('slashed_page fast') { slashed_page.fast_path } + x.compare! + end +end diff --git a/benchmark/proc-call-vs-yield b/benchmark/proc-call-vs-yield new file mode 100644 index 0000000..9b4ad37 --- /dev/null +++ b/benchmark/proc-call-vs-yield @@ -0,0 +1,15 @@ +#!/usr/bin/env ruby +require 'benchmark/ips' + +def fast + yield +end + +def slow(&block) + block.call +end + +Benchmark.ips do |x| + x.report('yield') { fast { (0..9).to_a } } + x.report('block.call') { slow { (0..9).to_a } } +end diff --git a/benchmark/regexp-vs-include.rb b/benchmark/regexp-vs-include.rb new file mode 100644 index 0000000..b2a4eff --- /dev/null +++ b/benchmark/regexp-vs-include.rb @@ -0,0 +1,51 @@ +#!/usr/bin/env ruby +require 'benchmark/ips' + +# For this pull request, which changes Page#dir +# https://github.com/jekyll/jekyll/pull/4403 + +CONTENT_CONTAINING = <<-HTML.freeze +<!DOCTYPE HTML> +<html lang="en-US"> + <head> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> + <meta charset="UTF-8"> + <title>Jemoji</title> + <meta name="viewport" content="width=device-width,initial-scale=1"> + <link rel="stylesheet" href="/css/screen.css"> + </head> + <body class="wrap"> + <p><img class="emoji" title=":+1:" alt=":+1:" src="https://assets.github.com/images/icons/emoji/unicode/1f44d.png" height="20" width="20" align="absmiddle"></p> + + </body> +</html> +HTML +CONTENT_NOT_CONTAINING = <<-HTML.freeze +<!DOCTYPE HTML> +<html lang="en-US"> + <head> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> + <meta charset="UTF-8"> + <title>Jemoji</title> + <meta name="viewport" content="width=device-width,initial-scale=1"> + <link rel="stylesheet" href="/css/screen.css"> + </head> + <body class="wrap"> + <p><img class="emoji" title=":+1:" alt=":+1:" src="https://assets.github.com/images/icons/emoji/unicode/1f44d.png" height="20" width="20" align="absmiddle"></p> + + </body> +</html> +HTML + +Benchmark.ips do |x| + x.report("no body include?") { CONTENT_NOT_CONTAINING.include?('<body') } + x.report("no body regexp") { CONTENT_NOT_CONTAINING =~ /<\s*body/ } + x.compare! +end + +# No trailing slash +Benchmark.ips do |x| + x.report("with body include?") { CONTENT_CONTAINING.include?('<body') } + x.report("with body regexp") { CONTENT_CONTAINING =~ /<\s*body/ } + x.compare! +end diff --git a/benchmark/sanitize-url.rb b/benchmark/sanitize-url.rb new file mode 100644 index 0000000..14ad278 --- /dev/null +++ b/benchmark/sanitize-url.rb @@ -0,0 +1,26 @@ +#!/usr/bin/env ruby + +require "benchmark/ips" + +PATH = "/../../..../...//.....//lorem/ipsum//dolor///sit.xyz" + +def sanitize_with_regex + "/" + PATH.gsub(%r!/{2,}!, "/").gsub(%r!\.+/|\A/+!, "") +end + +def sanitize_with_builtin + "/#{PATH}".gsub("..", "/").gsub("./", "").squeeze("/") +end + +if sanitize_with_regex == sanitize_with_builtin + Benchmark.ips do |x| + x.report("sanitize w/ regexes") { sanitize_with_regex } + x.report("sanitize w/ builtin") { sanitize_with_builtin } + x.compare! + end +else + puts "w/ regexes: #{sanitize_with_regex}" + puts "w/ builtin: #{sanitize_with_builtin}" + puts "" + puts "Thank you. Do try again :(" +end diff --git a/benchmark/schwartzian_transform.rb b/benchmark/schwartzian_transform.rb new file mode 100644 index 0000000..c3ade29 --- /dev/null +++ b/benchmark/schwartzian_transform.rb @@ -0,0 +1,110 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true +# +# The Ruby documentation for #sort_by describes what's called a Schwartzian transform: +# +# > A more efficient technique is to cache the sort keys (modification times in this case) +# > before the sort. Perl users often call this approach a Schwartzian transform, after +# > Randal Schwartz. We construct a temporary array, where each element is an array +# > containing our sort key along with the filename. We sort this array, and then extract +# > the filename from the result. +# > This is exactly what sort_by does internally. +# +# The well-documented efficiency of sort_by is a good reason to use it. However, when a property +# does not exist on an item being sorted, it can cause issues (no nil's allowed!) +# In Jekyll::Filters#sort_input, we extract the property in each iteration of #sort, +# which is quite inefficient! How inefficient? This benchmark will tell you just how, and how much +# it can be improved by using the Schwartzian transform. Thanks, Randall! + +require 'benchmark/ips' +require 'minitest' +require File.expand_path("../lib/jekyll", __dir__) + +def site + @site ||= Jekyll::Site.new( + Jekyll.configuration("source" => File.expand_path("../docs", __dir__)) + ).tap(&:reset).tap(&:read) +end + +def site_docs + site.collections["docs"].docs.dup +end + +def sort_by_property_directly(docs, meta_key) + docs.sort! do |apple, orange| + apple_property = apple[meta_key] + orange_property = orange[meta_key] + + if !apple_property.nil? && !orange_property.nil? + apple_property <=> orange_property + elsif !apple_property.nil? && orange_property.nil? + -1 + elsif apple_property.nil? && !orange_property.nil? + 1 + else + apple <=> orange + end + end +end + +def schwartzian_transform(docs, meta_key) + docs.collect! { |d| + [d[meta_key], d] + }.sort! { |apple, orange| + if !apple[0].nil? && !orange[0].nil? + apple.first <=> orange.first + elsif !apple[0].nil? && orange[0].nil? + -1 + elsif apple[0].nil? && !orange[0].nil? + 1 + else + apple[-1] <=> orange[-1] + end + }.collect! { |d| d[-1] } +end + +# Before we test efficiency, do they produce the same output? +class Correctness + include Minitest::Assertions + + require "pp" + define_method :mu_pp, &:pretty_inspect + + attr_accessor :assertions + + def initialize(docs, property) + @assertions = 0 + @docs = docs + @property = property + end + + def assert! + assert sort_by_property_directly(@docs, @property).is_a?(Array), "sort_by_property_directly must return an array" + assert schwartzian_transform(@docs, @property).is_a?(Array), "schwartzian_transform must return an array" + assert_equal sort_by_property_directly(@docs, @property), + schwartzian_transform(@docs, @property) + puts "Yeah, ok, correctness all checks out for property #{@property.inspect}" + end +end + +Correctness.new(site_docs, "redirect_from".freeze).assert! +Correctness.new(site_docs, "title".freeze).assert! + +def property(property, meta_key) + Benchmark.ips do |x| + x.config(time: 10, warmup: 5) + x.report("sort_by_property_directly with #{property} property") do + sort_by_property_directly(site_docs, meta_key) + end + x.report("schwartzian_transform with #{property} property") do + schwartzian_transform(site_docs, meta_key) + end + x.compare! + end +end + +# First, test with a property only a handful of documents have. +test_property('sparse', 'redirect_from') + +# Next, test with a property they all have. +test_property('non-sparse', 'title') diff --git a/benchmark/sequential-assignment b/benchmark/sequential-assignment new file mode 100644 index 0000000..790e995 --- /dev/null +++ b/benchmark/sequential-assignment @@ -0,0 +1,12 @@ +#!/usr/bin/env ruby +require 'benchmark/ips' + +Benchmark.ips do |x| + x.report('parallel assignment') do + a, b = 1, 2 + end + x.report('multi-line assignment') do + a = 1 + b = 2 + end +end diff --git a/benchmark/static-drop-vs-forwarded.rb b/benchmark/static-drop-vs-forwarded.rb new file mode 100644 index 0000000..9f8365d --- /dev/null +++ b/benchmark/static-drop-vs-forwarded.rb @@ -0,0 +1,83 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +require "forwardable" +require "colorator" +require "liquid" +require "benchmark/ips" +require "memory_profiler" + +# Set up (memory) profiler + +class Profiler + def self.run + yield new(ARGV[0] || 10_000) + end + + def initialize(count) + @count = count.to_i + end + + def report(label, color, &block) + prof_report = MemoryProfiler.report { @count.to_i.times(&block) } + + allocated_memory = prof_report.scale_bytes(prof_report.total_allocated_memsize) + allocated_objects = prof_report.total_allocated + retained_memory = prof_report.scale_bytes(prof_report.total_retained_memsize) + retained_objects = prof_report.total_retained + + puts <<~MSG.send(color) + With #{label} calls + + Total allocated: #{allocated_memory} (#{allocated_objects} objects) + Total retained: #{retained_memory} (#{retained_objects} objects) + MSG + end +end + +# Set up stage + +class Drop < Liquid::Drop + def initialize(obj) + @obj = obj + end +end + +class ForwardDrop < Drop + extend Forwardable + def_delegators :@obj, :name +end + +class StaticDrop < Drop + def name + @obj.name + end +end + +class Document + def name + "lipsum" + end +end + +# Set up actors + +document = Document.new +alpha = ForwardDrop.new(document) +beta = StaticDrop.new(document) +count = ARGV[0] || 10_000 + +# Run profilers +puts "\nMemory profiles for #{count} calls to invoke drop key:" +Profiler.run do |x| + x.report("forwarded", :cyan) { alpha["name"] } + x.report("static", :green) { beta["name"] } +end + +# Benchmark +puts "\nBenchmarking the two scenarios..." +Benchmark.ips do |x| + x.report("forwarded".cyan) { alpha["name"] } + x.report("static".green) { beta["name"] } + x.compare! +end diff --git a/benchmark/string-concat b/benchmark/string-concat new file mode 100644 index 0000000..2020bb5 --- /dev/null +++ b/benchmark/string-concat @@ -0,0 +1,9 @@ +#!/usr/bin/env ruby +require 'benchmark/ips' + +url = "https://jekyllrb.com" + +Benchmark.ips do |x| + x.report('+=') { url += '/' } + x.report('<<') { url << '/' } +end diff --git a/benchmark/string-replacement b/benchmark/string-replacement new file mode 100644 index 0000000..f522cfe --- /dev/null +++ b/benchmark/string-replacement @@ -0,0 +1,14 @@ +#!/usr/bin/env ruby +require 'benchmark/ips' + +def str + 'http://baruco.org/2014/some-talk-with-some-amount-of-value' +end + +Benchmark.ips do |x| + x.report('#tr') { str.tr('some', 'a') } + x.report('#gsub') { str.gsub('some', 'a') } + x.report('#gsub!') { str.gsub!('some', 'a') } + x.report('#sub') { str.sub('some', 'a') } + x.report('#sub!') { str.sub!('some', 'a') } +end diff --git a/benchmark/symbol-to-proc b/benchmark/symbol-to-proc new file mode 100644 index 0000000..7b568c4 --- /dev/null +++ b/benchmark/symbol-to-proc @@ -0,0 +1,7 @@ +#!/usr/bin/env ruby +require 'benchmark/ips' + +Benchmark.ips do |x| + x.report('block') { (1..100).map { |i| i.to_s } } + x.report('&:to_s') { (1..100).map(&:to_s) } +end diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 0000000..ab6b666 --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1,5 @@ +_site/ +.idea/ +*.swp +pkg/ +test/ diff --git a/docs/CNAME b/docs/CNAME new file mode 100644 index 0000000..f501cde --- /dev/null +++ b/docs/CNAME @@ -0,0 +1 @@ +jekyllrb.com diff --git a/docs/_config.yml b/docs/_config.yml new file mode 100644 index 0000000..74a9914 --- /dev/null +++ b/docs/_config.yml @@ -0,0 +1,64 @@ +--- +version: 4.3.1 +name: Jekyll • Simple, blog-aware, static sites +description: Transform your plain text into static websites and blogs +url: https://jekyllrb.com +repository: https://github.com/jekyll/jekyll +timezone: America/Los_Angeles +twitter: + username: jekyllrb +logo: "/img/logo-2x.png" +google_analytics_id: UA-50755011-1 +google_site_verification: onQcXpAvtHBrUI5LlroHNE_FP0b2qvFyPq7VZw36iEY +cloudinary_url: https://res.cloudinary.com/jekyll/image/upload/f_auto,q_auto,w_404 +collections: + docs: + permalink: "/:collection/:path/" + output: true + posts: + permalink: "/news/:year/:month/:day/:title/" + output: true + tutorials: + permalink: "/:collection/:path/" + output: true +defaults: +- scope: + path: _docs + type: docs + values: + layout: docs +- scope: + path: _posts + type: posts + values: + layout: news_item +- scope: + path: _tutorials + type: tutorials + values: + layout: tutorials +- scope: + path: '' + values: + image: "/img/jekyll-og.png" +future: true +plugins: +- jekyll-avatar +- jekyll-feed +- jekyll-mentions +- jekyll-redirect-from +- jekyll-seo-tag +- jekyll-sitemap +- jemoji +feed: + categories: + - release +kramdown: + syntax_highlighter_opts: + default_lang: plaintext +sass: + style: compressed +strict_front_matter: true +exclude: +- icomoon-selection.json +- readme.md diff --git a/docs/_data/config_options/build.yml b/docs/_data/config_options/build.yml new file mode 100644 index 0000000..7f63573 --- /dev/null +++ b/docs/_data/config_options/build.yml @@ -0,0 +1,123 @@ +- name: Regeneration + description: Enable auto-regeneration of the site when files are modified. + flag: "-w, --[no-]watch" + + +- name: Configuration + description: >- + Specify config files instead of using <code>_config.yml</code> automatically. + Settings in later files override settings in earlier files. + flag: "--config FILE1[,FILE2,...]" + + +- name: Plugins + description: >- + Specify plugin directories instead of using <code>_plugins/</code> automatically. + option: "plugins_dir: [ DIR1,... ]" + flag: "-p, --plugins DIR1[,DIR2,...]" + + +- name: Layouts + description: >- + Specify layout directory instead of using <code>_layouts/</code> automatically. + option: "layouts_dir: DIR" + flag: --layouts DIR + + +- name: Drafts + description: Process and render draft posts. + option: "show_drafts: BOOL" + flag: -D, --drafts + + +- name: Environment + description: Use a specific environment value in the build. + flag: JEKYLL_ENV=production + + +- name: Future + description: Publish posts or collection documents with a future date. + option: "future: BOOL" + flag: --future + + +- name: Unpublished + description: Render posts that were marked as unpublished. + option: "unpublished: BOOL" + flag: --unpublished + + +- name: LSI + description: >- + Produce an index for related posts. Requires the + <a href="https://jekyll.github.io/classifier-reborn/">classifier-reborn</a> plugin. + option: "lsi: BOOL" + flag: --lsi + + +- name: Limit posts + description: Limit the number of posts to parse and publish. + option: "limit_posts: NUM" + flag: --limit_posts NUM + + +- name: Force polling + description: Force watch to use polling. + option: "force_polling: BOOL" + flag: --force_polling + + +- name: Verbose output + description: Print verbose output. + option: "verbose: BOOL" + flag: -V, --verbose + + +- name: Silence output + description: Silence the normal output from Jekyll during a build. + option: "quiet: BOOL" + flag: -q, --quiet + + +- name: Log level + description: Specify a log level among debug, info, warn, or error. + flag: JEKYLL_LOG_LEVEL=info + + +- name: Incremental build + description: >- + Enable the experimental + <a href="/docs/configuration/incremental-regeneration/">incremental + build</a> feature. Incremental build only re-builds posts and pages that + have changed, resulting in significant performance improvements for large + sites, but may also break site generation in certain cases. + option: "incremental: BOOL" + flag: -I, --incremental + + +- name: Disable bundle require + description: Disables the need to require gems in `:jekyll_plugins` Gemfile + flag: JEKYLL_NO_BUNDLER_REQUIRE=true + + +- name: Liquid profiler + description: Generate a Liquid rendering profile to help you identify performance bottlenecks. + option: "profile: BOOL" + flag: --profile + + +- name: Strict front matter + description: Cause a build to fail if there is a YAML syntax error in a page's front matter. + option: "strict_front_matter: BOOL" + flag: --strict_front_matter + + +- name: Base URL + description: Serve the website from the given base URL. + option: "baseurl: URL" + flag: -b, --baseurl URL + + +- name: Trace + description: Show the full backtrace when an error occurs. + flag: -t, --trace diff --git a/docs/_data/config_options/global.yml b/docs/_data/config_options/global.yml new file mode 100644 index 0000000..406e841 --- /dev/null +++ b/docs/_data/config_options/global.yml @@ -0,0 +1,77 @@ +- name: Site source + description: Change the directory where Jekyll will read files + option: "source: DIR" + flag: -s, --source DIR + + +- name: Site destination + description: Change the directory where Jekyll will write files + option: "destination: DIR" + flag: -d, --destination DIR + + +- name: Safe + description: >- + Disable <a href="/docs/plugins/">non-whitelisted plugins</a>, caching to disk, and ignore symbolic links. + option: "safe: BOOL" + flag: --safe + + +- name: Disable disk cache + version-badge: 4.1.0 + description: >- + Disable caching of content to disk in order to skip creating a <code>.jekyll-cache</code> or similar directory at + the source to avoid interference with virtual environments and third-party directory watchers. Caching to disk is + always disabled in <code>safe</code> mode. + option: "disable_disk_cache: BOOL" + flag: --disable-disk-cache + + +- name: Ignore theme configuration + version-badge: 4.1.0 + description: >- + Jekyll 4.0 started allowing themes to bundle a <code>_config.yml</code> to simplify theme-onboarding for new users. + In the unfortunate situation that importing a bundled theme configuration messes up the merged site-configuration, + the user can configure Jekyll to not import the theme-config entirely. + option: "ignore_theme_config: BOOL" + + +- name: Exclude + description: >- + Exclude directories and/or files from the conversion. These exclusions are relative to the site's source directory + and cannot be outside the source directory. + option: "exclude: [DIR, FILE, ...]" + + +- name: Include + description: >- + Force inclusion of directories and/or files in the conversion. <code>.htaccess</code> is a good example since + dotfiles are excluded by default. + option: "include: [DIR, FILE, ...]" + + +- name: Keep files + description: >- + When clobbering the site destination, keep the selected files. Useful for files that are not generated by jekyll; + e.g. files or assets that are generated by your build tool. The paths are relative to the <code>destination</code>. + option: "keep_files: [DIR, FILE, ...]" + + +- name: Time zone + description: >- + Set the time zone for site generation. This sets the <code>TZ</code> environment variable, which Ruby uses to handle + time and date creation and manipulation. Any entry from the + <a href="https://en.wikipedia.org/wiki/Tz_database">IANA Time Zone Database</a> + is valid, e.g. <code>America/New_York</code>. A list of all available values can be found + <a href="https://en.wikipedia.org/wiki/List_of_tz_database_time_zones"> here</a>. + When serving on a local machine, the default time zone is set by your operating system. But when served on a remote + host/server, the default time zone depends on the server's setting or location. + option: "timezone: TIMEZONE" + + +- name: Encoding + description: >- + Set the encoding of files by name (only available for Ruby 1.9 or later). The default value is <code>utf-8</code> + starting in 2.0.0, and <code>nil</code> before 2.0.0, which will yield the Ruby default of <code>ASCII-8BIT</code>. + Available encodings can be shown by the command <code>ruby -e 'puts Encoding::list.join("\n")'</code>. + option: "encoding: ENCODING" diff --git a/docs/_data/config_options/serve.yml b/docs/_data/config_options/serve.yml new file mode 100644 index 0000000..fc25448 --- /dev/null +++ b/docs/_data/config_options/serve.yml @@ -0,0 +1,71 @@ +- name: Local server port + description: Listen on the given port. The default is `4000`. + option: "port: PORT" + flag: "-P, --port PORT" + + +- name: Local server hostname + description: Listen at the given hostname. The default is `localhost`. + option: "host: HOSTNAME" + flag: "-H, --host HOSTNAME" + + +- name: Live reload + description: Reload a page automatically on the browser when its content is edited. + option: "livereload: BOOL" + flag: "-l, --livereload" + + +- name: Live reload ignore + description: File glob patterns for LiveReload to ignore. + option: "livereload_ignore: [ GLOB1,... ]" + flag: "--livereload-ignore GLOB1[,GLOB2,...]" + + +- name: Live reload min/max delay + description: Minimum/Maximum delay before automatically reloading page. + options: + - "livereload_min_delay: SECONDS" + - "livereload_max_delay: SECONDS" + flags: + - "--livereload-min-delay SECONDS" + - "--livereload-max-delay SECONDS" + + +- name: Live reload port + description: Port for LiveReload to listen on. + flag: "--livereload-port PORT" + + +- name: Open URL + description: Open the site's URL in the browser. + option: "open_url: BOOL" + flag: "-o, --open-url" + + +- name: Detach + description: Detach the server from the terminal. + option: "detach: BOOL" + flag: "-B, --detach" + + +- name: Skips the initial site build + description: Skips the initial site build which occurs before the server is started. + option: "skip_initial_build: BOOL" + flag: "--skip-initial-build" + + +- name: Show directory listing + description: Show a directory listing instead of loading your index file. + option: "show_dir_listing: BOOL" + flag: "--show-dir-listing" + + +- name: X.509 (SSL) private key + description: "SSL Private Key, stored or symlinked in the site source." + flag: "--ssl-key" + + +- name: X.509 (SSL) certificate + description: "SSL Public certificate, stored or symlinked in the site source." + flag: "--ssl-cert" diff --git a/docs/_data/docs_nav.yml b/docs/_data/docs_nav.yml new file mode 100644 index 0000000..1e1af40 --- /dev/null +++ b/docs/_data/docs_nav.yml @@ -0,0 +1,37 @@ +- title: Getting Started + docs: + - link: /docs/ + - link: /docs/installation/ + - link: /docs/ruby-101/ + - link: /docs/community/ + - link: /docs/step-by-step/01-setup/ +- title: Build + docs: + - link: /docs/usage/ + - link: /docs/configuration/ + - link: /docs/rendering-process/ +- title: Content + docs: + - link: /docs/pages/ + - link: /docs/posts/ + - link: /docs/front-matter/ + - link: /docs/collections/ + - link: /docs/datafiles/ + - link: /docs/assets/ + - link: /docs/static-files/ +- title: Site Structure + docs: + - link: /docs/structure/ + - link: /docs/liquid/ + - link: /docs/variables/ + - link: /docs/includes/ + - link: /docs/layouts/ + - link: /docs/permalinks/ + - link: /docs/themes/ + - link: /docs/pagination/ +- title: Guides + docs: + - link: /docs/plugins/ + - link: /docs/migrations/ + - link: /docs/upgrading/ + - link: /docs/deployment/ diff --git a/docs/_data/jekyll_filters.yml b/docs/_data/jekyll_filters.yml new file mode 100644 index 0000000..de052a5 --- /dev/null +++ b/docs/_data/jekyll_filters.yml @@ -0,0 +1,348 @@ +# +# --------------------------------------------------------------------------------------- +# List of Liquid Filters provided by Jekyll Core that will be utilized for their +# documentation. +# +# To document a new filter, create a new "list-item" below with the following keys: +# name: : [REQUIRED] A string label that identifies the filter +# description: : [REQUIRED] A short description of what to expect from the filter +# version_badge: : [OPTIONAL] Jekyll version that introduced the filter +# examples: : [REQUIRED] A 'nested list' comprised of inputs and outputs +# input: : [REQUIRED] The filter syntax and usage +# output: : [OPTIONAL] The output from the filter +# +# Tip: Use YAML Block notations to "fold" a long string, or to "break" a long string +# to the following line. Block notations can also be used to avoid having to use +# backslashes to escape quotes. +# --------------------------------------------------------------------------------------- +# +- name: Relative URL + description: >- + Prepend <code>baseurl</code> config value to the input to convert a URL path into a relative URL. + This is recommended for a site that is hosted on a subpath of a domain. + examples: + - input: '{{ "/assets/style.css" | relative_url }}' + output: '/my-baseurl/assets/style.css' + +# + +- name: Absolute URL + description: >- + Prepend <code>url</code> and <code>baseurl</code> values to the input to convert a URL path to an absolute URL. + examples: + - input: '{{ "/assets/style.css" | absolute_url }}' + output: 'http://example.com/my-baseurl/assets/style.css' + +# + +- name: Date to XML Schema + description: Convert a Date into XML Schema (ISO 8601) format. + examples: + - input: '{{ site.time | date_to_xmlschema }}' + output: '2008-11-07T13:07:54-08:00' + +# + +- name: Date to RFC-822 Format + description: Convert a Date into the RFC-822 format used for RSS feeds. + examples: + - input: '{{ site.time | date_to_rfc822 }}' + output: 'Mon, 07 Nov 2008 13:07:54 -0800' + +# + +- name: Date to String + description: Convert a date to short format. + examples: + - input: '{{ site.time | date_to_string }}' + output: '07 Nov 2008' + +# + +- name: Date to String in ordinal US style + description: 'Format a date to ordinal, US, short format.' + version_badge: 3.8.0 + examples: + - input: '{{ site.time | date_to_string: "ordinal", "US" }}' + output: 'Nov 7th, 2008' + +# + +- name: Date to Long String + description: Format a date to long format. + examples: + - input: '{{ site.time | date_to_long_string }}' + output: '07 November 2008' + +# + +- name: Date to Long String in ordinal UK style + description: 'Format a date to ordinal, UK, long format.' + version_badge: 3.8.0 + examples: + - input: '{{ site.time | date_to_long_string: "ordinal" }}' + output: '7th November 2008' + +# + +- name: Where + description: Select all the objects in an array where the key has the given value. + examples: + - input: '{{ site.members | where:"graduation_year","2014" }}' + output: + +# + +- name: Where Expression + description: Select all the objects in an array where the expression is true. + version_badge: 3.2.0 + examples: + - input: |- + {{ site.members | where_exp:"item", + "item.graduation_year == 2014" }} + output: + - input: |- + {{ site.members | where_exp:"item", + "item.graduation_year < 2014" }} + output: + - input: |- + {{ site.members | where_exp:"item", + "item.projects contains 'foo'" }} + output: + +# + +- name: Find + description: >- + Return <strong>the first object</strong> in an array for which the queried + attribute has the given value or return <code>nil</code> if no item in + the array satisfies the given criteria. + version_badge: 4.1.0 + examples: + - input: '{{ site.members | find: "graduation_year", "2014" }}' + output: + +# + +- name: Find Expression + description: >- + Return <strong>the first object</strong> in an array for which the given + expression evaluates to true or return <code>nil</code> if no item in + the array satisfies the evaluated expression. + version_badge: 4.1.0 + examples: + - input: |- + {{ site.members | find_exp:"item", + "item.graduation_year == 2014" }} + output: + - input: |- + {{ site.members | find_exp:"item", + "item.graduation_year < 2014" }} + output: + - input: |- + {{ site.members | find_exp:"item", + "item.projects contains 'foo'" }} + output: + +# + +- name: Group By + description: Group an array's items by a given property. + examples: + - input: '{{ site.members | group_by:"graduation_year" }}' + output: |- + [{"name"=>"2013", "items"=>[...]}, + {"name"=>"2014", "items"=>[...]}] + +# + +- name: Group By Expression + description: Group an array's items using a Liquid expression. + version_badge: 3.4.0 + examples: + - input: |- + {{ site.members | group_by_exp: "item", + "item.graduation_year | truncate: 3, ''" }} + output: |- + [{"name"=>"201", "items"=>[...]}, + {"name"=>"200", "items"=>[...]}] + +# + +- name: XML Escape + description: Escape some text for use in XML. + examples: + - input: '{{ page.content | xml_escape }}' + output: + +# + +- name: CGI Escape + description: >- + CGI escape a string for use in a URL. Replaces any special characters + with appropriate <code>%XX</code> replacements. CGI escape normally + replaces a space with a plus <code>+</code> sign. + examples: + - input: '{{ "foo, bar; baz?" | cgi_escape }}' + output: 'foo%2C+bar%3B+baz%3F' + +# + +- name: URI Escape + description: >- + Percent encodes any special characters in a URI. + URI escape normally replaces a space with <code>%20</code>. + <a href="https://en.wikipedia.org/wiki/Percent-encoding#Types_of_URI_characters">Reserved characters</a> + will not be escaped. + examples: + - input: '{{ "http://foo.com/?q=foo, \bar?" | uri_escape }}' + output: 'http://foo.com/?q=foo,%20%5Cbar?' + +# + +- name: Number of Words + description: >- + Count the number of words in some text.<br/> + From <span class="version-badge">v4.1.0</span>, this filter takes an optional + argument to control the handling of Chinese-Japanese-Korean (CJK) characters + in the <code>input</code> string.<br/> + Passing <code>'cjk'</code> as the argument will count every CJK character + detected as one word irrespective of being separated by whitespace.<br/> + Passing <code>'auto'</code> (auto-detect) works similar to <code>'cjk'</code> + but is more performant if the filter is used on a variable string that may + or may not contain CJK chars. + examples: + - input: '{{ "Hello world!" | number_of_words }}' + output: 2 + - input: '{{ "你好hello世界world" | number_of_words }}' + output: 1 + - input: '{{ "你好hello世界world" | number_of_words: "cjk" }}' + output: 6 + - input: '{{ "你好hello世界world" | number_of_words: "auto" }}' + output: 6 + +# + +- name: Array to Sentence + description: >- + Convert an array into a sentence. Useful for listing tags. + Optional argument for connector. + examples: + - input: '{{ page.tags | array_to_sentence_string }}' + output: 'foo, bar, and baz' + - input: '{{ page.tags | array_to_sentence_string: "or" }}' + output: 'foo, bar, or baz' + +# + +- name: Markdownify + description: Convert a Markdown-formatted string into HTML. + examples: + - input: '{{ page.excerpt | markdownify }}' + output: + +# + +- name: Smartify + description: 'Convert "quotes" into “smart quotes.”' + examples: + - input: '{{ page.title | smartify }}' + output: + +# + +- name: Converting Sass/SCSS + description: Convert a Sass- or SCSS-formatted string into CSS. + examples: + - input: '{{ some_sass | sassify }}' + output: + - input: '{{ some_scss | scssify }}' + output: + +# + +- name: Slugify + description: Convert a string into a lowercase URL "slug". See below for options. + examples: + - input: '{{ "The _config.yml file" | slugify }}' + output: 'the-config-yml-file' + - input: '{{ "The _config.yml file" | slugify: "pretty" }}' + output: 'the-_config.yml-file' + - input: '{{ "The _cönfig.yml file" | slugify: "ascii" }}' + output: 'the-c-nfig-yml-file' + - input: '{{ "The cönfig.yml file" | slugify: "latin" }}' + output: 'the-config-yml-file' + +# + +- name: Data To JSON + description: Convert Hash or Array to JSON. + examples: + - input: '{{ site.data.projects | jsonify }}' + output: + +# + +- name: Normalize Whitespace + description: Replace any occurrence of whitespace with a single space. + examples: + - input: '{{ "a \n b" | normalize_whitespace }}' + output: + +# + +- name: Sort + description: >- + Sort an array. Optional arguments for hashes + 1. property name + 2. nils order (<em>first</em> or <em>last</em>). + examples: + - input: '{{ page.tags | sort }}' + output: + - input: '{{ site.posts | sort: "author" }}' + output: + - input: '{{ site.pages | sort: "title", "last" }}' + output: + +# + +- name: Sample + description: 'Pick a random value from an array. Optionally, pick multiple values.' + examples: + - input: '{{ site.pages | sample }}' + output: + - input: '{{ site.pages | sample: 2 }}' + output: + +# + +- name: To Integer + description: Convert a string or boolean to integer. + examples: + - input: '{{ some_var | to_integer }}' + output: + +# + +- name: Array Filters + description: >- + Push, pop, shift, and unshift elements from an Array. + These are <strong>NON-DESTRUCTIVE</strong>, i.e. they do not mutate the array, + but rather make a copy and mutate that. + examples: + - input: '{{ page.tags | push: "Spokane" }}' + output: '["Seattle", "Tacoma", "Spokane"]' + - input: '{{ page.tags | pop }}' + output: '["Seattle"]' + - input: '{{ page.tags | shift }}' + output: '["Tacoma"]' + - input: '{{ page.tags | unshift: "Olympia" }}' + output: '["Olympia", "Seattle", "Tacoma"]' + +# + +- name: Inspect + description: Convert an object into its String representation for debugging. + examples: + - input: '{{ some_var | inspect }}' + output: diff --git a/docs/_data/jekyll_variables.yml b/docs/_data/jekyll_variables.yml new file mode 100644 index 0000000..7f41a15 --- /dev/null +++ b/docs/_data/jekyll_variables.yml @@ -0,0 +1,193 @@ +# Variables provided by Jekyll core +# +# name: : name of the variable +# description: : content returned by the variable + +global: + - name: site + description: >- + Site wide information + configuration settings from <code>_config.yml</code>. + See below for details. + - name: page + description: >- + Page specific information + the <a href="/docs/front-matter/">front matter</a>. + Custom variables set via the front matter will be available here. See below for details. + - name: layout + description: >- + Layout specific information + the <a href="/docs/front-matter/">front matter</a>. + Custom variables set via front matter in layouts will be available here. + - name: theme + description: >- + Theme-gem specific information as defined in the theme's gemspec. Useful for rendering + information in the theme demo's "About" page, for example. See below for details. + - name: content + description: >- + In layout files, the rendered content of the Post or Page being wrapped. + Not defined in Post or Page files. + - name: paginator + description: >- + When the <code>paginate</code> configuration option is set, this variable becomes available + for use. See <a href="../pagination/">Pagination</a> for details. + +site: + - name: site.time + description: >- + The current time (when you run the <code>jekyll</code> command). + - name: site.pages + description: >- + A list of all Pages. + - name: site.posts + description: >- + A reverse chronological list of all Posts. + - name: site.related_posts + description: >- + If the page being processed is a Post, this contains a list of up to ten related Posts. + By default, these are the ten most recent posts. For high quality but slow to compute + results, run the <code>jekyll</code> command with the <code>--lsi</code> + (<a href="https://en.wikipedia.org/wiki/Latent_semantic_analysis#Latent_semantic_indexing">latent semantic indexing</a>) + option. Also note GitHub Pages does not support the + <code>lsi</code> option when generating sites. + - name: site.static_files + description: >- + A list of all <a href="/docs/static-files/">static files</a> (i.e. + files not processed by Jekyll's converters or the Liquid renderer). + Each file has five properties: <code>path</code>, <code>modified_time</code>, + <code>name</code>, <code>basename</code> and <code>extname</code>. + - name: site.html_pages + description: >- + A subset of <code>site.pages</code> listing those which end in <code>.html</code>. + - name: site.html_files + description: >- + A subset of <code>site.static_files</code> listing those which end in <code>.html</code>. + - name: site.collections + description: >- + A list of all the collections (including posts). + - name: site.data + description: >- + A list containing the data loaded from the YAML files located in the <code>_data</code> + directory. + - name: site.documents + description: >- + A list of all the documents in every collection. + - name: site.categories.CATEGORY + description: >- + The list of all Posts in category <code>CATEGORY</code>. + - name: site.tags.TAG + description: >- + The list of all Posts with tag <code>TAG</code>. + - name: site.url + description: >- + Contains the url of your site as it is configured in the <code>_config.yml</code>. + For example, if you have <code>url: http://mysite.com</code> in your configuration file, + then it will be accessible in Liquid as <code>site.url</code>. For the development + environment there is <a href="/news/2016/10/06/jekyll-3-3-is-here/#3-siteurl-is-set-by-the-development-server">an + exception</a>, if you are running <code>jekyll serve</code> in a development environment + <code>site.url</code> will be set to the value of <code>host</code>, <code>port</code>, + and SSL-related options. This defaults to <code>url: http://localhost:4000</code>. + - name: "site.[CONFIGURATION_DATA]" + description: >- + All the variables set via the command line and your <code>_config.yml</code> are available + through the <code>site</code> variable. For example, if you have <code>foo: bar</code> in + your configuration file, then it will be accessible in Liquid as <code>site.foo</code>. + Jekyll does not parse changes to <code>_config.yml</code> in + <code>watch</code> mode, you must restart Jekyll to see changes to variables. + +page: + - name: page.content + description: >- + The content of the Page, rendered or un-rendered depending upon + what Liquid is being processed and what <code>page</code> is. + - name: page.title + description: >- + The title of the Page. + - name: page.excerpt + description: >- + The un-rendered excerpt of a document. + - name: page.url + description: >- + The URL of the Post without the domain, but with a leading slash, e.g. + <code>/2008/12/14/my-post.html</code> + - name: page.date + description: >- + The Date assigned to the Post. This can be overridden in a Post’s front matter by specifying + a new date/time in the format <code>YYYY-MM-DD HH:MM:SS</code> (assuming UTC), or + <code>YYYY-MM-DD HH:MM:SS +/-TTTT</code> (to specify a time zone using an offset from UTC. + e.g. <code>2008-12-14 10:30:00 +0900</code>). + - name: page.id + description: >- + An identifier unique to a document in a Collection or a Post (useful in RSS feeds). e.g. + <code>/2008/12/14/my-post</code><code>/my-collection/my-document</code> + - name: page.categories + description: >- + The list of categories to which this post belongs. Categories are derived from the directory + structure above the <code>_posts</code> directory. For example, a post at + <code>/work/code/_posts/2008-12-24-closures.md</code> would have this field set to + <code>['work', 'code']</code>. These can also be specified in the + <a href="/docs/front-matter/">front matter</a>. + - name: page.collection + description: >- + The label of the collection to which this document belongs. e.g. <code>posts</code> for a post, or + <code>puppies</code> for a document at path <code>_puppies/rover.md</code>. If not part of a + collection, an empty string is returned. + - name: page.tags + description: >- + The list of tags to which this post belongs. These can be specified in the + <a href="/docs/front-matter/">front matter</a>. + - name: page.dir + description: >- + The path between the source directory and the file of the post or page, e.g. + <code>/pages/</code>. + This can be overridden by <code>permalink</code> in the <a href="/docs/front-matter/">front matter</a>. + - name: page.name + description: >- + The filename of the post or page, e.g. <code>about.md</code> + - name: page.path + description: >- + The path to the raw post or page. Example usage: Linking back to the page or post’s source + on GitHub. This can be overridden in the <a href="/docs/front-matter/">front matter</a>. + - name: page.next + description: >- + The next post relative to the position of the current post in <code>site.posts</code>. + Returns <code>nil</code> for the last entry. + - name: page.previous + description: >- + The previous post relative to the position of the current post in <code>site.posts</code>. + Returns <code>nil</code> for the first entry. + +theme: + - name: theme.root + description: Absolute path to the theme-gem. + - name: theme.authors + description: Comma separated string composed of the authors of the theme-gem. + - name: theme.description + description: Description or summary of the theme-gem as specified in the theme gemspec. + - name: theme.version + description: The version string of current theme. + - name: theme.dependencies + description: List of runtime dependencies of the theme. + - name: theme.metadata + description: A mapping of key-value pairs as defined in the theme gemspec. + +paginator: + - name: paginator.page + description: The number of the current page + - name: paginator.per_page + description: Number of posts per page + - name: paginator.posts + description: Posts available for the current page + - name: paginator.total_posts + description: Total number of posts + - name: paginator.total_pages + description: Total number of pages + - name: paginator.previous_page + description: >- + The number of the previous page, or <code>nil</code> if no previous page exists + - name: paginator.previous_page_path + description: >- + The path to the previous page, or <code>nil</code> if no previous page exists + - name: paginator.next_page + description: >- + The number of the next page, or <code>nil</code> if no subsequent page exists + - name: paginator.next_page_path + description: >- + The path to the next page, or <code>nil</code> if no subsequent page exists diff --git a/docs/_data/jekyllconf-talks.yml b/docs/_data/jekyllconf-talks.yml new file mode 100644 index 0000000..c386f81 --- /dev/null +++ b/docs/_data/jekyllconf-talks.yml @@ -0,0 +1,209 @@ +- speaker: Ben Balter + twitter_handle: BenBalter + youtube_id: Z-37y1qaoxc + topic: GitHub Pages behind the scenes + year: 2015 + +- speaker: Brandon Mathis + twitter_handle: imathis + youtube_id: KS6e4XxY2H4 + topic: What the heck is Octopress and why should I care? + year: 2015 + +- speaker: Brian Rinaldi + twitter_handle: remotesynth + youtube_id: vT7DhK5zbv0 + topic: Comparing Jekyll with the Competition + year: 2015 + +- speaker: Kyle Rush + twitter_handle: kylerush + youtube_id: ia8vsuiXiL0 + topic: Meet the Obama Campaign's $250 Million Fundraising Platform + year: 2015 + +- speaker: Michael Jovel + twitter_handle: mjovel + youtube_id: 8zSHG6XU_xY + topic: Building Living Style Guides with Jekyll + year: 2015 + +- speaker: Mike Neumegen + twitter_handle: mikeneumegen + youtube_id: NuChR_YdjrI + topic: A CMS for Jekyll + year: 2015 + +- speaker: Parker Moore + twitter_handle: parkr + youtube_id: y2SbOIQ5nSA + topic: Jekyll 3 and Beyond + year: 2015 + +- speaker: Tom Preston-Werner + twitter_handle: mojombo + youtube_id: BMve1OCKj6M + topic: Some crazy ideas I have for the future of static sites + year: 2015 + +- speaker: Allison Zadrozny + twitter_handle: allizad + youtube_id: Rsc0Mmp1qc8 + topic: Elasticsearch for Jekyll + year: 2016 + +- speaker: Amy Johnston + twitter_handle: AmyJohnstonXL + youtube_id: HR12JiUI2Zc + topic: Jekyll for Technical Documentation + year: 2016 + +- speaker: Bud Parr + twitter_handle: budparr + youtube_id: A1nTuNjoNbg + topic: Real World Content Strategy with Jekyll + year: 2016 + +- speaker: George Phillips + twitter_handle: gphillips_nz + youtube_id: skb_XWABEDc + topic: Building client-editable Jekyll sites + year: 2016 + +- speaker: Ire Aderinokun + twitter_handle: ireaderinokun + youtube_id: PRKV5IGKF2c + topic: Using Jekyll for Rapid CSS Testing + year: 2016 + +- speaker: Jon Chan + twitter_handle: JonHMChan + youtube_id: vDeKPs6xpOM + topic: Stack Overflow on Jekyll + year: 2016 + +- speaker: Julio Faerman + twitter_handle: juliodevrel + youtube_id: SOMonG8Iqak + topic: Jekyll on AWS + year: 2016 + +- speaker: Katy DeCorah + twitter_handle: katydecorah + youtube_id: s84wFRD8vfE + topic: Unconventional use cases for Jekyll + year: 2016 + +- speaker: David Darnes + twitter_handle: DavidDarnes + youtube_id: Y4qwpN40Dvg + topic: Doing a lot with a little + year: 2016 + +- speaker: Ronan Berder + twitter_handle: hunvreus + youtube_id: TteAQq25_Ns + topic: Designing fast websites with Jekyll + year: 2016 + +- speaker: David Von Lehman + twitter_handle: davidvlsea + youtube_id: wMlPlKCZfEk + topic: Continuous deployment of Jekyll sites powered by Docker + year: 2016 + +- speaker: David Jones + twitter_handle: d_jones + youtube_id: 4XxYQ7efk0E + topic: Building our agency site with Jekyll + year: 2016 + +- speaker: Scott Hewitt + twitter_handle: scotthewitt + youtube_id: qSd3pXQaPsE + topic: Jekyll For Every Case + year: 2016 + +- speaker: Tim Carry + twitter_handle: pixelastic + youtube_id: ivMML1J4ABY + topic: Algolia search on Jekyll sites + year: 2016 + +- speaker: Nils Borchers + twitter_handle: nilsbo + youtube_id: DtNMjuv6Rbo + topic: Building a living brand guide with Jekyll and Hologram + year: 2016 + +- speaker: Mike Neumegen + twitter_handle: mikeneumegen + youtube_id: rJ5EhVmTR7I + topic: Learning resources for the Jekyll community + year: 2016 + +- speaker: Oliver Pattison + twitter_handle: olivermakes + youtube_id: BIf6oNpGl74 + topic: Responsive srcset images with imgix + year: 2016 + +- speaker: Michael Lee + twitter_handle: michaelsoolee + youtube_id: F4bJRLEvXIc + topic: Jekyll, Your Website's Baseplate + year: 2016 + +- speaker: Paul Webb + twitter_handle: NetOpWibby + youtube_id: BRB5DgAE5nM + topic: Deploy Jekyll Like A Boss + year: 2016 + +- speaker: Tom Johnson + twitter_handle: tomjohnson + youtube_id: nq1AUB72GCQ + topic: Overcoming challenges in using Jekyll for documentation projects + year: 2016 + +- speaker: Pieter Roozen + twitter_handle: Pieter_Roozen + youtube_id: moQP0SqEPsw + topic: Jekyll As An API Endpoint + year: 2019 + +- speaker: Chen Hui Jing + twitter_handle: hj_chen + youtube_id: CERXESTZ5w4 + topic: Why I love Jekyll Data Files + year: 2019 + +- speaker: Chris Ferdinandi + twitter_handle: ChrisFerdinandi + youtube_id: vR1aI_kQ4-A + topic: The Lean Web + year: 2019 + +- speaker: Catherine Roebuck + twitter_handle: + youtube_id: zTAP1m1BaDM + topic: Jekyll For City Government + year: 2019 + +- speaker: Joost van der Schee + twitter_handle: jhvanderschee + youtube_id: ztJJ1GSlYgI + topic: "Jekyll Codex - Jekyll for front-end developers" + year: 2019 + +- speaker: Matthew Loberg + twitter_handle: mloberg + youtube_id: 6eiAjAtSGqw + topic: Leverage AWS S3 And CloudFront To Deploy Blazing Fast Jekyll Sites + year: 2019 + +- speaker: George Phillips + twitter_handle: gphillips_nz + youtube_id: nEvdOwFJBVc + topic: Structuring Jekyll Sites For Enterprise Design Systems + year: 2019 diff --git a/docs/_data/primary_nav.yml b/docs/_data/primary_nav.yml new file mode 100644 index 0000000..02ff6e9 --- /dev/null +++ b/docs/_data/primary_nav.yml @@ -0,0 +1,15 @@ +- title: Home + link: / + show_on_mobile: true +- title: Docs + link: /docs/ + show_on_mobile: true +- title: Resources + link: /resources/ + show_on_mobile: true +- title: Showcase + link: /showcase/ + show_on_mobile: false +- title: News + link: /news/ + show_on_mobile: true diff --git a/docs/_data/ruby.yml b/docs/_data/ruby.yml new file mode 100644 index 0000000..3655def --- /dev/null +++ b/docs/_data/ruby.yml @@ -0,0 +1,3 @@ +min_version: 2.5.0 +current_version: 3.1.2 +current_version_output: ruby 3.1.2p20 (2022-04-12 revision 4491bb740a) diff --git a/docs/_data/showcase.yml b/docs/_data/showcase.yml new file mode 100644 index 0000000..a109666 --- /dev/null +++ b/docs/_data/showcase.yml @@ -0,0 +1,315 @@ +- name: Tom Preston Werner Blog + url: https://tom.preston-werner.com/ + image: tom-preston-werner.png + categories: + - personal + - blog + +# - name: White House Social and Behavioral Sciences Team +# url: https://sbst.gov/ +# image: sbst.png +# categories: +# - government + +- name: SiteLeaf + url: https://siteleaf.com + image: siteleaf.png + categories: + - software + - marketing-site + +- name: CloudCannon + url: https://cloudcannon.com/ + image: cloudcannon.png + categories: + - software + - marketing-site + +- name: Vesterheim Norwegian-American Museum + url: https://vesterheim.org/ + image: vesterheim.png + categories: + - marketing-site + +- name: KOTN + url: https://kotn.com/ + image: kotn.png + categories: + - marketing-site + +- name: MvvmCross + url: https://www.mvvmcross.com/ + image: mvvm.png + categories: + - software + - marketing-site + +- name: Vidgrid + url: https://www.vidgrid.com/ + image: vidgrid.png + categories: + - software + - marketing-site + +- name: Bitcoin + url: https://bitcoin.org/en/ + image: bitcoin.png + categories: + - software + - marketing-site + +- name: Mapwize + url: https://www.mapwize.io/ + image: mapwize.png + categories: + - software + - marketing-site + +- name: Auth0 Blog + url: https://auth0.com/blog/ + image: auth0-blog.png + categories: + - software + - blog + +- name: Freedom of Information Act + url: https://www.foia.gov/ + image: foia-gov.png + categories: + - government + +- name: "Art & About Sydney" + url: https://www.artandabout.com.au/ + image: art-sydney.png + categories: + - government + +- name: Passbolt Help + url: https://help.passbolt.com/ + image: passbolt-help.png + categories: + - knowledgebase + +- name: We are COLLINS + url: https://www.wearecollins.com/ + image: collins.png + categories: + - agency + +- name: Lightburn + url: https://lightburn.co/ + image: lightburn.png + categories: + - agency + +- name: italia.it + url: https://developers.italia.it/ + image: italia-it.png + categories: + - community + +- name: Sydney New Years Eve + url: https://www.sydneynewyearseve.com/ + image: nsw.png + categories: + - government + +- name: Login.gov + url: https://login.gov/ + image: login-gov.png + categories: + - government + +- name: plainlanguage.gov + url: https://plainlanguage.gov/ + image: plainlanguage-gov.png + categories: + - government + +- name: U.S. Web Design Standards + url: https://standards.usa.gov/ + image: uswds.png + categories: + - government + +- name: Grantmaker Search + url: https://www.grantmakers.io/ + image: grantmakers.png + categories: + - marketing-site + +- name: Rehan Butt + url: https://rehanbutt.com/ + image: rehn.png + categories: + - personal + - portfolio + +- name: The Markdown Guide + url: https://www.markdownguide.org/ + image: markdown-guide.png + categories: + - knowledgebase + +- name: Probot + url: https://probot.github.io/ + image: probot.png + categories: + - documentation + +- name: Matt Grey + url: https://himatt.com/ + image: matt-grey.png + categories: + - personal + - portfolio + +- name: Lattice + url: https://latticehq.com/ + image: lattice.png + categories: + - software + - marketing-site + +- name: MailTape + url: https://www.mailta.pe/ + image: mailtape.png + categories: + - other + +- name: Digital Democracy + url: https://www.digital-democracy.org/ + image: digital-democracy.png + categories: + - other + +- name: HTML Reference + url: https://htmlreference.io/ + image: htmlreference.png + categories: + - documentation + +- name: CSS Reference + url: https://cssreference.io/ + image: cssreference.png + categories: + - documentation + +- name: Chain + url: https://chain.com/ + image: chain.png + categories: + - marketing-site + +- name: IBM MobileFirst Foundation + url: https://mobilefirstplatform.ibmcloud.com/ + image: ibm-mobile-foundation.png + categories: + - documentation + +- name: "18F" + url: https://18f.gsa.gov/ + image: 18f.png + categories: + - agency + - government + +- name: Development Seed + url: https://developmentseed.org/ + image: development-seed.png + categories: + - agency + +- name: Isomer - Singapore Government Static Websites + url: https://isomer.gov.sg/ + image: isomer.png + categories: + - government + +- name: French Government Digital Services + url: https://beta.gouv.fr/ + image: beta-gouv-fr.png + categories: + - government + +- name: Paris Call for Trust and Security in Cyberspace + url: https://pariscall.international/ + image: appel-de-paris.png + categories: + - government + +- name: GitHub On Demand Training + url: https://services.github.com/on-demand/ + image: github-learning-lab.png + categories: + - software + - knowledgebase + +- name: TwitchCon + url: https://www.twitchcon.com/ + image: twitchcon.png + categories: + - marketing-site + - conference + +- name: UN World Statistics + url: https://worldstatisticsday.org + image: world-statistics-day.png + categories: + - government + +- name: Netflix Devices + url: https://devices.netflix.com/en/ + image: netflix.png + categories: + - marketing-site + +- name: Twitch Developer Documentation + url: https://dev.twitch.tv/ + image: twitch-developers.png + categories: + - marketing-site + - documentation + +- name: Yeoman + url: https://yeoman.io/ + image: yeoman.png + categories: + - open-source + - marketing-site + +- name: Release Management Blog + url: https://release.mozilla.org/ + image: mozilla-release-blog.png + categories: + - software + - blog + +- name: frame.ai + url: https://frame.ai/ + image: frame-ai.png + categories: + - software + - marketing-site + +- name: Spotify for Developers + url: https://developer.spotify.com + image: spotify-developers.png + categories: + - marketing-site + - documentation + - software + +- name: Sketch + url: https://sketch.com/ + image: sketch.png + categories: + - software + - marketing-site + +- name: Ruby on Rails + url: https://rubyonrails.org/ + image: ruby-on-rails.png + categories: + - marketing-site + - documentation diff --git a/docs/_data/tutorials.yml b/docs/_data/tutorials.yml new file mode 100644 index 0000000..d0bdad2 --- /dev/null +++ b/docs/_data/tutorials.yml @@ -0,0 +1,14 @@ +- title: Tutorials + tutorials: + - home + - video-walkthroughs + - navigation + - orderofinterpretation + - custom-404-page + - convert-site-to-jekyll + - using-jekyll-with-bundler + - csv-to-table + +#- title: Another section +# tutorials: +# - sample diff --git a/docs/_docs/assets.md b/docs/_docs/assets.md new file mode 100644 index 0000000..2cba9c9 --- /dev/null +++ b/docs/_docs/assets.md @@ -0,0 +1,94 @@ +--- +title: Assets +permalink: /docs/assets/ +--- + +Jekyll provides built-in support for [Sass](https://sass-lang.com/) +and can work with [CoffeeScript](https://coffeescript.org/) via a Ruby gem. +In order to use them, you must first create a file with the proper extension +name (one of `.sass`, `.scss`, or `.coffee`) and +***start the file with two lines of triple dashes***, like this: + +```sass +--- +--- + +// start content +.my-definition + font-size: 1.2em +``` + +Jekyll treats these files the same as a regular page, in that the output file +will be placed in the same directory that it came from. For instance, if you +have a file named `css/styles.scss` in your site's source folder, Jekyll +will process it and put it in your site's destination folder under +`css/styles.css`. + +<div class="note info"> + <h5>Jekyll processes all Liquid filters and tags in asset files</h5> + <p>If you are using <a href="https://mustache.github.io">Mustache</a> + or another JavaScript templating language that conflicts with + the <a href="{{ '/docs/templates/' | relative_url }}">Liquid template syntax</a>, you + will need to place <code>{% raw %}</code> and + <code>{% endraw %}</code> tags around your code.</p> +</div> + +## Sass/SCSS + +Jekyll allows you to customize your Sass conversion in certain ways. + +Place all your partials in your `sass_dir`, which defaults to +`<source>/_sass`. Place your main SCSS or Sass files in the place you want +them to be in the output file, such as `<source>/css`. For an example, take +a look at [this example site using Sass support in Jekyll][example-sass]. + +If you are using Sass `@import` statements, you'll need to ensure that your +`sass_dir` is set to the base directory that contains your Sass files: + +```yaml +sass: + sass_dir: _sass +``` + +The Sass converter will default the `sass_dir` configuration option to +`_sass`. + +[example-sass]: https://github.com/jekyll/jekyll-sass-converter/tree/master/docs + +<div class="note info"> + <h5>The <code>sass_dir</code> is only used by Sass</h5> + <p> + + Note that the <code>sass_dir</code> becomes the load path for Sass imports, + nothing more. This means that Jekyll does not know about these files + directly. Any files here should not contain the empty front matter as + described above. If they do, they'll not be transformed as described above. This + folder should only contain imports. + + </p> +</div> + +You may also specify the output style with the `style` option in your +`_config.yml` file: + +```yaml +sass: + style: compressed +``` + +These are passed to Sass, so any output style options Sass supports are valid +here, too. + +For more information on Sass configuration options, see the [Sass configuration]({{ '/docs/configuration/sass/' | relative_url }}) docs. + +## Coffeescript + +To enable Coffeescript in Jekyll 3.0 and up you must + +* Install the `jekyll-coffeescript` gem +* Ensure that your `_config.yml` is up-to-date and includes the following: + +```yaml +plugins: + - jekyll-coffeescript +``` diff --git a/docs/_docs/code_of_conduct.md b/docs/_docs/code_of_conduct.md new file mode 100644 index 0000000..a0f5299 --- /dev/null +++ b/docs/_docs/code_of_conduct.md @@ -0,0 +1,82 @@ +--- +title: Code of Conduct +permalink: "/docs/code_of_conduct/" +note: This file is autogenerated. Edit /.github/CODE_OF_CONDUCT.markdown instead. +redirect_from: "/conduct/index.html" +editable: false +--- + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, sex characteristics, gender identity and expression, +level of experience, education, socio-economic status, nationality, personal +appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or + advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at [olivia@jekyllrb.com](mailto:olivia@jekyllrb.com). All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at [https://www.contributor-covenant.org/version/1/4/code-of-conduct.html](https://www.contributor-covenant.org/version/1/4/code-of-conduct.html) + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see +[https://www.contributor-covenant.org/faq](https://www.contributor-covenant.org/faq) diff --git a/docs/_docs/collections.md b/docs/_docs/collections.md new file mode 100644 index 0000000..7bc69e1 --- /dev/null +++ b/docs/_docs/collections.md @@ -0,0 +1,382 @@ +--- +title: Collections +permalink: /docs/collections/ +--- + +Collections are a great way to group related content like members of a team or +talks at a conference. + +## Setup + +To use a Collection you first need to define it in your `_config.yml`. For +example here's a collection of staff members: + +```yaml +collections: + - staff_members +``` + +In this case `collections` is defined as a sequence (i.e., array) with no additional metadata defined for each collection. +You can optionally specify metadata for your collection by defining `collections` as a mapping (i.e., hashmap) instead of sequence, and then defining additional fields in it: + +```yaml +collections: + staff_members: + people: true +``` + +{: .note .info} +When defining a collection as a sequence, its pages will not be rendered by +default. To enable this, <code>output: true</code> must be specified on the +collection, which requires defining the collection as a mapping. For more +information, see the section <a href="#output">Output</a>. + +<div class="note"> + <h5>Gather your collections {%- include docs_version_badge.html version="3.7.0" -%}</h5> + + <p>You can optionally specify a directory to store all your collections in the same place with <code>collections_dir: my_collections</code>.</p> + + <p>Then Jekyll will look in <code>my_collections/_books</code> for the <code>books</code> collection, and + in <code>my_collections/_recipes</code> for the <code>recipes</code> collection.</p> +</div> + +<div class="note warning"> + <h5>Be sure to move drafts and posts into custom collections directory</h5> + + <p>If you specify a directory to store all your collections in the same place with <code>collections_dir: my_collections</code>, then you will need to move your <code>_drafts</code> and <code>_posts</code> directory to <code>my_collections/_drafts</code> and <code>my_collections/_posts</code>. Note that, the name of your collections directory cannot start with an underscore (`_`).</p> +</div> + +## Add content + +Create a corresponding folder (e.g. `<source>/_staff_members`) and add +documents. Front matter is processed if the front matter exists, and everything +after the front matter is pushed into the document's `content` attribute. If no front +matter is provided, Jekyll will consider it to be a [static file]({{ '/docs/static-files/' | relative_url }}) +and the contents will not undergo further processing. If front matter is provided, +Jekyll will process the file contents into the expected output. + +Regardless of whether front matter exists or not, Jekyll will write to the destination +directory (e.g. `_site`) only if `output: true` has been set in the collection's +metadata. + +For example here's how you would add a staff member to the collection set above. +The filename is `./_staff_members/jane.md` with the following content: + +```markdown +--- +name: Jane Doe +position: Developer +--- +Jane has worked on Jekyll for the past *five years*. +``` + +<em> + Do note that in spite of being considered as a collection internally, the above + doesn't apply to [posts](/docs/posts/). Posts with a valid filename format will be + marked for processing even if they do not contain front matter. +</em> + +<div class="note info"> + <h5>Be sure to name your directories correctly</h5> + <p> +The folder must be named identically to the collection you defined in +your <code>_config.yml</code> file, with the addition of the preceding <code>_</code> character. + </p> +</div> + +## Output + +Now you can iterate over `site.staff_members` on a page and output the content +for each staff member. Similar to posts, the body of the document is accessed +using the `content` variable: + +{% raw %} +```liquid +{% for staff_member in site.staff_members %} + <h2>{{ staff_member.name }} - {{ staff_member.position }}</h2> + <p>{{ staff_member.content | markdownify }}</p> +{% endfor %} +``` +{% endraw %} + +If you'd like Jekyll to create a rendered page for each document in your +collection, you can set the `output` key to `true` in your collection +metadata in `_config.yml`: + +```yaml +collections: + staff_members: + output: true +``` + +You can link to the generated page using the `url` attribute: + +{% raw %} +```liquid +{% for staff_member in site.staff_members %} + <h2> + <a href="{{ staff_member.url }}"> + {{ staff_member.name }} - {{ staff_member.position }} + </a> + </h2> + <p>{{ staff_member.content | markdownify }}</p> +{% endfor %} +``` +{% endraw %} + +## Permalinks + +There are special [permalink variables for collections]({{ '/docs/permalinks/#collections' | relative_url }}) to +help you control the output url for the entire collection. + +## Custom Sorting of Documents {%- include docs_version_badge.html version="4.0" -%} +{: #custom-sorting-of-documents} + +By default, two documents in a collection are sorted by their `date` attribute when both of them have the `date` key in their front matter. However, if either or both documents do not have the `date` key in their front matter, they are sorted by their respective paths. + +You can control this sorting via the collection's metadata. + +### Sort By Front Matter Key + +Documents can be sorted based on a front matter key by setting a `sort_by` metadata to the front matter key string. For example, +to sort a collection of tutorials based on key `lesson`, the configuration would be: + +```yaml +collections: + tutorials: + sort_by: lesson +``` + +The documents are arranged in the increasing order of the key's value. If a document does not have the front matter key defined +then that document is placed immediately after sorted documents. When multiple documents do not have the front matter key defined, +those documents are sorted by their dates or paths and then placed immediately after the sorted documents. + +### Manually Ordering Documents + +You can also manually order the documents by setting an `order` metadata with **the filenames listed** in the desired order. +For example, a collection of tutorials would be configured as: + +```yaml +collections: + tutorials: + order: + - hello-world.md + - introduction.md + - basic-concepts.md + - advanced-concepts.md +``` + +Any documents with filenames that do not match the list entry simply gets placed after the rearranged documents. If a document is +nested under subdirectories, include them in entries as well: + +```yaml +collections: + tutorials: + order: + - hello-world.md + - introduction.md + - concepts/basics.md + - concepts/advanced.md +``` + +If both metadata keys have been defined properly, `order` list takes precedence. + +## Liquid Attributes + +### Collections + +Collections are also available under `site.collections`, with the metadata +you specified in your `_config.yml` (if present) and the following information: + +<div class="mobile-side-scroller"> +<table> + <thead> + <tr> + <th>Variable</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td> + <p><code>label</code></p> + </td> + <td> + <p> + The name of your collection, e.g. <code>my_collection</code>. + </p> + </td> + </tr> + <tr> + <td> + <p><code>docs</code></p> + </td> + <td> + <p> + An array of <a href="#documents">documents</a>. + </p> + </td> + </tr> + <tr> + <td> + <p><code>files</code></p> + </td> + <td> + <p> + An array of static files in the collection. + </p> + </td> + </tr> + <tr> + <td> + <p><code>relative_directory</code></p> + </td> + <td> + <p> + The path to the collection's source directory, relative to the site + source. + </p> + </td> + </tr> + <tr> + <td> + <p><code>directory</code></p> + </td> + <td> + <p> + The full path to the collections's source directory. + </p> + </td> + </tr> + <tr> + <td> + <p><code>output</code></p> + </td> + <td> + <p> + Whether the collection's documents will be output as individual + files. + </p> + </td> + </tr> + </tbody> +</table> +</div> + +<div class="note info"> + <h5>A Hard-Coded Collection</h5> + <p>In addition to any collections you create yourself, the + <code>posts</code> collection is hard-coded into Jekyll. It exists whether + you have a <code>_posts</code> directory or not. This is something to note + when iterating through <code>site.collections</code> as you may need to + filter it out.</p> + <p>You may wish to use filters to find your collection: + <code>{% raw %}{{ site.collections | where: "label", "myCollection" | first }}{% endraw %}</code></p> +</div> + +<div class="note info"> + <h5>Collections and Time</h5> + <p>Except for documents in hard-coded default collection <code>posts</code>, all documents in collections + you create, are accessible via Liquid irrespective of their assigned date, if any, and therefore renderable. + </p> + <p>Documents are attempted to be written to disk only if the concerned collection + metadata has <code>output: true</code>. Additionally, future-dated documents are only written if + <code>site.future</code> <em>is also true</em>. + </p> + <p>More fine-grained control over documents being written to disk can be exercised by setting + <code>published: false</code> (<em><code>true</code> by default</em>) in the document's front matter. + </p> +</div> + +### Documents + +In addition to any front matter provided in the document's corresponding +file, each document has the following attributes: + +<div class="mobile-side-scroller"> +<table> + <thead> + <tr> + <th>Variable</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td> + <p><code>content</code></p> + </td> + <td> + <p> + The (unrendered) content of the document. If no front matter is + provided, Jekyll will not generate the file in your collection. If + front matter is used, then this is all the contents of the file + after the terminating + `---` of the front matter. + </p> + </td> + </tr> + <tr> + <td> + <p><code>output</code></p> + </td> + <td> + <p> + The rendered output of the document, based on the + <code>content</code>. + </p> + </td> + </tr> + <tr> + <td> + <p><code>path</code></p> + </td> + <td> + <p> + The full path to the document's source file. + </p> + </td> + </tr> + <tr> + <td> + <p><code>relative_path</code></p> + </td> + <td> + <p> + The path to the document's source file relative to the site source. + </p> + </td> + </tr> + <tr> + <td> + <p><code>url</code></p> + </td> + <td> + <p> + The URL of the rendered collection. The file is only written to the destination when the collection to which it belongs has <code>output: true</code> in the site's configuration. + </p> + </td> + </tr> + <tr> + <td> + <p><code>collection</code></p> + </td> + <td> + <p> + The name of the document's collection. + </p> + </td> + </tr> + <tr> + <td> + <p><code>date</code></p> + </td> + <td> + <p> + The date of the document's collection. + </p> + </td> + </tr> + </tbody> +</table> +</div> diff --git a/docs/_docs/community/bug.md b/docs/_docs/community/bug.md new file mode 100644 index 0000000..06d4ab5 --- /dev/null +++ b/docs/_docs/community/bug.md @@ -0,0 +1,8 @@ +--- +title: Report a bug +permalink: "/docs/community/bug/" +--- + +If you think you've found a bug within a Jekyll plugin, open an issue in that plugin's repository — First [look for the plugin on rubygems](https://rubygems.org/) then click on the `Homepage` link to access the plugin repository. + +If you think you've found a bug within Jekyll itself, [open an issue](https://github.com/jekyll/jekyll/issues/new). diff --git a/docs/_docs/community/community.md b/docs/_docs/community/community.md new file mode 100644 index 0000000..59d365a --- /dev/null +++ b/docs/_docs/community/community.md @@ -0,0 +1,44 @@ +--- +title: Community +permalink: /docs/community/ +redirect_from: "/help/index.html" +--- + +## Jekyll Contributor Code of Conduct + +As contributors and maintainers of this project, and in the interest of fostering an open and welcoming community, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities. + +Read the full [code of conduct]({{ '/docs/conduct/' | relative_url }}) + +## Reporting Security Vulnerabilities + +Find something in our codebase that could be exploited by malicious elements? + +Consult our [Security Policy]({{ '/docs/security/' | relative_url }}) to see if a product version is considered *outdated* and how to report +the situation responsibly. + +## Where to get support + +If you're looking for support for Jekyll, there are a lot of options: + +* Read the [Jekyll Documentation]({{ '/docs/' | relative_url }}) +* If you have a question about using Jekyll, start a discussion on the [Jekyll Forum](https://talk.jekyllrb.com/) or [StackOverflow](https://stackoverflow.com/questions/tagged/jekyll) +* Chat with Jekyllers — Join our [Gitter channel](https://gitter.im/jekyll/jekyll) or our IRC channel #jekyll on [Libera](irc://irc.libera.chat/#jekyll). + +There are a bunch of helpful community members on these services who are willing to point you in the right direction. + +**Reminder: Jekyll's issue tracker is not a support forum.** + +## Ways to contribute + +* [How to Contribute]({{ '/docs/contributing/' | relative_url }}) +* [How to file a bug]({{ '/docs/community/bug/' | relative_url }}) +* [Guide for maintaining Jekyll]({{ '/docs/maintaining/' | relative_url }}) + +## Jekyllconf + +[Watch videos]({{ '/jekyllconf/' | relative_url }}) from members of the Jekyll community speak about interesting use cases, tricks they’ve learned or meta Jekyll topics. + +## Jekyll on Twitter + +The [official Jekyll Twitter account](https://twitter.com/jekyllrb). diff --git a/docs/_docs/conduct.md b/docs/_docs/conduct.md new file mode 100644 index 0000000..05f6b01 --- /dev/null +++ b/docs/_docs/conduct.md @@ -0,0 +1,82 @@ +--- +title: Code of Conduct +permalink: "/docs/conduct/" +note: This file is autogenerated. Edit /CONDUCT.markdown instead. +redirect_from: "/conduct/index.html" +editable: false +--- + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, sex characteristics, gender identity and expression, +level of experience, education, socio-economic status, nationality, personal +appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or + advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at [olivia@jekyllrb.com](mailto:olivia@jekyllrb.com). All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at [https://www.contributor-covenant.org/version/1/4/code-of-conduct.html](https://www.contributor-covenant.org/version/1/4/code-of-conduct.html) + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see +[https://www.contributor-covenant.org/faq](https://www.contributor-covenant.org/faq) diff --git a/docs/_docs/configuration.md b/docs/_docs/configuration.md new file mode 100644 index 0000000..6cf6685 --- /dev/null +++ b/docs/_docs/configuration.md @@ -0,0 +1,19 @@ +--- +title: Configuration +permalink: /docs/configuration/ +--- + +Jekyll gives you a lot of flexibility to customize how it builds your site. These +options can either be specified in a `_config.yml` or `_config.toml` file placed +in your site’s root directory, or can be specified as flags for the `jekyll` +executable in the terminal. + +* [Configuration Options]({{ '/docs/configuration/options/' | relative_url }}) +* [Default Configuration]({{ '/docs/configuration/default/' | relative_url }}) +* [Front Matter Defaults]({{ '/docs/configuration/front-matter-defaults/' | relative_url }}) +* [Environments]({{ '/docs/configuration/environments/' | relative_url }}) +* [Markdown Options]({{ '/docs/configuration/markdown/' | relative_url }}) +* [Liquid Options]({{ '/docs/configuration/liquid/' | relative_url }}) +* [Sass/SCSS Options]({{ '/docs/configuration/sass/' | relative_url }}) +* [Webrick Options]({{ '/docs/configuration/webrick/' | relative_url }}) +* [Incremental Regeneration]({{ '/docs/configuration/incremental-regeneration/' | relative_url }}) diff --git a/docs/_docs/configuration/default.md b/docs/_docs/configuration/default.md new file mode 100644 index 0000000..e96c507 --- /dev/null +++ b/docs/_docs/configuration/default.md @@ -0,0 +1,89 @@ +--- +title: Default Configuration +permalink: "/docs/configuration/default/" +--- + +Jekyll runs with the following configuration options by default. Alternative +settings for these options can be explicitly specified in the configuration +file or on the command-line. + +<div class="note info"> + <h5>Be aware of directory paths</h5> + <p> + In general, make directory path values in configuration keys like <code>plugins_dir</code> relative to the current working directory, not the site source. The <code>sass</code> configuration key is an exception, where values must be relative to the site source. + </p> +</div> + +```yaml +# Where things are +source : . +destination : ./_site +collections_dir : . +plugins_dir : _plugins # takes an array of strings and loads plugins in that order +layouts_dir : _layouts +data_dir : _data +includes_dir : _includes +sass: + sass_dir: _sass +collections: + posts: + output : true + +# Handling Reading +safe : false +include : [".htaccess"] +exclude : ["Gemfile", "Gemfile.lock", "node_modules", "vendor/bundle/", "vendor/cache/", "vendor/gems/", "vendor/ruby/"] +keep_files : [".git", ".svn"] +encoding : "utf-8" +markdown_ext : "markdown,mkdown,mkdn,mkd,md" +strict_front_matter : false + +# Filtering Content +show_drafts : null +limit_posts : 0 +future : false +unpublished : false + +# Plugins +whitelist : [] +plugins : [] + +# Conversion +markdown : kramdown +highlighter : rouge +lsi : false +excerpt_separator : "\n\n" +incremental : false + +# Serving +detach : false +port : 4000 +host : 127.0.0.1 +baseurl : "" # does not include hostname +show_dir_listing : false + +# Outputting +permalink : date +paginate_path : /page:num +timezone : null + +quiet : false +verbose : false +defaults : [] + +liquid: + error_mode : warn + strict_filters : false + strict_variables : false + +# Markdown Processors +kramdown: + auto_ids : true + entity_output : as_char + toc_levels : [1, 2, 3, 4, 5, 6] + smart_quotes : lsquo,rsquo,ldquo,rdquo + input : GFM + hard_wrap : false + footnote_nr : 1 + show_warnings : false +``` diff --git a/docs/_docs/configuration/environments.md b/docs/_docs/configuration/environments.md new file mode 100644 index 0000000..6b06c84 --- /dev/null +++ b/docs/_docs/configuration/environments.md @@ -0,0 +1,49 @@ +--- +title: Environments +permalink: "/docs/configuration/environments/" +--- +In the `build` (or `serve`) arguments, you can specify a Jekyll environment +and value. The build will then apply this value in any conditional statements +in your content. + +For example, suppose you set this conditional statement in your code: + +{% raw %} +```liquid +{% if jekyll.environment == "production" %} + {% include disqus.html %} +{% endif %} +``` +{% endraw %} + +When you build your Jekyll site, the content inside the `if` statement won't be +run unless you also specify a `production` environment in the build command, +like this: + +```sh +JEKYLL_ENV=production jekyll build +``` + +Specifying an environment value allows you to make certain content available +only within specific environments. + +The default value for `JEKYLL_ENV` is `development`. Therefore if you omit +`JEKYLL_ENV` from the build arguments, the default value will be +`JEKYLL_ENV=development`. Any content inside +{% raw %}`{% if jekyll.environment == "development" %}`{% endraw %} tags will +automatically appear in the build. + +Your environment values can be anything you want (not just `development` or +`production`). Some elements you might want to hide in development +environments include Disqus comment forms or Google Analytics. Conversely, +you might want to expose an "Edit me in GitHub" button in a development +environment but not include it in production environments. + +By specifying the option in the build command, you avoid having to change +values in your configuration files when moving from one environment to another. + +{: .note} +To switch part of your config settings depending on the environment, use the +<a href="{{ '/docs/configuration/options/#build-command-options' | relative_url }}">build command option</a>, +for example <code>--config _config.yml,_config_development.yml</code>. Settings +in later files override settings in earlier files. diff --git a/docs/_docs/configuration/front-matter-defaults.md b/docs/_docs/configuration/front-matter-defaults.md new file mode 100644 index 0000000..9f0aca8 --- /dev/null +++ b/docs/_docs/configuration/front-matter-defaults.md @@ -0,0 +1,153 @@ +--- +title: Front Matter Defaults +permalink: "/docs/configuration/front-matter-defaults/" +--- + +Using [front matter](/docs/front-matter/) is one way that you can specify configuration in the pages and posts for your site. Setting things like a default layout, or customizing the title, or specifying a more precise date/time for the post can all be added to your page or post front matter. + +Often times, you will find that you are repeating a lot of configuration options. Setting the same layout in each file, adding the same category - or categories - to a post, etc. You can even add custom variables like author names, which might be the same for the majority of posts on your blog. + +Instead of repeating this configuration each time you create a new post or page, Jekyll provides a way to set these defaults in the site configuration. To do this, you can specify site-wide defaults using the `defaults` key in the `_config.yml` file in your project's root directory. + +The `defaults` key holds an array of scope/values pairs that define what defaults should be set for a particular file path, and optionally, a file type in that path. + +Let's say that you want to add a default layout to all pages and posts in your site. You would add this to your `_config.yml` file: + +```yaml +defaults: + - + scope: + path: "" # an empty string here means all files in the project + values: + layout: "default" +``` + +<div class="note info"> + <h5>Stop and rerun `jekyll serve` command.</h5> + <p> + The <code>_config.yml</code> master configuration file contains global configurations + and variable definitions that are read once at execution time. Changes made to <code>_config.yml</code> + during automatic regeneration are not loaded until the next execution. + </p> + <p> + Note <a href="{{ '/docs/datafiles/' | relative_url }}">Data Files</a> are included and reloaded during automatic regeneration. + </p> +</div> + +Here, we are scoping the `values` to any file that exists in the path `scope`. Since the path is set as an empty string, it will apply to **all files** in your project. You probably don't want to set a layout on every file in your project - like css files, for example - so you can also specify a `type` value under the `scope` key. + +```yaml +defaults: + - + scope: + path: "" # an empty string here means all files in the project + type: "posts" # previously `post` in Jekyll 2.2. + values: + layout: "default" +``` + +Now, this will only set the layout for files where the type is `posts`. +The different types that are available to you are `pages`, `posts`, `drafts` or any collection in your site. While `type` is optional, you must specify a value for `path` when creating a `scope/values` pair. + +As mentioned earlier, you can set multiple scope/values pairs for `defaults`. + +```yaml +defaults: + - + scope: + path: "" + type: "pages" + values: + layout: "my-site" + - + scope: + path: "projects" + type: "pages" # previously `page` in Jekyll 2.2. + values: + layout: "project" # overrides previous default layout + author: "Mr. Hyde" +``` + +With these defaults, all pages would use the `my-site` layout. Any html files that exist in the `projects/` +folder will use the `project` layout, if it exists. Those files will also have the `page.author` +[liquid variable]({{ '/docs/variables/' | relative_url }}) set to `Mr. Hyde`. + +```yaml +collections: + my_collection: + output: true + +defaults: + - + scope: + path: "" + type: "my_collection" # a collection in your site, in plural form + values: + layout: "default" +``` + +In this example, the `layout` is set to `default` inside the +[collection]({{ '/docs/collections/' | relative_url }}) with the name `my_collection`. + +### Glob patterns in Front Matter defaults + +It is also possible to use glob patterns (currently limited to patterns that contain `*`) 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: + my_collection: + output: true + +defaults: + - + scope: + path: "section/*/special-page.html" + values: + layout: "specific-layout" +``` + +<div class="note warning"> + <h5>Globbing and Performance</h5> + <p> + Please note that globbing a path is known to have a negative effect on + performance and is currently not optimized, especially on Windows. + Globbing a path will increase your build times in proportion to the size + of the associated collection directory. + </p> +</div> + +### Precedence + +Jekyll will apply all of the configuration settings you specify in the `defaults` section of your `_config.yml` file. You can choose to override settings from other scope/values pair by specifying a more specific path for the scope. + +You can see that in the second to last example above. First, we set the default page layout to `my-site`. Then, using a more specific path, we set the default layout for pages in the `projects/` path to `project`. This can be done with any value that you would set in the page or post front matter. + +Finally, if you set defaults in the site configuration by adding a `defaults` section to your `_config.yml` file, you can override those settings in a post or page file. All you need to do is specify the settings in the post or page front matter. For example: + +```yaml +# In _config.yml +... +defaults: + - + scope: + path: "projects" + type: "pages" + values: + layout: "project" + author: "Mr. Hyde" + category: "project" +... +``` + +```yaml +# In projects/foo_project.md +--- +author: "John Smith" +layout: "foobar" +--- +The post text goes here... +``` + +The `projects/foo_project.md` would have the `layout` set to `foobar` instead +of `project` and the `author` set to `John Smith` instead of `Mr. Hyde` when +the site is built. diff --git a/docs/_docs/configuration/incremental-regeneration.md b/docs/_docs/configuration/incremental-regeneration.md new file mode 100644 index 0000000..b538c37 --- /dev/null +++ b/docs/_docs/configuration/incremental-regeneration.md @@ -0,0 +1,36 @@ +--- +title: Default Configuration +permalink: "/docs/configuration/incremental-regeneration/" +--- + +## Incremental Regeneration +<div class="note warning"> + <h5>Incremental regeneration is still an experimental feature</h5> + <p> + While incremental regeneration will work for the most common cases, it will + not work correctly in every scenario. Please be extremely cautious when + using the feature, and report any problems not listed below by + <a href="https://github.com/jekyll/jekyll/issues/new">opening an issue on GitHub</a>. + </p> +</div> + +Incremental regeneration helps shorten build times by only generating documents +and pages that were updated since the previous build. It does this by keeping +track of both file modification times and inter-document dependencies in the +`.jekyll-metadata` file. + +Under the current implementation, incremental regeneration will only generate a +document or page if either it, or one of its dependencies, is modified. Currently, +the only types of dependencies tracked are includes (using the +{% raw %}`{% include %}`{% endraw %} tag) and layouts. This means that plain +references to other documents (for example, the common case of iterating over +`site.posts` in a post listings page) will not be detected as a dependency. + +To remedy some of these shortfalls, putting `regenerate: true` in the front-matter +of a document will force Jekyll to regenerate it regardless of whether it has been +modified. Note that this will generate the specified document only; references +to other documents' contents will not work since they won't be re-rendered. + +Incremental regeneration can be enabled via the `--incremental` flag (`-I` for +short) from the command-line or by setting `incremental: true` in your +configuration file. diff --git a/docs/_docs/configuration/liquid.md b/docs/_docs/configuration/liquid.md new file mode 100644 index 0000000..5109645 --- /dev/null +++ b/docs/_docs/configuration/liquid.md @@ -0,0 +1,38 @@ +--- +title: Liquid Options +permalink: "/docs/configuration/liquid/" +--- +Liquid's response to errors can be configured by setting `error_mode`. The +options are + +- `lax` --- Ignore all errors. +- `warn` --- Output a warning on the console for each error. (default) +- `strict` --- Output an error message and stop the build. + +Within _config.yml, the default configuration is as follows: + +```yaml +liquid: + error_mode: warn +``` + +The above example depicts the "warn" value, which is already set by default- `error_mode: warn`. This results in any issues being called out during the build process however will continue to build if possible. + +You can also configure Liquid's renderer to catch non-assigned variables and +non-existing filters by setting `strict_variables` and / or `strict_filters` +to `true` respectively. {% include docs_version_badge.html version="3.8.0" %} + +Do note that while `error_mode` configures Liquid's parser, the `strict_variables` +and `strict_filters` options configure Liquid's renderer and are consequently +orthogonal. + +An example of setting these variables within _config.yml is as follows: + +```yaml +liquid: + error_mode: strict + strict_variables: true + strict_filters: true +``` + +Configuring as described above will stop your build/serve from happening and call out the offending error and halt. This is helpful when desiring to catch liquid-related issues by stopping the build or serve process and allowing you to deal with any issues. diff --git a/docs/_docs/configuration/markdown.md b/docs/_docs/configuration/markdown.md new file mode 100644 index 0000000..9cd65a3 --- /dev/null +++ b/docs/_docs/configuration/markdown.md @@ -0,0 +1,97 @@ +--- +title: Markdown Options +permalink: "/docs/configuration/markdown/" +--- +The various Markdown renderers supported by Jekyll sometimes have extra options +available. + +## Kramdown + +Kramdown is the default Markdown renderer for Jekyll, and often works well with no additional configuration. However, it does support many configuration options. + +### Kramdown Processor + +By default, Jekyll uses the [GitHub Flavored Markdown (GFM) processor](https://github.com/kramdown/parser-gfm) for Kramdown. (Specifying `input: GFM` is fine, but redundant.) GFM supports a couple additional Kramdown options, documented by [kramdown-parser-gfm](https://github.com/kramdown/parser-gfm). These options can be used directly in your Kramdown Jekyll config, like this: + +```yaml +kramdown: + gfm_quirks: [paragraph_end] +``` + +You can also change the processor used by Kramdown (as specified for the `input` key in the [Kramdown RDoc](https://kramdown.gettalong.org/rdoc/Kramdown/Document.html#method-c-new)). For example, to use the non-GFM Kramdown processor in Jekyll, add the following to your configuration. + +```yaml +kramdown: + input: Kramdown +``` + +Documentation for Kramdown parsers is available in the [Kramdown docs](https://kramdown.gettalong.org/parser/kramdown.html). If you use a Kramdown parser other than Kramdown or GFM, you'll need to add the gem for it. + +### Syntax Highlighting (CodeRay) + +To use the [CodeRay](http://coderay.rubychan.de/) syntax highlighter with Kramdown, you need to add a dependency on the `kramdown-syntax-coderay` gem. For example, `bundle add kramdown-syntax-coderay`. Then, you'll be able to specify CodeRay in your `syntax_highlighter` config: + +```yaml +kramdown: + syntax_highlighter: coderay +``` + +CodeRay supports several of its own configuration options, documented in the [kramdown-syntax-coderay docs](https://github.com/kramdown/syntax-coderay) which can be passed as `syntax_highlighter_opts` like this: + +```yaml +kramdown: + syntax_highlighter: coderay + syntax_highlighter_opts: + line_numbers: table + bold_every: 5 +``` + +### Advanced Kramdown Options + +Kramdown supports a variety of other relatively advanced options such as `header_offset` and `smart_quotes`. These are documented in the [Kramdown configuration documentation](https://kramdown.gettalong.org/options.html) and can be added to your Kramdown config like this: + +```yaml +kramdown: + header_offset: 2 +``` + +<div class="note warning"> + <h5>There are several unsupported kramdown options</h5> + <p> + Please note that Jekyll uses Kramdown's HTML converter. Kramdown options used only by other converters, such as <code>remove_block_html_tags</code> (used by the RemoveHtmlTags converter), will not work. + </p> +</div> + +## CommonMark + +[CommonMark](https://commonmark.org/) is a rationalized version of Markdown syntax, implemented in C and thus faster than default Kramdown implemented in Ruby. It [slightly differs](https://github.com/commonmark/CommonMark#differences-from-original-markdown) from original Markdown and does not support all the syntax elements implemented in Kramdown, like [Block Inline Attribute Lists](https://kramdown.gettalong.org/syntax.html#block-ials). + +It comes in two flavors: basic CommonMark with [jekyll-commonmark](https://github.com/jekyll/jekyll-commonmark) plugin and [GitHub Flavored Markdown supported by GitHub Pages](https://github.com/github/jekyll-commonmark-ghpages). + +### Custom Markdown Processors + +If you're interested in creating a custom markdown processor, you're in luck! Create a new class in the `Jekyll::Converters::Markdown` namespace: + +```ruby +class Jekyll::Converters::Markdown::MyCustomProcessor + def initialize(config) + require 'funky_markdown' + @config = config + rescue LoadError + STDERR.puts 'You are missing a library required for Markdown. Please run:' + STDERR.puts ' $ [sudo] gem install funky_markdown' + raise FatalException.new("Missing dependency: funky_markdown") + end + + def convert(content) + ::FunkyMarkdown.new(content).convert + end +end +``` + +Once you've created your class and have it properly set up either as a plugin +in the `_plugins` folder or as a gem, specify it in your `_config.yml`: + +```yaml +markdown: MyCustomProcessor +``` diff --git a/docs/_docs/configuration/options.md b/docs/_docs/configuration/options.md new file mode 100644 index 0000000..7713f0c --- /dev/null +++ b/docs/_docs/configuration/options.md @@ -0,0 +1,162 @@ +--- +title: Configuration Options +permalink: "/docs/configuration/options/" +--- + +The tables below list the available settings for Jekyll, and the various <code +class="option">options</code> (specified in the configuration file) and <code +class="flag">flags</code> (specified on the command-line) that control them. + +### Global Configuration + +<div class="mobile-side-scroller"> +<table> + <thead> + <tr> + <th>Setting</th> + <th> + <span class="option">Options</span> and <span class="flag">Flags</span> + </th> + </tr> + </thead> + <tbody> + {% for setting in site.data.config_options.global %} + <tr class="setting"> + <td> + <p class="name"> + <strong>{{ setting.name }}</strong> + {% if setting.version-badge %} + <span class="version-badge" title="Introduced in v{{ setting.version-badge }}">{{ setting.version-badge }}</span> + {% endif %} + </p> + <p class="description">{{ setting.description }}</p> + </td> + <td class="align-center"> + <p><code class="option">{{ setting.option }}</code></p> + {% if setting.flag %} + <p><code class="flag">{{ setting.flag }}</code></p> + {% endif %} + </td> + </tr> + {% endfor %} + <tr> + <td> + <p class='name'><strong>Defaults</strong></p> + <p class='description'> + Set defaults for <a href="{{ '/docs/front-matter/' | relative_url }}" title="front matter">front matter</a> + variables. + </p> + </td> + <td class='align-center'> + <p>see <a href="{{ '/docs/configuration/front-matter-defaults/' | relative_url }}" title="details">below</a></p> + </td> + </tr> + </tbody> +</table> +</div> + +<div class="note warning"> + <h5>Destination folders are cleaned on site builds</h5> + <p> + The contents of <code><destination></code> are automatically + cleaned, by default, when the site is built. Files or folders that are not + created by your site will be removed. Some files could be retained + by specifying them within the <code><keep_files></code> configuration directive. + </p> + <p> + Do not use an important location for <code><destination></code>; instead, use it as + a staging area and copy files from there to your web server. + </p> +</div> + +### Build Command Options + +<div class="mobile-side-scroller"> +<table> + <thead> + <tr> + <th>Setting</th> + <th><span class="option">Options</span> and <span class="flag">Flags</span></th> + </tr> + </thead> + <tbody> + {% for setting in site.data.config_options.build %} + <tr class="setting"> + <td> + <p class="name"> + <strong>{{ setting.name }}</strong> + {% if setting.version-badge %} + <span class="version-badge" title="Introduced in v{{ setting.version-badge }}">{{ setting.version-badge }}</span> + {% endif %} + </p> + <p class="description">{{ setting.description }}</p> + </td> + <td class="align-center"> + {% if setting.option %}<p><code class="option">{{ setting.option }}</code></p>{% endif %} + {% if setting.flag %}<p><code class="flag">{{ setting.flag }}</code></p>{% endif %} + </td> + </tr> + {% endfor %} + </tbody> +</table> +</div> + +### Serve Command Options + +In addition to the options below, the `serve` sub-command can accept any of the options +for the `build` sub-command, which are then applied to the site build which occurs right +before your site is served. + +<div class="mobile-side-scroller"> +<table> + <thead> + <tr> + <th>Setting</th> + <th><span class="option">Options</span> and <span class="flag">Flags</span></th> + </tr> + </thead> + <tbody> + {% for setting in site.data.config_options.serve %} + <tr class="setting"> + <td> + <p class="name"> + <strong>{{ setting.name }}</strong> + {% if setting.version-badge %} + <span class="version-badge" title="Introduced in v{{ setting.version-badge }}">{{ setting.version-badge }}</span> + {% endif %} + </p> + <p class="description">{{ setting.description }}</p> + </td> + <td class="align-center"> + {% if setting.option %} + <p><code class="option">{{ setting.option }}</code></p> + {% elsif setting.options %} + <p> + {% for option in setting.options %} + <code class="option">{{ option }}</code><br> + {% endfor %} + </p> + {% endif %} + {% if setting.flag %} + <p><code class="flag">{{ setting.flag }}</code></p> + {% elsif setting.flags %} + <p> + {% for flag in setting.flags %} + <code class="flag">{{ flag }}</code><br> + {% endfor %} + </p> + {% endif %} + </td> + </tr> + {% endfor %} + </tbody> +</table> +</div> + +<div class="note warning"> + <h5>Do not use tabs in configuration files</h5> + <p> + This will either lead to parsing errors, or Jekyll will revert to the + default settings. Use spaces instead. + </p> +</div> diff --git a/docs/_docs/configuration/sass.md b/docs/_docs/configuration/sass.md new file mode 100644 index 0000000..14c142f --- /dev/null +++ b/docs/_docs/configuration/sass.md @@ -0,0 +1,15 @@ +--- +title: Sass/SCSS Options +permalink: "/docs/configuration/sass/" +--- + +Jekyll comes bundled with [jekyll-sass-converter](https://github.com/jekyll/jekyll-sass-converter) plugin. By default, Jekyll will look for Sass partials in the `_sass` directory relative to your site's `source` directory. + +You can further configure the plugin by adding options to your Jekyll config under the `sass` attribute. See the [plugin's documentation](https://github.com/jekyll/jekyll-sass-converter#usage) for details and for its default values. + +<div class="note info"> + <p> + Note that directory paths specified in the <code>sass</code> configuration + are resolved relative to your site's <code>source</code>, not relative to the location of the <code>_config.yml</code> file. + </p> +</div> diff --git a/docs/_docs/configuration/webrick.md b/docs/_docs/configuration/webrick.md new file mode 100644 index 0000000..c760b85 --- /dev/null +++ b/docs/_docs/configuration/webrick.md @@ -0,0 +1,20 @@ +--- +title: WEBrick Options +permalink: "/docs/configuration/webrick/" +--- +You can provide custom headers for your site by adding them to `_config.yml` + +```yaml +# File: _config.yml +webrick: + headers: + My-Header: My-Value + My-Other-Header: My-Other-Value +``` + +### Defaults + +Jekyll provides by default `Content-Type` and `Cache-Control` response +headers: one dynamic in order to specify the nature of the data being served, +the other static in order to disable caching so that you don't have to fight +with Chrome's aggressive caching when you are in development mode. diff --git a/docs/_docs/continuous-integration/buddyworks.md b/docs/_docs/continuous-integration/buddyworks.md new file mode 100644 index 0000000..e342ca9 --- /dev/null +++ b/docs/_docs/continuous-integration/buddyworks.md @@ -0,0 +1,60 @@ +--- +title: "Buddy" +--- + +[Buddy][buddy-homepage] is a [Docker][docker-homepage]-based CI server that you can set up in 15-20 minutes to build, test, and deploy your Jekyll websites. It supports [GitHub][github-homepage], [Bitbucket][bitbucket-homepage], and [GitLab][gitlab-homepage] repositories, and can be installed on-premises or used in cloud. The following guide will show you how to set up a free environment to build and test your Jekyll project. + +[buddy-homepage]: https://buddy.works +[docker-homepage]: https://www.docker.com/ +[github-homepage]: https://github.com +[bitbucket-homepage]: https://bitbucket.org/ +[gitlab-homepage]: https://gitlab.com + +## 1. Getting started + +1. Log in at [https://buddy.works][buddy-homepage] with your GitHub/Bitbucket account or email +2. Choose your Git provider and select or push your Jekyll Project +3. Create a new pipeline and set the trigger mode to 'On every push' +4. Add and configure the Jekyll action and save the pipeline + +## 2. How it works + +Whenever you make a push to the selected branch, the Jekyll action runs `jekyll build` in an isolated [Jekyll Docker image][jekyll-docker-image]. The output is generated to the `/filesystem` directory, and can be further deployed to FTP/SFTP and IaaS services. You can add your own commands, install additional packages, attach services, and run Selenium tests, as well as add other actions down the pipeline, eg. a Slack notification or an SSH script that will restart your server. + +[jekyll-docker-image]: https://hub.docker.com/r/jekyll/jekyll/ + +## 3. Using YAML for configuration + +If you prefer configuration as code over GUI, you can generate a `buddy.yml` that will create a pipeline with the Jekyll action once you push it to the target branch: + +```yaml +- pipeline: "Build and Deploy Jekyll site" + trigger_mode: "ON_EVERY_PUSH" + ref_name: "master" + actions: + - action: "Execute: jekyll build" + type: "BUILD" + docker_image_name: "jekyll/jekyll" + docker_image_tag: "latest" + execute_commands: + - "chown jekyll:jekyll $WORKING_DIR" + - "jekyll build" +``` + +## 4. Setting up on-premises server + +The self-hosted version of Buddy can be installed on any type of server supporting Docker, including [Linux][bw-linux], [Mac][bw-mac], [AWS EC2][bw-aws-ec2], [DigitalOcean][bw-digitalocean], and [Microsoft Azure][bw-azure]. + +[bw-linux]: https://buddy.works/knowledge/standalone/installation-linux +[bw-mac]: https://buddy.works/knowledge/standalone/installation-mac-osx +[bw-aws-ec2]: https://buddy.works/knowledge/standalone/installation-amazon-ec2 +[bw-digitalocean]: https://buddy.works/knowledge/standalone/installation-digitalocean +[bw-azure]: https://buddy.works/knowledge/standalone/installation-azure + +## 5. Questions? + +This entire guide is open-source. Go ahead and [edit it][jekyll-docs-ci-buddy] if you want to expand it or have a fix or [ask for help][jekyll-help] if you run into trouble and need assistance. Buddy also has an [online community][buddy-forum] for help. + +[jekyll-docs-ci-buddy]: https://github.com/jekyll/jekyll/edit/master/docs/_docs/continuous-integration/buddyworks.md +[jekyll-help]: https://jekyllrb.com/help/ +[buddy-forum]: https://forum.buddy.works/ diff --git a/docs/_docs/continuous-integration/circleci.md b/docs/_docs/continuous-integration/circleci.md new file mode 100644 index 0000000..fb1275a --- /dev/null +++ b/docs/_docs/continuous-integration/circleci.md @@ -0,0 +1,137 @@ +--- +title: "CircleCI" +--- + +Building, testing, and deploying your Jekyll-generated website can quickly be done with [CircleCI][0], a continuous integration & delivery tool. CircleCI supports [GitHub][1] and [Bitbucket][2], and you can get started for free using an open-source or private repository. + +[0]: https://circleci.com/ +[1]: https://github.com/ +[2]: https://bitbucket.org/ + +## 1. Follow Your Project on CircleCI + +To start building your project on CircleCI, all you need to do is 'follow' your project from CircleCI's website: + +1. Visit the 'Add Projects' page +1. From the GitHub or Bitbucket tab on the left, choose a user or organization. +1. Find your project in the list and click 'Build project' on the right. +1. The first build will start on its own. You can start telling CircleCI how to build your project by creating a [.circleci/config.yml][3] file in the root of your repository. + +[3]: https://circleci.com/docs/2.0/configuration-reference/ + +## 2. Dependencies + +The easiest way to manage dependencies for a Jekyll project (with or without CircleCI) is via a [Gemfile][4]. You'd want to have Jekyll, any Jekyll plugins, [HTML Proofer](#html-proofer), and any other gems that you are using in the `Gemfile`. Don't forget to version `Gemfile.lock` as well. Here's an example `Gemfile`: + +[4]: http://bundler.io/gemfile.html + +```ruby +source 'https://rubygems.org' + +ruby '2.7.4' + +gem 'jekyll' +gem 'html-proofer' +``` + +```yaml + - step: + run: bundle install +``` + +## 3. Testing + +The most basic test that can be run is seeing if `jekyll build` actually works. This is a blocker, a dependency if you will, for other tests you might run on the generate site. So we'll run Jekyll, via Bundler, in the `dependencies` phase. + +```yaml + - step: + run: bundle exec jekyll build +``` + +### HTML Proofer + +With your site built, it's useful to run tests to check for valid HTML, broken links, etc. There's a few tools out there but [HTML Proofer][5] is popular amongst Jekyll users. We'll run it in the `test` phase with a few preferred flags. Check out the `html-proofer` [README][6] for all available flags, or run `htmlproofer --help` locally. + +[5]: https://github.com/gjtorikian/html-proofer +[6]: https://github.com/gjtorikian/html-proofer/blob/master/README.md#configuration + +```yaml + - step: + run: bundle exec htmlproofer ./_site --check-html --disable-external +``` + +## Complete Example .circleci/config.yml File + +The example `.circleci/config.yml` below demonstrates how to +deploy your Jekyll project to AWS. In order for this to work you would first have to set the +`S3_BUCKET_NAME` [environment variable](https://circleci.com/docs/2.0/env-vars/). + +```yaml +workflows: + test-deploy: + jobs: + - build + - deploy: + requires: + - build + filters: + branches: + only: master +version: 2.1 +jobs: + build: + docker: + - image: cimg/ruby:2.7.4 + environment: + BUNDLE_PATH: ~/repo/vendor/bundle + steps: + - checkout + - restore_cache: + keys: + - rubygems-v1-{% raw %}{{ checksum "Gemfile.lock" }}{% endraw %} + - rubygems-v1-fallback + - run: + name: Bundle Install + command: bundle check || bundle install + - save_cache: + key: rubygems-v1-{% raw %}{{ checksum "Gemfile.lock" }}{% endraw %} + paths: + - vendor/bundle + - run: + name: Jekyll build + command: bundle exec jekyll build + - run: + name: HTMLProofer tests + command: | + bundle exec htmlproofer ./_site \ + --allow-hash-href \ + --check-favicon \ + --check-html \ + --disable-external + - persist_to_workspace: + root: ./ + paths: + - _site + deploy: + docker: + - image: cimg/python:3.9.1 + environment: + S3_BUCKET_NAME: <<YOUR BUCKET NAME HERE>> + steps: + - attach_workspace: + at: ./ + - run: + name: Install AWS CLI + command: pip install awscli --upgrade --user + - run: + name: Upload to s3 + command: ~/.local/bin/aws s3 sync ./_site s3://$S3_BUCKET_NAME/ --delete --acl public-read +``` + +## Questions? + +This entire guide is open-source. Go ahead and [edit it][7] if you have a fix or [ask for help][8] if you run into trouble and need some help. CircleCI also has an [online community][9] for help. + +[7]: https://github.com/jekyll/jekyll/edit/master/docs/_docs/continuous-integration/circleci.md +[8]: https://jekyllrb.com/help/ +[9]: https://discuss.circleci.com diff --git a/docs/_docs/continuous-integration/github-actions.md b/docs/_docs/continuous-integration/github-actions.md new file mode 100644 index 0000000..392bde8 --- /dev/null +++ b/docs/_docs/continuous-integration/github-actions.md @@ -0,0 +1,232 @@ +--- +title: GitHub Actions +--- + +When building a Jekyll site with GitHub Pages, the standard flow is restricted for security reasons +and to make it simpler to get a site setup. For more control over the build and still host the site +with GitHub Pages you can use GitHub Actions. + +## Advantages of using Actions + +### Control over gemset + +- **Jekyll version** --- Instead of using the currently enabled version at `3.9.0`, you can use any + version of Jekyll you want. For example `{{site.version}}`, or point directly to the repository. +- **Plugins** --- You can use any Jekyll plugins irrespective of them being on the + [supported versions][ghp-whitelist] list, even `*.rb` files placed in the `_plugins` directory + of your site. +- **Themes** --- While using a custom theme is possible without Actions, it is now simpler. + +### Workflow Management + +- **Customization** --- By creating a workflow file to run Actions, you can specify custom build + steps, use environment variables. +- **Logging** --- The build log is visible and can be tweaked to be verbose, so it is much easier to + debug errors using Actions. + +## Workspace setup + +The first and foremost requirement is a Jekyll project hosted at GitHub. Choose an existing Jekyll +project or follow the [quickstart]({{ '/docs/' | relative_url }}) and push the repository to GitHub +if it is not hosted there already. + +We're only going to cover builds from the `main` branch in this page. Therefore, ensure that you +are working on the `main` branch. If necessary, you may create it based on your default branch. +When the Action builds your site, the contents of the _destination_ directory will be automatically +pushed to the `gh-pages` branch with a commit, ready to be used for serving. + +{: .note .warning} +The Action we're using here will create (or reset an existing) `gh-pages` branch on every successful +deploy.<br/> So, if you have an existing `gh-pages` branch that is used to deploy your production +build, ensure to make a backup of the contents into a different branch so that you can rollback +easily if necessary. + +The Jekyll site we'll be using for the rest of this page initially consists of just a `_config.yml`, +an `index.md` page and a `Gemfile`. The contents are respectively: + +```yaml +# _config.yml + +title: "Jekyll Actions Demo" +``` + +{% raw %} + +```liquid +--- +--- + +Welcome to My Home Page + +{% assign date = '2020-04-13T10:20:00Z' %} + +- Original date - {{ date }} +- With timeago filter - {{ date | timeago }} +``` + +{% endraw %} + +```ruby +# Gemfile + +source 'https://rubygems.org' + +gem 'jekyll', '~> 4.2' + +group :jekyll_plugins do + gem 'jekyll-timeago', '~> 0.13.1' +end +``` + +{: .note .info} +The demo site uses Jekyll 4 and a [third-party plugin][timeago-plugin], both of which are currently +not whitelisted for use on GitHub pages. The plugin will allow us to describe how far back a date +was from today. e.g. If we give a date as `2016-03-23T10:20:00Z` and the current date is +`2020-04-13T10:20:00Z`, then the output would be `4 years and 3 weeks ago`. + +{: .note .info} +The action we're using takes care of installing the Ruby gems and dependencies. While that keeps +the setup simple for the user, one may encounter issues if they also check-in `Gemfile.lock` if it +was generated with an old version of Bundler. + +### Setting up the Action + +GitHub Actions are registered for a repository by using a YAML file inside the directory path +`.github/workflows` (note the dot at the start). For simplicity, here we use one of the +[Jekyll Actions](#external-links) to show you how to use the action. + +Create a **workflow file**, say `github-pages.yml`, using either the GitHub interface or by pushing +a YAML file to the workflow directory path manually. The base contents are: + +{% raw %} + +```yaml +name: Build and deploy Jekyll site to GitHub Pages + +on: + push: + branches: + - main # or master before October 2020 + +jobs: + github-pages: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/cache@v2 + with: + path: vendor/bundle + key: ${{ runner.os }}-gems-${{ hashFiles('**/Gemfile') }} + restore-keys: | + ${{ runner.os }}-gems- + - uses: helaili/jekyll-action@2.0.5 # Choose any one of the Jekyll Actions + with: # Some relative inputs of your action + token: ${{ secrets.GITHUB_TOKEN }} +``` + +{% endraw %} + +The above workflow can be explained as the following: + +- We trigger the build using **on.push** condition for `main` branch only --- this prevents + the Action from overwriting the `gh-pages` branch on any feature branch pushes. +- The **name** of the job matches our YAML filename: `github-pages`. +- The **checkout** action takes care of cloning your repository. +- The **cache** action is an optimization to avoid fetching and installing gems on every build. +- We specify our selected **action** and **version number** using `helaili/jekyll-action@2.0.5`, + this handles the build and deploy. You can choose any one of the Jekyll Actions that matches + your project and flavor from [GitHub Marketplace](https://github.com/marketplace?type=actions&query=jekyll+action). +- We set a reference to a secret **environment variable** for the action to use. The `GITHUB_TOKEN` + is a secret token automatically initialized at the start of every workflow run. + More information can be found in [GitHub documentation](https://docs.github.com/en/actions/security-guides/automatic-token-authentication#about-the-github_token-secret). + +Instead of using the **on.push** condition, you could trigger your build on a **schedule** by +using the [on.schedule] parameter. For example, here we build daily at midnight by specifying +**cron** syntax, which can be tested at the [crontab guru] site. + +```yaml +on: + schedule: + - cron: "0 0 * * *" +``` + +Note that this string must be quoted to prevent the asterisks from being evaluated incorrectly. + +[on.schedule]: https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions#onschedule +[crontab guru]: https://crontab.guru/ + +### Providing permissions + +At the start of each workflow run, GitHub automatically creates a unique `GITHUB_TOKEN` secret to use in +your workflow. You can use the `GITHUB_TOKEN` to authenticate in a workflow run. You can use the +`GITHUB_TOKEN` by using the standard syntax for referencing secrets: `${{ secrets.GITHUB_TOKEN }}`. +For more information, please read [GitHub's docs on token authentication][github-token-ref] + +[github-token-ref]: https://docs.github.com/en/actions/security-guides/automatic-token-authentication + +If you need a token that requires permissions that aren't available in the `GITHUB_TOKEN`, you can create +a Personal Access Token (PAT), and set it as a secret in your repository for this action to push to the +`gh-pages` branch: + +1. On your GitHub profile, under **Developer Settings**, go to the [Personal Access Tokens][tokens] + section. +2. **Create** a token. Give it a name like "GitHub Actions" and ensure it has permissions to + `public_repos` (or the entire `repo` scope for private repository) --- necessary for the action + to commit to the `gh-pages` branch. +3. **Copy** the token value. +4. Go to your repository's **Settings** and then the **Secrets** tab. +5. **Create** a token named `YOUR_CUSTOM_TOKEN` (_important_). Give it a value using the value copied + above. + +### Build and deploy + +On pushing any local changes onto `main`, the action will be triggered and the build will +**start**. + +To watch the progress and see any build errors, check on the build **status** using one of the +following approaches: + +- **View by commit** + - Go to the repository level view in GitHub. Under the most recent commit (near the top) you’ll + see a **status symbol** next to the commit message as a tick or _X_. Hover over it and click + the **details** link. +- **Actions tab** + - Go to the repository's Actions tab. Click on the `jekyll` workflow tab. + +If all goes well, all steps will be green and the built assets will now exist on the `gh-pages` +branch. + +On a successful build, GitHub Pages will **publish** the site stored on the repository `gh-pages` +branches. Note that you do not need to setup a `gh-pages` branch or enable GitHub Pages, as the +action will take care of this for you. +(For private repositories, you'll have to upgrade to a paid plan). + +To see the **live site**: + +1. Go to the **environment** tab on your repository. +2. Click **View Deployment** to see the deployed site URL. +3. View your site at the **URL**. Make sure the `timeago` filter works as expected. +4. Optionally **add** this URL to your repository's main page and to your `README.md`, to make it + easy for people to find. + +When you need to make further **changes** to the site, commit to `master` and push. The workflow +will build and deploy your site again. + +Be sure **not to edit** the `gh-pages` branch directly, as any changes will be lost on the next +successful deploy from the Action. + +## External links + +- [jekyll-actions] is an action available on the GitHub Marketplace and was used in this guide. +- [jekyll-actions-quickstart] is an unofficial repository that includes a live demo of the + `jekyll-actions` action. That project can be used as a template for making a new site. +- [jekyll-action-ts] is another action to build and publish Jekyll sites on GiHub Pages that includes HTML formatting options with Prettier and caching. +- [jekyll-deploy-action] is a GitHub Action to deploy the Jekyll site conveniently for GitHub Pages (An alternative action with better speed and compatibility). + +[ghp-whitelist]: https://pages.github.com/versions/ +[timeago-plugin]: https://rubygems.org/gems/jekyll-timeago +[tokens]: https://github.com/settings/tokens +[jekyll-actions]: https://github.com/marketplace/actions/jekyll-actions +[jekyll-actions-quickstart]: https://github.com/MichaelCurrin/jekyll-actions-quickstart +[jekyll-action-ts]: https://github.com/limjh16/jekyll-action-ts +[jekyll-deploy-action]: https://github.com/jeffreytse/jekyll-deploy-action diff --git a/docs/_docs/continuous-integration/razorops.md b/docs/_docs/continuous-integration/razorops.md new file mode 100644 index 0000000..fc92d33 --- /dev/null +++ b/docs/_docs/continuous-integration/razorops.md @@ -0,0 +1,61 @@ +--- +title: "Razorops" +--- + +[Razorops][razorops-homepage] is a complete container native CI/CD solution handling all aspects of the software lifecycle from the moment a commit is created until it is deployed to production. +Razorops has all the capabilities that you would expect from a CI/CD platform such as +1. Code compilation/build +2. Artifact packaging +3. Testing Automation(unit, integration, acceptance etc.) +4. Faster builds and shipping to production + +Razorops is a single solution that implements the whole pipeline from start to deployment. + +With [Razorops][razorops-homepage] you can set up your Jekyll websites project's build, test, and deploy steps just in 15 min. It supports [GitHub][github-homepage], [Bitbucket][bitbucket-homepage], and [GitLab][gitlab-homepage] repositories. The following guide will show you how to set up a free environment to build, test and deploy your Jekyll project. + +[razorops-homepage]: https://razorops.com/ +[docker-homepage]: https://www.docker.com/ +[github-homepage]: https://github.com +[bitbucket-homepage]: https://bitbucket.org/ +[gitlab-homepage]: https://gitlab.com +[deploy-s3]: https://razorops.com/blog/how-to-deploy-a-static-website-to-aws-s3-with-razorops-ci-cd/ + +## 1. Getting started + +1. Log in at [https://razorops.com/][razorops-homepage] with your GitHub/Bitbucket or Gitlab account +2. Create a pipeline, choose your Git provider and select your Jekyll Project +3. Add .razorops.yaml file in your root directory of your project +4. Add environment var and your deployment is ready +5. Add build and deployment steps as shown in this post [How to Deploy a Static Website to AWS S3 with Razorops CI/CD][deploy-s3] + +## 2. How it works + +Whenever you make a push to the selected branch, your steps auto runs as defined in .razorops.yaml file + +```yaml + tasks: + build-and-deploy: + steps: + - checkout + # commands to build jekyll website + - commands: + - bundle install + - JEKYLL_ENV=production bundle exec jekyll build + # Commands to upload static pages folder to AWS S3 or ftp + # Set AWS access key & secrets environment variables under + # Razorops dashboard project pipelines + - commands: + - aws s3 rm s3://$AWS_S3_BUCKET --recursive + - aws s3 cp _site s3://$AWS_S3_BUCKET --recursive + if: branch == 'main' + +``` + + + + Build step generates _site folder as Jekyll default and during deploy you will able to ship code to s3 or any ftp server you can define any command to ship your website code to server. + +Razorops is FREE for opensource projects, Try it Now +[https://razorops.com/][razorops-homepage] + + diff --git a/docs/_docs/continuous-integration/travis-ci.md b/docs/_docs/continuous-integration/travis-ci.md new file mode 100644 index 0000000..16e58a7 --- /dev/null +++ b/docs/_docs/continuous-integration/travis-ci.md @@ -0,0 +1,233 @@ +--- +title: "Travis CI" +--- + +You can test your website build against one or more versions of Ruby. +The following guide will show you how to set up a free build environment on +[Travis][travis], with [GitHub][github] integration for pull requests. + +[travis]: https://travis-ci.org/ +[github]: https://github.com/ + +## 1. Enabling Travis and GitHub + +To enable Travis builds for your GitHub repository: + +1. Go to your profile on travis-ci.org: https://travis-ci.org/profile/username +2. Find the repository for which you're interested in enabling builds. +3. Flick the repository switch on so that it turns blue. +4. Optionally configure the build by clicking on the gear icon. Further + configuration happens via your `.travis.yml` file. More details below. + +## 2. The Test Script + +The simplest test script runs `jekyll build` and ensures that Jekyll +doesn't fail to build the site. It doesn't check the resulting site, but it +does ensure things are built properly. + +When testing Jekyll output, there is no better tool than [html-proofer][html-proofer]. +This tool checks your resulting site to ensure all links and images exist. +Utilize it either with the convenient `htmlproofer` command-line executable, +or write a Ruby script which utilizes the gem. + +Save the commands you want to run and succeed in a file: `./script/cibuild` + +### The HTML Proofer Executable + +```bash +#!/usr/bin/env bash +set -e # halt script on error + +bundle exec jekyll build +bundle exec htmlproofer ./_site +``` + +Some options can be specified via command-line switches. Check out the +`html-proofer` README for more information about these switches, or run +`htmlproofer --help` locally. + +For example to avoid testing external sites, use this command: + +```sh +bundle exec htmlproofer ./_site --disable-external +``` + +### The HTML Proofer Library + +You can also invoke `html-proofer` in Ruby scripts (e.g. in a Rakefile): + +```ruby +#!/usr/bin/env ruby + +require 'html-proofer' +HTMLProofer.check_directory("./_site").run +``` + +Options are given as a second argument to `.new`, and are encoded in a +symbol-keyed Ruby Hash. For more information about the configuration options, +check out `html-proofer`'s README file. + +[html-proofer]: https://github.com/gjtorikian/html-proofer + +## 3. Configuring Your Travis Builds + +This file is used to configure your Travis builds. Because Jekyll is built +with Ruby and requires RubyGems to install, we use the Ruby language build +environment. Below is a sample `.travis.yml` file, followed by +an explanation of each line. + +**Note:** You will need a Gemfile as well, [Travis will automatically install](https://docs.travis-ci.com/user/languages/ruby/#Dependency-Management) the dependencies based on the referenced gems. Here is an example `Gemfile` with two referenced gems, "jekyll" and "html-proofer": + +```ruby +source "https://rubygems.org" + +gem "jekyll" +gem "html-proofer" +``` + +Your `.travis.yml` file should look like this: + +```yaml +language: ruby +rvm: + - 2.6.3 + +before_script: + - chmod +x ./script/cibuild # or do this locally and commit + +# Assume bundler is being used, therefore +# the `install` step will run `bundle install` by default. +script: ./script/cibuild + +# branch whitelist, only for GitHub Pages +branches: + only: + - gh-pages # test the gh-pages branch + - /pages-(.*)/ # test every branch which starts with "pages-" + +addons: + apt: + packages: + - libcurl4-openssl-dev + +cache: bundler # caching bundler gem packages will speed up build + +# Optional: disable email notifications about the outcome of your builds +notifications: + email: false +``` + +Ok, now for an explanation of each line: + +```yaml +language: ruby +``` + +This line tells Travis to use a Ruby build container. It gives your script +access to Bundler, RubyGems, and a Ruby runtime. + +```yaml +rvm: + - 2.6.3 +``` + +RVM is a popular Ruby Version Manager (like rbenv, chruby, etc). This +directive tells Travis the Ruby version to use when running your test +script. Use a [version which is pre-installed on the Travis build docker][5] +image to speed up the build. + +```yaml +before_script: + - chmod +x ./script/cibuild +``` + +The build script file needs to have the *executable* attribute set or +Travis will fail with a permission denied error. You can also run this +locally and commit the permissions directly, thus rendering this step +irrelevant. + +```yaml +script: ./script/cibuild +``` + +Travis allows you to run any arbitrary shell script to test your site. One +convention is to put all scripts for your project in the `script` +directory, and to call your test script `cibuild`. This line is completely +customizable. If your script won't change much, you can write your test +incantation here directly: + +```yaml +install: gem install jekyll html-proofer +script: jekyll build && htmlproofer ./_site +``` + +The `script` directive can be absolutely any valid shell command. + +```yaml +# branch whitelist, only for GitHub Pages +branches: + only: + - gh-pages # test the gh-pages branch + - /pages-(.*)/ # test every branch which starts with "pages-" +``` + +You want to ensure the Travis builds for your site are being run only on +the branch or branches which contain your site. One means of ensuring this +isolation is including a branch whitelist in your Travis configuration +file. By specifying the `gh-pages` branch, you will ensure the associated +test script (discussed above) is only executed on site branches. If you use +a pull request flow for proposing changes, you may wish to enforce a +convention for your builds such that all branches containing edits are +prefixed, exemplified above with the `/pages-(.*)/` regular expression. + +The `branches` directive is completely optional. Travis will build from every +push to any branch of your repo if leave it out. + +<div class="note warning"> + <h5>Be sure to exclude <code>vendor</code> from your + <code>_config.yml</code></h5> + <p>Travis bundles all gems in the <code>vendor</code> directory on its build + servers, which Jekyll will mistakenly read and explode on.</p> +</div> + +```yaml +exclude: [vendor] +``` + +To speed up the build, you should cache the gem packages created by `bundler`. +Travis has a pre-defined [cache strategy for this tool][6] which should have +all the default configs to do exactly that. + +```yaml +cache: bundler +``` + +Optionally, if you are not interested in the build email notifications you +can disable them with this configuration. Travis supports a wide array of +notification services, you may find [another one more useful (e.g. slack)][7]. + +```yaml +notifications: + email: false +``` + +### Troubleshooting + +**Travis error:** *"You are trying to install in deployment mode after changing +your Gemfile. Run bundle install elsewhere and add the updated Gemfile.lock +to version control."* + +**Workaround:** Either run `bundle install` locally and commit your changes to +`Gemfile.lock`, or remove the `Gemfile.lock` file from your repository and add +an entry in the `.gitignore` file to avoid it from being checked in again. + +### Questions? + +This entire guide is open-source. Go ahead and [edit it][3] if you have a +fix or [ask for help][4] if you run into trouble and need some help. + +[3]: https://github.com/jekyll/jekyll/edit/master/docs/_docs/continuous-integration/travis-ci.md +[4]: https://jekyllrb.com/help/ +[5]: https://docs.travis-ci.com/user/languages/ruby/#Specifying-Ruby-versions-and-implementations +[6]: https://docs.travis-ci.com/user/caching/#Caching-directories-(Bundler%2C-dependencies) +[7]: https://docs.travis-ci.com/user/notifications/ diff --git a/docs/_docs/contributing.md b/docs/_docs/contributing.md new file mode 100644 index 0000000..4c9056a --- /dev/null +++ b/docs/_docs/contributing.md @@ -0,0 +1,162 @@ +--- +title: Contributing +permalink: "/docs/contributing/" +note: This file is autogenerated. Edit /.github/CONTRIBUTING.markdown instead. +--- + +Hi there! Interested in contributing to Jekyll? We'd love your help. Jekyll is an open source project, built one contribution at a time by users like you. + +## Where to get help or report a problem + +See the [support guidelines](https://jekyllrb.com/docs/support/) + +## Ways to contribute + +Whether you're a developer, a designer, or just a Jekyll devotee, there are lots of ways to contribute. Here's a few ideas: + +- [Install Jekyll on your computer](https://jekyllrb.com/docs/installation/) and kick the tires. Does it work? Does it do what you'd expect? If not, [open an issue](https://github.com/jekyll/jekyll/issues/new) and let us know. +- Comment on some of the project's [open issues](https://github.com/jekyll/jekyll/issues). Have you experienced the same problem? Know a work around? Do you have a suggestion for how the feature could be better? +- Read through the [documentation](https://jekyllrb.com/docs/home/), and click the "improve this page" button, any time you see something confusing, or have a suggestion for something that could be improved. +- Browse through the [Jekyll discussion forum](https://talk.jekyllrb.com/), and lend a hand answering questions. There's a good chance you've already experienced what another user is experiencing. +- Find an [open issue](https://github.com/jekyll/jekyll/issues) (especially [those labeled `help-wanted`](https://github.com/jekyll/jekyll/issues?q=is%3Aopen+is%3Aissue+label%3Ahelp-wanted)), and submit a proposed fix. If it's your first pull request, we promise we won't bite, and are glad to answer any questions. +- Help evaluate [open pull requests](https://github.com/jekyll/jekyll/pulls), by testing the changes locally and reviewing what's proposed. + +## Submitting a pull request + +### Pull requests generally + +- The smaller the proposed change, the better. If you'd like to propose two unrelated changes, submit two pull requests. + +- The more information, the better. Make judicious use of the pull request body. Describe what changes were made, why you made them, and what impact they will have for users. + +- If this is your first pull request, it may help to [understand GitHub Flow](https://guides.github.com/introduction/flow/). + +- If you're submitting a code contribution, be sure to read the [code contributions](#code-contributions) section below. + +### Submitting a pull request via github.com + +Many small changes can be made entirely through the github.com web interface. + +1. Navigate to the file within [`jekyll/jekyll`](https://github.com/jekyll/jekyll) that you'd like to edit. +2. Click the pencil icon in the top right corner to edit the file +3. Make your proposed changes +4. Click "Propose file change" +5. Click "Create pull request" +6. Add a descriptive title and detailed description for your proposed change. The more information the better. +7. Click "Create pull request" + +That's it! You'll be automatically subscribed to receive updates as others review your proposed change and provide feedback. + +### Submitting a pull request via Git command line + +1. Fork the project by clicking "Fork" in the top right corner of [`jekyll/jekyll`](https://github.com/jekyll/jekyll). +2. Clone the repository locally `git clone https://github.com/<you-username>/jekyll`. +3. Create a new, descriptively named branch to contain your change ( `git checkout -b my-awesome-feature` ). +4. Hack away, add tests. Not necessarily in that order. +5. Make sure everything still passes by running `script/cibuild` (see the [tests section](#running-tests-locally) below) +6. Push the branch up ( `git push origin my-awesome-feature` ). +7. Create a pull request by visiting `https://github.com/<your-username>/jekyll` and following the instructions at the top of the screen. + +## Proposing updates to the documentation + +We want the Jekyll documentation to be the best it can be. We've open-sourced our docs and we welcome any pull requests if you find it lacking. + +### How to submit changes + +You can find the documentation for jekyllrb.com in the [docs](https://github.com/jekyll/jekyll/tree/master/docs) directory. See the section above, [submitting a pull request](#submitting-a-pull-request) for information on how to propose a change. + +One gotcha, all pull requests should be directed at the `master` branch (the default branch). + +### Updating FontAwesome iconset for jekyllrb.com + +We use a custom version of FontAwesome which contains just the icons we use. + +If you ever need to update our documentation with an icon that is not already available in our custom iconset, you'll have to regenerate the iconset using Icomoon's Generator: + +1. Go to <https://icomoon.io/app/>. +2. Click `Import Icons` on the top-horizontal-bar and upload the existing `<jekyll>/docs/icomoon-selection.json`. +3. Click `Add Icons from Library..` further down on the page, and add 'Font Awesome'. +4. Select the required icon(s) from the Library (make sure its the 'FontAwesome' library instead of 'IcoMoon-Free' library). +5. Click `Generate Font` on the bottom-horizontal-bar. +6. Inspect the included icons and proceed by clicking `Download`. +7. Extract the font files and adapt the CSS to the paths we use in Jekyll: + +- Copy the entire `fonts` directory over and overwrite existing ones at `<jekyll>/docs/`. +- Copy the contents of `selection.json` and overwrite existing content inside `<jekyll>/docs/icomoon-selection.json`. +- Copy the entire `@font-face {}` declaration and only the **new-icon(s)' css declarations** further below, to update the + `<jekyll>/docs/_sass/_font-awesome.scss` sass partial. +- Fix paths in the `@font-face {}` declaration by adding `../` before `fonts/FontAwesome.*` like so: + `('../fonts/Fontawesome.woff?9h6hxj')`. + +### Adding plugins + +If you want to add your plugin to the [list of plugins](https://jekyllrb.com/docs/plugins/#available-plugins), please submit a pull request modifying the [plugins page source file](https://github.com/jekyll/jekyll/blob/master/docs/_docs/plugins.md) by adding a link to your plugin under the proper subheading depending upon its type. + +## Code Contributions + +Interested in submitting a pull request? Awesome. Read on. There's a few common gotchas that we'd love to help you avoid. + +### Tests and documentation + +Any time you propose a code change, you should also include updates to the documentation and tests within the same pull request. + +#### Documentation + +If your contribution changes any Jekyll behavior, make sure to update the documentation. Documentation lives in the `docs/_docs` folder (spoiler alert: it's a Jekyll site!). If the docs are missing information, please feel free to add it in. Great docs make a great project. Include changes to the documentation within your pull request, and once merged, `jekyllrb.com` will be updated. + +#### Tests + +- If you're creating a small fix or patch to an existing feature, a simple test is more than enough. You can usually copy/paste from an existing example in the `tests` folder, but if you need you can find out about our tests suites [Shoulda](https://github.com/thoughtbot/shoulda/tree/master) and [RSpec-Mocks](https://github.com/rspec/rspec-mocks). + +- If it's a brand new feature, create a new [Cucumber](https://github.com/cucumber/cucumber/) feature, reusing existing steps where appropriate. + +### Code contributions generally + +- Jekyll uses the [Rubocop](https://github.com/bbatsov/rubocop) static analyzer to ensure that contributions follow the [GitHub Ruby Styleguide](https://github.com/styleguide/ruby). Please check your code using `script/fmt` and resolve any errors before pushing your branch. + +- Don't bump the Gem version in your pull request (if you don't know what that means, you probably didn't). + +- You can use the command `script/console` to start a REPL to explore the result of + Jekyll's methods. It also provides you with helpful methods to quickly create a + site or configuration. [Feel free to check it out!](https://github.com/jekyll/jekyll/blob/master/script/console) + +- Previously, we've used the WIP Probot app to help contributors determine whether their pull request is ready for review. Please use a [draft pull request](https://help.github.com/en/articles/about-pull-requests#draft-pull-requests) instead. When you're ready, [mark the pull request as ready for review](https://help.github.com/en/articles/changing-the-stage-of-a-pull-request) + +## Running tests locally + +### Test Dependencies + +To run the test suite and build the gem you'll need to install Jekyll's dependencies by running the following command: + +```sh +script/bootstrap +``` + +Before you make any changes, run the tests and make sure that they pass (to confirm your environment is configured properly): + +```sh +script/cibuild +``` + +If you are only updating a file in `test/`, you can use the command: + +```sh +script/test test/blah_test.rb +``` + +If you are only updating a `.feature` file, you can use the command: + +```sh +script/cucumber features/blah.feature +``` + +Both `script/test` and `script/cucumber` can be run without arguments to +run its entire respective suite. + +## Visual Studio Code Development Container + +If you've got [Visual Studio Code](https://code.visualstudio.com/) with the [Remote Development Extension Pack](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.vscode-remote-extensionpack) installed then simply opening this repository in Visual Studio Code and following the prompts to "Re-open In A Development Container" will get you setup and ready to go with a fresh environment with all the requirements installed. + +## A thank you + +Thanks! Hacking on Jekyll should be fun. If you find any of this hard to figure out, let us know so we can improve our process or documentation! diff --git a/docs/_docs/datafiles.md b/docs/_docs/datafiles.md new file mode 100644 index 0000000..e7ca78b --- /dev/null +++ b/docs/_docs/datafiles.md @@ -0,0 +1,177 @@ +--- +title: Data Files +permalink: /docs/datafiles/ +--- + +In addition to the [built-in variables]({{'/docs/variables/' | relative_url }}) available from Jekyll, +you can specify your own custom data that can be accessed via the [Liquid +templating system](https://github.com/Shopify/liquid/wiki/Liquid-for-Designers). + +Jekyll supports loading data from [YAML](https://yaml.org), [JSON](https://www.json.org/json-en.html), [CSV](https://en.wikipedia.org/wiki/Comma-separated_values), and [TSV](https://en.wikipedia.org/wiki/Tab-separated_values) files located in the `_data` directory. +Note that CSV and TSV files *must* contain a header row. + +This powerful feature allows you to avoid repetition in your templates and to +set site specific options without changing `_config.yml`. + +Plugins/themes can also leverage Data Files to set configuration variables. + +## The Data Folder + +The `_data` folder is where you can store additional data for Jekyll to use when +generating your site. These files must be YAML, JSON, TSV or CSV files (using either +the `.yml`, `.yaml`, `.json`, `.tsv`, or `.csv` extension), and they will be +accessible via `site.data`. + +## Example: List of members + +Here is a basic example of using Data Files to avoid copy-pasting large chunks +of code in your Jekyll templates: + +In `_data/members.yml`: + +```yaml +- name: Eric Mill + github: konklone + +- name: Parker Moore + github: parkr + +- name: Liu Fengyun + github: liufengyun +``` + +Or `_data/members.csv`: + +``` +name,github +Eric Mill,konklone +Parker Moore,parkr +Liu Fengyun,liufengyun +``` + +This data can be accessed via `site.data.members` (notice that the file's *basename* determines the variable name and +therefore one should avoid having data files with the same basename but different extensions, in the same directory). + +You can now render the list of members in a template: + +{% raw %} +```liquid +<ul> +{% for member in site.data.members %} + <li> + <a href="https://github.com/{{ member.github }}"> + {{ member.name }} + </a> + </li> +{% endfor %} +</ul> +``` +{% endraw %} + +## Subfolders + +Data files can also be placed in sub-folders of the `_data` folder. Each folder +level will be added to a variable's namespace. The example below shows how +GitHub organizations could be defined separately in a file under the `orgs` +folder: + +In `_data/orgs/jekyll.yml`: + +```yaml +username: jekyll +name: Jekyll +members: + - name: Tom Preston-Werner + github: mojombo + + - name: Parker Moore + github: parkr +``` + +In `_data/orgs/doeorg.yml`: + +```yaml +username: doeorg +name: Doe Org +members: + - name: John Doe + github: jdoe +``` + +The organizations can then be accessed via `site.data.orgs`, followed by the +file name: + +{% raw %} +```liquid +<ul> +{% for org_hash in site.data.orgs %} +{% assign org = org_hash[1] %} + <li> + <a href="https://github.com/{{ org.username }}"> + {{ org.name }} + </a> + ({{ org.members | size }} members) + </li> +{% endfor %} +</ul> +``` +{% endraw %} + +## Example: Accessing a specific author + +Pages and posts can also access a specific data item. The example below shows how to access a specific item: + +`_data/people.yml`: + +```yaml +dave: + name: David Smith + twitter: DavidSilvaSmith +``` + +The author can then be specified as a page variable in a post's front matter: + +{% raw %} +```liquid +--- +title: sample post +author: dave +--- + +{% assign author = site.data.people[page.author] %} +<a rel="author" + href="https://twitter.com/{{ author.twitter }}" + title="{{ author.name }}"> + {{ author.name }} +</a> +``` +{% endraw %} + +For information on how to build robust navigation for your site (especially if you have a documentation website or another type of Jekyll site with a lot of pages to organize), see [Navigation]({{ '/tutorials/navigation/' | relative_url }}). + +## CSV/TSV Parse Options + +The way Ruby parses CSV and TSV files can be customized with the `csv_reader` and `tsv_reader` +configuration options. Each configuration key exposes the same options: + +`converters`: What [CSV converters](https://ruby-doc.org/stdlib-2.5.0/libdoc/csv/rdoc/CSV.html#Converters) should be + used when parsing the file. Available options are `integer`, `float`, `numeric`, `date`, `date_time` and + `all`. By default, this list is empty. +`encoding`: What encoding the files are in. Defaults to the site `encoding` configuration option. +`headers`: Boolean field for whether to parse the first line of the file as headers. When `false`, it treats the + first row as data. Defaults to `true`. + +Examples: + +```yaml +csv_reader: + converters: + - numeric + - datetime + headers: true + encoding: utf-8 +tsv_reader: + converters: + - all + headers: false +``` diff --git a/docs/_docs/deployment.md b/docs/_docs/deployment.md new file mode 100644 index 0000000..45722e4 --- /dev/null +++ b/docs/_docs/deployment.md @@ -0,0 +1,11 @@ +--- +title: Deployment +permalink: /docs/deployment/ +redirect_from: "/docs/deployment-methods/index.html" +--- + +Sites built using Jekyll can be deployed in a large number of ways due to the static nature of the generated output. Here's some of the most common ways: + +* [Manually]({{ '/docs/deployment/manual/' | relative_url }}) +* [Automated]({{ '/docs/deployment/automated/' | relative_url }}) +* [Third Party]({{ '/docs/deployment/third-party/' | relative_url }}) diff --git a/docs/_docs/deployment/automated.md b/docs/_docs/deployment/automated.md new file mode 100644 index 0000000..8778a82 --- /dev/null +++ b/docs/_docs/deployment/automated.md @@ -0,0 +1,70 @@ +--- +title: Automated Deployment +permalink: /docs/deployment/automated/ +--- +There are a number of ways to easily automate the deployment of a Jekyll site. + +## Continuous Integration Service + +One of the easiest ways to set up an automated deployment flow is by using a +CI. + +These services run a script when there's a commit on your Git repository. +You might want this script to build the site, run tests over the output then deploy it to the +service of your choice. + +We have guides for the following providers: + +* [GitHub Actions]({{ '/docs/continuous-integration/github-actions/' | relative_url }}) +* [Travis CI]({{ '/docs/continuous-integration/travis-ci/' | relative_url }}) +* [CircleCI]({{ '/docs/continuous-integration/circleci/' | relative_url }}) +* [Buddy]({{ '/docs/continuous-integration/buddyworks/' | relative_url }}) +* [Razorops CI/CD]({{ '/docs/continuous-integration/razorops/' | relative_url }}) + +## Git post-receive hook + +To have a remote server handle the deploy for you every time you push changes using Git, you can create a user account which has all the public keys that are authorized to deploy in its `authorized_keys` file. With that in place, setting up the post-receive hook is done as follows: + +```sh +laptop$ ssh deployer@example.com +server$ mkdir myrepo.git +server$ cd myrepo.git +server$ git --bare init +server$ cp hooks/post-receive.sample hooks/post-receive +server$ mkdir /var/www/myrepo +``` + +Next, add the following lines to hooks/post-receive and be sure Jekyll is +installed on the server: + +```bash +#!/bin/bash -l + +# Install Ruby Gems to ~/gems +export GEM_HOME=$HOME/gems +export PATH=$GEM_HOME/bin:$PATH + +TMP_GIT_CLONE=$HOME/tmp/myrepo +GEMFILE=$TMP_GIT_CLONE/Gemfile +PUBLIC_WWW=/var/www/myrepo + +git clone $GIT_DIR $TMP_GIT_CLONE +BUNDLE_GEMFILE=$GEMFILE bundle install +BUNDLE_GEMFILE=$GEMFILE bundle exec jekyll build -s $TMP_GIT_CLONE -d $PUBLIC_WWW +rm -Rf $TMP_GIT_CLONE +exit +``` + +Finally, run the following command on any users laptop that needs to be able to +deploy using this hook: + +```sh +laptops$ git remote add deploy deployer@example.com:~/myrepo.git +``` + +Deploying is now as easy as telling nginx or Apache to look at +`/var/www/myrepo` and running the following: + +```sh +laptops$ git push deploy master +``` diff --git a/docs/_docs/deployment/manual.md b/docs/_docs/deployment/manual.md new file mode 100644 index 0000000..b0805c1 --- /dev/null +++ b/docs/_docs/deployment/manual.md @@ -0,0 +1,36 @@ +--- +title: Manual Deployment +permalink: /docs/deployment/manual/ +--- + +Jekyll generates your static site to the `_site` directory by default. You can +transfer the contents of this directory to almost any hosting provider to get +your site live. Here are some manual ways of achieving this: + +## rsync + +Rsync is similar to scp except it can be faster as it will only send changed +parts of files as opposed to the entire file. You can learn more about using +rsync in the [Digital Ocean tutorial](https://www.digitalocean.com/community/tutorials/how-to-use-rsync-to-sync-local-and-remote-directories-on-a-vps). + +## Amazon S3 + +If you want to host your site in Amazon S3, you can do so by +using the [s3_website](https://github.com/laurilehmijoki/s3_website) +application. It will push your site to Amazon S3 where it can be served like +any web server, +dynamically scaling to almost unlimited traffic. This approach has the +benefit of being about the cheapest hosting option available for +low-volume blogs as you only pay for what you use. + +## FTP + +Most traditional web hosting providers let you upload files to their servers over FTP. To upload a Jekyll site to a web host using FTP, run the `jekyll build` command and copy the contents of the generated `_site` folder to the root folder of your hosting account. This is most likely to be the `httpdocs` or `public_html` folder on most hosting providers. + +## scp + +If you have direct access to the deployment web server, the process is essentially the same, except you might have other methods available to you (such as `scp`, or even direct filesystem access) for transferring the files. Remember to make sure the contents of the generated `_site` folder get placed in the appropriate web root directory for your web server. + +## Rack-Jekyll + +[Rack-Jekyll](https://github.com/adaoraul/rack-jekyll/) allows you to deploy your site on any Rack server such as Amazon EC2, Slicehost, Heroku, and so forth. It also can run with [shotgun](https://github.com/rtomayko/shotgun/), [rackup](https://github.com/rack/rack), [mongrel](https://github.com/mongrel/mongrel), [unicorn](https://github.com/defunkt/unicorn/), and [others](https://github.com/adaoraul/rack-jekyll#readme). diff --git a/docs/_docs/deployment/third-party.md b/docs/_docs/deployment/third-party.md new file mode 100644 index 0000000..41ca6ee --- /dev/null +++ b/docs/_docs/deployment/third-party.md @@ -0,0 +1,74 @@ +--- +title: 3rd Party +permalink: /docs/deployment/third-party/ +--- + + +## AWS Amplify + +The [AWS Amplify Console](https://console.amplify.aws) provides continuous deployment and hosting for modern web apps (single page apps and static site generators). Continuous deployment allows developers to deploy updates to their web app on every code commit to their Git repository. Hosting includes features such as globally available CDNs, 1-click custom domain setup + HTTPS, feature branch deployments, redirects, trailing slashes, and password protection. + +Read this [step-by-step guide](https://medium.com/@jameshamann/deploy-your-jekyll-site-using-aws-amplify-with-only-a-few-clicks-8f3dd8f26112) to deploy and host your Jekyll site on AWS Amplify. + +## Bip + +[Bip](https://bip.sh) provides zero downtime deployment, a global CDN, SSL, unlimited bandwidth and more for Jekyll websites. Deploy in seconds from the command line. [Visit the Bip website](https://bip.sh) for more information - which is also built with Jekyll. + +## CloudCannon + +[CloudCannon](https://cloudcannon.com) has everything you need to build, host +and update Jekyll websites. Take advantage of our global CDN, automated SSL, +continuous deployment and [more](https://cloudcannon.com/features/). + +## GitHub Pages + +Sites on GitHub Pages are powered by Jekyll behind the scenes, so if you’re looking for a zero-hassle, zero-cost solution, GitHub Pages are a great way to [host your Jekyll-powered website for free](/docs/github-pages/). + +## GitLab Pages + +[GitLab Pages](https://about.gitlab.com/stages-devops-lifecycle/pages/) offers free hosting with custom domains. [Get started with Jekyll](https://docs.gitlab.com/ee/user/project/pages/getting_started_part_four.html#practical-example) and a fully customizable pipeline. + +## KeyCDN + +[KeyCDN](https://www.keycdn.com) accelerates Jekyll-powered websites with a wide range of other features such as real time image processing including WebP transformation. +The [Jekyll hosting tutorial](https://www.keycdn.com/support/jekyll-hosting) provides various options to supercharge Jekyll sites with just a few steps. It combines best flexibility and excellent performance. + +## Kickster + +Use [Kickster](https://kickster.nielsenramon.com/) for automated deploys to GitHub Pages when using unsupported plugins on GitHub Pages. + +Kickster provides a basic Jekyll project setup packed with web best practices and useful optimization tools increasing your overall project quality. Kickster ships with automated and worry-free deployment scripts for GitHub Pages. + +Install the Kickster gem and you are good to go. More documentation can here found [here](https://github.com/nielsenramon/kickster#kickster). If you do not want to use the gem or start a new project you can just copy paste the deployment scripts for [Travis CI](https://github.com/nielsenramon/kickster/tree/master/snippets/travis) or [Circle CI](https://github.com/nielsenramon/kickster#automated-deployment-with-circle-ci). + +## Netlify + +Netlify provides Global CDN, Continuous Deployment, one click HTTPS and [much more](https://www.netlify.com/features/), providing developers a robust toolset for modern web projects, without added complexity. Netlify supports custom plugins for Jekyll and has a free plan for open source projects. + +Read this [Jekyll step-by-step guide](https://www.netlify.com/blog/2020/04/02/a-step-by-step-guide-jekyll-4.0-on-netlify/) to setup your Jekyll site on Netlify. + +## Render + +[Render](https://render.com) provides zero config continuous deployment for static sites. The service is free under 100GB monthly bandwidth. + +## Hostman + +[Hostman](https://hostman.com) allows you to host websites for free with no configurations. Read [this guide](https://hostman.com/docs/jekyll) to deploy your Jekyll site on Hostman. + +## Static Publisher + +[Static Publisher](https://github.com/static-publisher/static-publisher) is another automated deployment option with a server listening for webhook posts, though it's not tied to GitHub specifically. It has a one-click deploy to Heroku, it can watch multiple projects from one server, it has an easy to user admin interface and can publish to either S3 or to a git repository (e.g. gh-pages). + +## Vercel + +[Vercel](https://vercel.com/) provides zero config continuous deployment, HTTPS Custom domains, high performance smart CDN, you get instant static deploy for free. + +## 21YunBox + +[21YunBox](https://www.21yunbox.com) provides blazing fast Chinese CDN, Continuous Deployment, one click HTTPS and [much more](https://www.21yunbox.com/docs/), providing developers a hassle-free solution to launch their web projects in China. + +Read this [Jekyll step-by-step guide](https://www.21yunbox.com/docs/#/deploy-jekyll) to deploy your Jekyll site on 21YunBox. + +## Layer0 + +[Layer0](https://www.layer0.co) is an all-in-one platform to develop, deploy, preview, experiment on, monitor, and run your headless frontend. It is focused on large, dynamic websites and best-in-class performance through EdgeJS (a JavaScript-based Content Delivery Network), predictive prefetching, and performance monitoring. Layer0 offers a free tier. Get started in just a few minutes by following [Layer0's guide to deploying Jekyll](https://docs.layer0.co/guides/jekyll). diff --git a/docs/_docs/front-matter.md b/docs/_docs/front-matter.md new file mode 100644 index 0000000..7b88a15 --- /dev/null +++ b/docs/_docs/front-matter.md @@ -0,0 +1,212 @@ +--- +title: Front Matter +permalink: /docs/front-matter/ +redirect_from: /docs/frontmatter/index.html +--- + +Any file that contains a [YAML](https://yaml.org/) front matter block will be +processed by Jekyll as a special file. The front matter must be the first thing +in the file and must take the form of valid YAML set between triple-dashed +lines. Here is a basic example: + +```yaml +--- +layout: post +title: Blogging Like a Hacker +--- +``` + +Between these triple-dashed lines, you can set predefined variables (see below +for a reference) or even create custom ones of your own. These variables will +then be available for you to access using Liquid tags both further down in the +file and also in any layouts or includes that the page or post in question +relies on. + +<div class="note warning"> + <h5>UTF-8 Character Encoding Warning</h5> + <p> + If you use UTF-8 encoding, make sure that no <code>BOM</code> header + characters exist in your files or very, very bad things will happen to + Jekyll. This is especially relevant if you’re running + <a href="{{ '/docs/installation/windows/' | relative_url }}">Jekyll on Windows</a>. + </p> +</div> + +<div class="note"> + <h5>Front Matter Variables Are Optional</h5> + <p> + If you want to use <a href="{{ '/docs/variables/' | relative_url }}">Liquid tags and variables</a> + but don’t need anything in your front matter, just leave it empty! The set + of triple-dashed lines with nothing in between will still get Jekyll to + process your file. (This is useful for things like CSS and RSS feeds!) + </p> +</div> + +## Predefined Global Variables + +There are a number of predefined global variables that you can set in the +front matter of a page or post. + +<div class="mobile-side-scroller"> +<table> + <thead> + <tr> + <th>Variable</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td> + <p><code>layout</code></p> + </td> + <td> + <p> + + If set, this specifies the layout file to use. Use the layout file + name without the file extension. Layout files must be placed in the + <code>_layouts</code> directory. + + </p> + <ul> + <li> + Using <code>null</code> will produce a file without using a layout + file. This is overridden if the file is a post/document and has a + layout defined in the <a href="{{ '/docs/configuration/front-matter-defaults/' | relative_url }}"> + front matter defaults</a>. + </li> + <li> + Starting from version 3.5.0, using <code>none</code> in a post/document will + produce a file without using a layout file regardless of front matter defaults. + Using <code>none</code> in a page will cause Jekyll to attempt to + use a layout named "none". + </li> + </ul> + </td> + </tr> + <tr> + <td> + <p><code>permalink</code></p> + </td> + <td> + <p> + + If you need your processed blog post URLs to be something other than + the site-wide style (default <code>/year/month/day/title.html</code>), then you can set + this variable and it will be used as the final URL. + + </p> + </td> + </tr> + <tr> + <td> + <p><code>published</code></p> + </td> + <td> + <p> + Set to false if you don’t want a specific post to show up when the + site is generated. + </p> + </td> + </tr> + </tbody> +</table> +</div> + +<div class="note"> + <h5>Render Posts Marked As Unpublished</h5> + <p> + To preview unpublished pages, run `jekyll serve` or `jekyll build` + with the `--unpublished` switch. Jekyll also has a handy <a href="{{ '/docs/posts/#drafts' | relative_url }}">drafts</a> + feature tailored specifically for blog posts. + </p> +</div> + +## Custom Variables + +You can also set your own front matter variables you can access in Liquid. For +instance, if you set a variable called `food`, you can use that in your page: + +{% raw %} +```liquid +--- +food: Pizza +--- + +<h1>{{ page.food }}</h1> +``` +{% endraw %} + +## Predefined Variables for Posts + +These are available out-of-the-box to be used in the front matter for a post. + +<div class="mobile-side-scroller"> +<table> + <thead> + <tr> + <th>Variable</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td> + <p><code>date</code></p> + </td> + <td> + <p> + A date here overrides the date from the name of the post. This can be + used to ensure correct sorting of posts. A date is specified in the + format <code>YYYY-MM-DD HH:MM:SS +/-TTTT</code>; hours, minutes, seconds, and timezone offset + are optional. + </p> + </td> + </tr> + <tr> + <td> + <p><code>category</code></p> + <p><code>categories</code></p> + </td> + <td> + <p> + + Instead of placing posts inside of folders, you can specify one or + more categories that the post belongs to. When the site is generated + the post will act as though it had been set with these categories + normally. Categories (plural key) can be specified as a <a + href="https://en.wikipedia.org/wiki/YAML#Basic_components">YAML list</a> or a + space-separated string. + + </p> + </td> + </tr> + <tr> + <td> + <p><code>tags</code></p> + </td> + <td> + <p> + + Similar to categories, one or multiple tags can be added to a post. + Also like categories, tags can be specified as a <a + href="https://en.wikipedia.org/wiki/YAML#Basic_components">YAML list</a> or a + space-separated string. + + </p> + </td> + </tr> + </tbody> +</table> +</div> + +<div class="note"> + <h5>Don't repeat yourself</h5> + <p> + If you don't want to repeat your frequently used front matter variables + over and over, define + <a href="{{ '/docs/configuration/front-matter-defaults/' | relative_url }}" title="Front Matter defaults">defaults</a> + for them and only override them where necessary (or not at all). This works + both for predefined and custom variables. + </p> +</div> diff --git a/docs/_docs/github-pages.md b/docs/_docs/github-pages.md new file mode 100644 index 0000000..298e4a8 --- /dev/null +++ b/docs/_docs/github-pages.md @@ -0,0 +1,131 @@ +--- +title: GitHub Pages +permalink: /docs/github-pages/ +--- + +[GitHub Pages](https://pages.github.com) are public web pages for users, +organizations, and repositories, that are freely hosted on GitHub's `github.io` +domain or on a custom domain name of your choice. GitHub Pages are powered by +Jekyll behind the scenes, so they're a great way to host your Jekyll-powered +website for free. + +Your site is automatically generated by GitHub Pages when you push your source +files. Note that GitHub Pages works equally well for regular HTML content, +simply because Jekyll treats files without front matter as static assets. +So if you only need to push generated HTML, you're good to go without any +further setup. + +The [GitHub Pages Documentation](https://docs.github.com/en/free-pro-team@latest/github/working-with-github-pages) +is comprehensive and includes a [a guide to setting up a GitHub Pages site using +Jekyll](https://docs.github.com/en/free-pro-team@latest/github/working-with-github-pages/setting-up-a-github-pages-site-with-jekyll). +We recommend following this guide. + +This page contains some additional information which may be useful when working +on GitHub Pages sites with Jekyll. + +<div class="note"> + <h5>GitHub Pages Documentation, Help, and Support</h5> + <p> + For more information about what you can do with GitHub Pages, as well as for + troubleshooting guides, you should check out + <a href="https://docs.github.com/en/free-pro-team@latest/github/working-with-github-pages">GitHub’s Pages Help section</a>. + If all else fails, you should contact <a href="https://github.com/contact">GitHub Support</a>. + </p> +</div> + +### Project Page URL Structure + +Sometimes it's nice to preview your Jekyll site before you push your `gh-pages` +branch to GitHub. The subdirectory-like URL structure GitHub uses for +Project Pages complicates the proper resolution of URLs. In order to assure your +site builds properly, use the handy [URL filters]({{ '/docs/liquid/filters/' | relative_url }}): + +{% raw %} +```liquid +<!-- For styles with static names... --> +<link href="{{ 'assets/css/style.css' | relative_url }}" rel="stylesheet"> +<!-- For documents/pages whose URLs can change... --> +[{{ page.title }}]("{{ page.url | relative_url }}") +``` +{% endraw %} + +This way you can preview your site locally from the site root on localhost, +but when GitHub generates your pages from the `gh-pages` branch all the URLs +will resolve properly. + +## Deploying Jekyll to GitHub Pages + +GitHub Pages work by looking at certain branches of repositories on GitHub. +There are two basic types available: [user/organization and project pages](https://docs.github.com/en/free-pro-team@latest/github/working-with-github-pages/about-github-pages#types-of-github-pages-sites). +The way to deploy these two types of sites are nearly identical, except for a +few minor details. + +### User and Organization Pages + +User and organization pages live in a special GitHub repository dedicated to +only the GitHub Pages files. This repository must be named after the account +name. For example, [@mojombo’s user page repository](https://github.com/mojombo/mojombo.github.io) has the name +`mojombo.github.io`. + +Content from the `master` branch of your repository will be used to build and +publish the GitHub Pages site, so make sure your Jekyll site is stored there. + +<div class="note info"> + <h5>Custom domains do not affect repository names</h5> + <p> + GitHub Pages are initially configured to live under the + <code>username.github.io</code> subdomain, which is why repositories must + be named this way <strong>even if a custom domain is being used</strong>. + </p> +</div> + +### Project Pages + +Unlike user and organization Pages, Project Pages are kept in the same +repository as the project they are for, except that the website content is +stored in a specially named `gh-pages` branch or in a `docs` folder on the +`master` branch. The content will be rendered using Jekyll, and the output +will become available under a subpath of your user pages subdomain, such as +`username.github.io/project` (unless a custom domain is specified). + +The Jekyll project repository itself is a perfect example of this branch +structure—the [master branch]({{ site.repository }}) contains the +actual software project for Jekyll, and the Jekyll website that you’re +looking at right now is contained in the [docs +folder]({{ site.repository }}/tree/master/docs) of the same repository. + +Please refer to GitHub official documentation on +[user, organization and project pages](https://docs.github.com/en/free-pro-team@latest/github/working-with-github-pages/about-github-pages#types-of-github-pages-sites) +to see more detailed examples. + +<div class="note warning"> + <h5>Source files must be in the root directory</h5> + <p> + GitHub Pages <a href="https://help.github.com/en/github/working-with-github-pages/troubleshooting-jekyll-build-errors-for-github-pages-sites">overrides</a> + the <a href="{{ '/docs/configuration/options/' | relative_url }}">“Site Source”</a> + configuration value, so if you locate your files anywhere other than the + root directory, your site may not build correctly. + </p> +</div> + +<div class="note info"> + <h5>Installing the <code>github-pages</code> gem on Windows</h5> + + <p> + While Windows is not officially supported, it is possible + to install the <code>github-pages</code> gem on Windows. + Special instructions can be found on our + <a href="{{ '/docs/installation/windows/' | relative_url }}">Windows-specific docs page</a>. + </p> +</div> + +### Running and Testing Locally + +Once the project is configured with the github-pages environment, it's quite hard to switch back and forth with the local settings and the production-level settings. For that we can use certain CLI options to make the workflow hassle-free. + +```sh +bundle exec jekyll serve --baseurl="" +``` + +This will run the jekyll server on your local machine i.e. on `http://localhost:4000`. Refer <a href="{{ '/docs/configuration/options/#serve-command-options' | relative_url }}">server options</a> for available options. + diff --git a/docs/_docs/history.md b/docs/_docs/history.md new file mode 100644 index 0000000..1700c17 --- /dev/null +++ b/docs/_docs/history.md @@ -0,0 +1,4423 @@ +--- +title: History +permalink: "/docs/history/" +note: This file is autogenerated. Edit /History.markdown instead. +--- + +## 4.3.1 / 2022-10-26 +{: #v4-3-1} + +### Bug Fixes +{: #bug-fixes-v4-3-1} + +- Respect user-defined name attribute in documents ([#9167]({{ site.repository }}/issues/9167)) +- Revert "Incrementally rebuild when a data file is changed" ([#9170]({{ site.repository }}/issues/9170)) + +### Documentation + +- Release post for v4.3.1 ([#9171]({{ site.repository }}/issues/9171)) + + +## 4.3.0 / 2022-10-20 +{: #v4-3-0} + +### Minor Enhancements +{: #minor-enhancements-v4-3-0} + +- Add webrick as a dependency ([#8524]({{ site.repository }}/issues/8524)) +- Regenerate supported mime types ([#8542]({{ site.repository }}/issues/8542)) +- Update include tag to be more permissive ([#8618]({{ site.repository }}/issues/8618)) +- Optimize `Jekyll::Utils.parse_date` ([#8425]({{ site.repository }}/issues/8425)) +- Update rubocop from 1.12 to 1.18 and min ruby from 2.4 to 2.5 ([#8741]({{ site.repository }}/issues/8741)) +- Always hide cache-dir contents from Git ([#8798]({{ site.repository }}/issues/8798)) +- Remove the warning about auto-regeneration on Windows ([#8821]({{ site.repository }}/issues/8821)) +- Propagate _data folder from theme ([#8815]({{ site.repository }}/issues/8815)) +- Support both tzinfo v1 and v2 along with non-half hour offsets. ([#8880]({{ site.repository }}/issues/8880)) +- Run vendor-mimes to update mime.types ([#8940]({{ site.repository }}/issues/8940)) +- Expose collection static files via `site.static_files` ([#8961]({{ site.repository }}/issues/8961)) +- Expose `basename` from `document.rb` as `name` to Liquid templates ([#8761]({{ site.repository }}/issues/8761)) +- Allow Configurable Converters on CSV ([#8858]({{ site.repository }}/issues/8858)) +- Introduce `theme` drop to expose theme-gem details ([#9129]({{ site.repository }}/issues/9129)) +- Relax version constraint to allow Rouge 4.x ([#9134]({{ site.repository }}/issues/9134)) +- Incrementally rebuild when a data file is changed ([#8771]({{ site.repository }}/issues/8771)) +- Support jekyll-sass-converter 3.x ([#9132]({{ site.repository }}/issues/9132)) + +### Bug Fixes +{: #bug-fixes-v4-3-0} + +- fix: pin rubocop to 1.12 due to error with ruby 2.4 ([#8651]({{ site.repository }}/issues/8651)) +- Load Jekyll plugins from BUNDLE_GEMFILE location ([#8585]({{ site.repository }}/issues/8585)) +- fix(security): CVE-2021-28834 ([#8680]({{ site.repository }}/issues/8680)) +- Inject livereload script using `location.protocol` instead of `http:` ([#8718]({{ site.repository }}/issues/8718)) +- Respect collections_dir config within include tag ([#8756]({{ site.repository }}/issues/8756)) +- Fix regression in Convertible module from v4.2.0 ([#8786]({{ site.repository }}/issues/8786)) +- Revert [#7253]({{ site.repository }}/issues/7253): "Don't reset site.url to localhost:4000 by default" ([#8620]({{ site.repository }}/issues/8620)) +- Improve readability of CI logs ([#8877]({{ site.repository }}/issues/8877)) +- Fix deprecation message for missing doc method ([#8960]({{ site.repository }}/issues/8960)) +- Fix response header for content served via `jekyll serve` ([#8965]({{ site.repository }}/issues/8965)) +- Trigger livereload in sites without pages ([#8337]({{ site.repository }}/issues/8337)) +- Only enable BOM encoding option on UTF encodings ([#8363]({{ site.repository }}/issues/8363)) +- Ensure theme config is a `Jekyll::Configuration` object ([#8988]({{ site.repository }}/issues/8988)) +- Remove misleading totals row from `--profile` table ([#9039]({{ site.repository }}/issues/9039)) +- Unlock Psych dependency ([#9135]({{ site.repository }}/issues/9135)) +- Fix false positive conflicts for static files in a collection ([#9141]({{ site.repository }}/issues/9141)) + +### Development Fixes +{: #development-fixes-v4-3-0} + +- style: enable new cops ([#8538]({{ site.repository }}/issues/8538)) +- Allow dependabot to keep github actions up-to-date ([#8540]({{ site.repository }}/issues/8540)) +- Update actions/cache requirement to v2.1.3 ([#8543]({{ site.repository }}/issues/8543)) +- Pin rubocop version ([#8564]({{ site.repository }}/issues/8564)) +- style: add rubocop 1.9 cops ([#8567]({{ site.repository }}/issues/8567)) +- Cross Version Testing Locally and Faster CI ([#8610]({{ site.repository }}/issues/8610)) +- Use official Ruby setup GH action ([#8614]({{ site.repository }}/issues/8614)) +- Spell check action for markdown documentation ([#8675]({{ site.repository }}/issues/8675)) +- Update expect to cover docs/_posts ([#8677]({{ site.repository }}/issues/8677)) +- Bump check-spelling/check-spelling from 0.0.18 to 0.0.19 ([#8740]({{ site.repository }}/issues/8740)) +- Enable Rubocop accessor grouping, fix existing offenses ([#8293]({{ site.repository }}/issues/8293)) +- Tags:Highlight: Decomposed HTMLLegacy formatter ([#8623]({{ site.repository }}/issues/8623)) +- Relax Rubocop Dependency ([#8831]({{ site.repository }}/issues/8831)) +- Add a workflow to build gems consistently ([#8830]({{ site.repository }}/issues/8830)) +- Fix random test failures in TestExcerpt #to_liquid ([#8884]({{ site.repository }}/issues/8884)) +- Lock gem `psych` to `v3.x` ([#8918]({{ site.repository }}/issues/8918)) +- Fix typo in Bug Report template ([#8951]({{ site.repository }}/issues/8951)) +- Check symlink outside site_source without Pathutil ([#9015]({{ site.repository }}/issues/9015)) +- Stop testing with Rubies older than 2.7 on non-Windows ([#8955]({{ site.repository }}/issues/8955)) +- Bump actions/checkout from 2 to 3 ([#8986]({{ site.repository }}/issues/8986)) +- Remove git.io shortlinks from repo ([#9045]({{ site.repository }}/issues/9045)) +- Bump rubocop to 1.32 ([#9093]({{ site.repository }}/issues/9093)) +- Bump RuboCop to `1.36.x` ([#9125]({{ site.repository }}/issues/9125)) +- Use check-spelling/check-spelling@v0.0.20 ([#9111]({{ site.repository }}/issues/9111)) +- Disable pending cops when running rubocop ([#9136]({{ site.repository }}/issues/9136)) +- Relax RDoc version dependency ([#9142]({{ site.repository }}/issues/9142)) + +### Documentation + +- typo - do instead of don't ([#8518]({{ site.repository }}/issues/8518)) +- Document support for TSV files consistently ([#8488]({{ site.repository }}/issues/8488)) +- Add a disclaimer to tutorials involving Ruby code ([#8525]({{ site.repository }}/issues/8525)) +- Improve documentation on developing generators ([#8527]({{ site.repository }}/issues/8527)) +- Fixes typo in layouts_dir documentation ([#8532]({{ site.repository }}/issues/8532)) +- Fix i.e. typos in collections.md ([#8529]({{ site.repository }}/issues/8529)) +- Remove GitHub Pages content which is in GitHub docs ([#8533]({{ site.repository }}/issues/8533)) +- Step By Step Instructions Review ([#8399]({{ site.repository }}/issues/8399)) +- Fix typo in migrating from 3.0 to 4.0 page ([#8572]({{ site.repository }}/issues/8572)) +- Fix for important missing step in macOS Installation Docs: Add the Homebrew gems directory to the PATH ([#8496]({{ site.repository }}/issues/8496)) +- Use latest Jekyll-action configuration ([#8579]({{ site.repository }}/issues/8579)) +- docs: troubleshoot macOS with ARM64 architecture ([#8560]({{ site.repository }}/issues/8560)) +- docs: add overview of .jekyll-cache dir ([#8648]({{ site.repository }}/issues/8648)) +- docs: clarify where .jekyll-metadata comes from ([#8646]({{ site.repository }}/issues/8646)) +- Razorops CI/CD added ([#8656]({{ site.repository }}/issues/8656)) +- Specify default port and host for serve commands in docs ([#8624]({{ site.repository }}/issues/8624)) +- Update third-party.md ([#8652]({{ site.repository }}/issues/8652)) +- Add documentation for Sass configuration options ([#8587]({{ site.repository }}/issues/8587)) +- Add formcarry to forms section ([#8471]({{ site.repository }}/issues/8471)) +- Add step to set SDKROOT ([#8478]({{ site.repository }}/issues/8478)) +- Improve the "Markdown Options" Docs ([#8681]({{ site.repository }}/issues/8681)) +- Add 'webrick' warning note to "Quickstart" Docs ([#8727]({{ site.repository }}/issues/8727)) +- Update windows.md ([#8701]({{ site.repository }}/issues/8701)) +- IRC networks - Libera, Freenode ([#8706]({{ site.repository }}/issues/8706)) +- Improve GitHub Flavored Markdown Docs ([#8684]({{ site.repository }}/issues/8684)) +- Fixing URL in MacOS install for rbenv-doctor ([#8693]({{ site.repository }}/issues/8693)) +- Fix adjective in `troubleshooting.md` document ([#8777]({{ site.repository }}/issues/8777)) +- Goodbye Frank. We'll miss you. 💔 ([#8807]({{ site.repository }}/issues/8807)) +- Update index.html: Grammar fix. ([#8803]({{ site.repository }}/issues/8803)) +- Prefer Libera. Remove Freenode. ([#8811]({{ site.repository }}/issues/8811)) +- Update feature_request.md ([#8797]({{ site.repository }}/issues/8797)) +- Remove AWS Amplify from the showcase ([#8812]({{ site.repository }}/issues/8812)) +- Move Frank to Emeritus Core Team Members ([#8813]({{ site.repository }}/issues/8813)) +- Release post for v4.2.1 ([#8818]({{ site.repository }}/issues/8818)) +- Update CircleCI example ([#8829]({{ site.repository }}/issues/8829)) +- Fix typo ([#8835]({{ site.repository }}/issues/8835)) +- Added docs for running locally ([#8852]({{ site.repository }}/issues/8852)) +- Linting README.markdown ([#8900]({{ site.repository }}/issues/8900)) +- Remove text on GITHUB_TOKEN which is now built-in ([#8907]({{ site.repository }}/issues/8907)) +- Add Security Policy document ([#8823]({{ site.repository }}/issues/8823)) +- Manage repository meta documents consistently ([#8908]({{ site.repository }}/issues/8908)) +- docs: add Layer0 deployment guide ([#8915]({{ site.repository }}/issues/8915)) +- docs: Update README generated by `jekyll new-theme` ([#8919]({{ site.repository }}/issues/8919)) +- Update resources.md ([#8925]({{ site.repository }}/issues/8925)) +- Rewrite documentation on installing plugins ([#8921]({{ site.repository }}/issues/8921)) +- Improve maintainers guide on releasing a new version ([#8928]({{ site.repository }}/issues/8928)) +- Fix link for "CloudSh" ([#8934]({{ site.repository }}/issues/8934)) +- Recommend using `actions/cache` in GitHub Actions documentation ([#8948]({{ site.repository }}/issues/8948)) +- Remove references to EOL hakiri.io service ([#8946]({{ site.repository }}/issues/8946)) +- Release post for v4.2.2 ([#8982]({{ site.repository }}/issues/8982)) +- Document releasing off `*-stable` branches ([#8984]({{ site.repository }}/issues/8984)) +- Update document by fix yaml syntax error ([#8991]({{ site.repository }}/issues/8991)) +- Enhance option's case for Jekyll configuration ([#8992]({{ site.repository }}/issues/8992)) +- Fix typo in `_docs/deployment/manual.md` ([#8997]({{ site.repository }}/issues/8997)) +- Add quiet/verbose options ([#8996]({{ site.repository }}/issues/8996)) +- Update README.markdown re IRC Pointer ([#9005]({{ site.repository }}/issues/9005)) +- Remove Aerobatic ([#9007]({{ site.repository }}/issues/9007)) +- Add Jekyll 3.9.2 release post to 'master' branch ([#9013]({{ site.repository }}/issues/9013)) +- Simplify macOS installation docs ([#8993]({{ site.repository }}/issues/8993)) +- Improve document about Github Actions section ([#8853]({{ site.repository }}/issues/8853)) +- Update permalinks.md ([#9017]({{ site.repository }}/issues/9017)) +- Add clarity to docs on permalinks placeholders and built-ins ([#8995]({{ site.repository }}/issues/8995)) +- Remove Ionic Framework site from showcase ([#9057]({{ site.repository }}/issues/9057)) +- Windows: describe which option to choose ([#9049]({{ site.repository }}/issues/9049)) +- Improve links (http -> https) ([#9064]({{ site.repository }}/issues/9064)) +- Update ruby version for macos guide ([#9086]({{ site.repository }}/issues/9086)) +- Update posts.md ([#9151]({{ site.repository }}/issues/9151)) +- Release post for v4.3.0 ([#9157]({{ site.repository }}/issues/9157)) + +### Site Enhancements +{: #site-enhancements-v4-3-0} + +- Improvements to CSS ([#7834]({{ site.repository }}/issues/7834)) +- Slightly update lang `sh` code-block styling ([#8857]({{ site.repository }}/issues/8857)) + + +## 4.2.2 / 2022-03-03 +{: #v4-2-2} + +### Bug Fixes +{: #bug-fixes-v4-2-2} + +- Lock `http_parser.rb` gem to `v0.6.x` on JRuby. + +### Development Fixes +{: #development-fixes-v4-2-2} + +- Backport [#8830]({{ site.repository }}/issues/8830) for v4.2.x: Add a workflow to build gems consistently ([#8869]({{ site.repository }}/issues/8869)) +- Lock `rubocop-performance` to `v1.11.x`. + + +## 4.2.1 / 2021-09-27 +{: #v4-2-1} + +### Bug Fixes +{: #bug-fixes-v4-2-1} + +- Backport [#8620]({{ site.repository }}/issues/8620) for v4.2.x: Revert [#7253]({{ site.repository }}/issues/7253): "Don't reset site.url to localhost:4000 by default" ([#8808]({{ site.repository }}/issues/8808)) +- Backport [#8756]({{ site.repository }}/issues/8756) for v4.2.x: Respect collections_dir config within include tag ([#8794]({{ site.repository }}/issues/8794)) +- Backport [#8786]({{ site.repository }}/issues/8786) for v4.2.x: Fix regression in Convertible module from v4.2.0 ([#8793]({{ site.repository }}/issues/8793)) + + +## 4.2.0 / 2020-12-14 +{: #v4-2-0} + +### Minor Enhancements +{: #minor-enhancements-v4-2-0} + +- Warn on command-line with permalink conflict ([#8342]({{ site.repository }}/issues/8342)) +- Suppress warning issued for redirect pages ([#8347]({{ site.repository }}/issues/8347)) +- Enhance detection of conflicting destination URLs ([#8459]({{ site.repository }}/issues/8459)) +- Add `:post_convert` hook to modify HTML content before layout ([#8368]({{ site.repository }}/issues/8368)) +- Allow triggering `:post_convert` events atomically ([#8465]({{ site.repository }}/issues/8465)) +- Debug reading Page and Layout objects ([#8100]({{ site.repository }}/issues/8100)) +- Do not reset `site.url` to `http://localhost:4000` by default ([#7253]({{ site.repository }}/issues/7253)) +- Add custom debug strings for Jekyll objects ([#8473]({{ site.repository }}/issues/8473)) +- Debug reading data files in a site ([#8481]({{ site.repository }}/issues/8481)) + +### Bug Fixes +{: #bug-fixes-v4-2-0} + +- Replace nested conditional with guard clauses ([#8294]({{ site.repository }}/issues/8294)) +- Fix: security bump ([#8349]({{ site.repository }}/issues/8349)) +- Fix path matching regex in post_url Liquid tag ([#8375]({{ site.repository }}/issues/8375)) +- Enable `Performance/ChainArrayAllocation` cop ([#8404]({{ site.repository }}/issues/8404)) +- Enable Lint/NoReturnInBeginEndBlocks Cop ([#8457]({{ site.repository }}/issues/8457)) +- Generate items from `site.include` list only once ([#8463]({{ site.repository }}/issues/8463)) +- Explicitly return nil after site process phase ([#8472]({{ site.repository }}/issues/8472)) + +### Optimization Fixes +{: #optimization-fixes-v4-2-0} + +- Implement custom delegators for drop methods ([#8183]({{ site.repository }}/issues/8183)) +- Handle `nil` argument to `Jekyll.sanitized_path` ([#8415]({{ site.repository }}/issues/8415)) +- Cache `Jekyll.sanitized_path` ([#8424]({{ site.repository }}/issues/8424)) +- Memoize array of drop getter method names ([#8421]({{ site.repository }}/issues/8421)) +- Reduce string allocations from the `link` tag ([#8387]({{ site.repository }}/issues/8387)) +- Optimize parsing of parameters in `include` tag ([#8192]({{ site.repository }}/issues/8192)) +- Stash documents `write?` attribute in a variable ([#8389]({{ site.repository }}/issues/8389)) +- Reduce string allocations from generating doc URLs ([#8392]({{ site.repository }}/issues/8392)) +- Check if site is in incremental mode optimally ([#8401]({{ site.repository }}/issues/8401)) +- Utilize flexibility of `Site#in_dest_dir` ([#8403]({{ site.repository }}/issues/8403)) +- Reduce allocations from rendering item as liquid ([#8406]({{ site.repository }}/issues/8406)) +- Compute relative_path of pages using PathManager ([#8408]({{ site.repository }}/issues/8408)) +- Reduce allocation from `normalize_whitespace` filter ([#8400]({{ site.repository }}/issues/8400)) +- Use `Regexp#match?` when `MatchData` is not required ([#8427]({{ site.repository }}/issues/8427)) +- Check default front matter scope against symbols ([#8393]({{ site.repository }}/issues/8393)) +- Stash frequently used `Drop` setter keys for reuse ([#8394]({{ site.repository }}/issues/8394)) +- Memoize defaults computed for Convertibles ([#8451]({{ site.repository }}/issues/8451)) +- Reduce array allocations from merging categories ([#8453]({{ site.repository }}/issues/8453)) +- Memoize destination of pages, documents and staticfiles ([#8458]({{ site.repository }}/issues/8458)) +- Reduce allocations from computing item property ([#8485]({{ site.repository }}/issues/8485)) +- Optimize `Page#dir` with a private method ([#8489]({{ site.repository }}/issues/8489)) +- Stash attribute hash for Liquid computed for pages ([#8497]({{ site.repository }}/issues/8497)) + +### Development Fixes +{: #development-fixes-v4-2-0} + +- Update cucumber gem to version 4.1 ([#8278]({{ site.repository }}/issues/8278)) +- Move permalink styles data to constant ([#8282]({{ site.repository }}/issues/8282)) +- Update rubocop gem to 0.87.1 ([#8287]({{ site.repository }}/issues/8287)) +- Update RuboCop to-do file ([#8296]({{ site.repository }}/issues/8296)) +- Fix `rake console` generating LoadError ([#8312]({{ site.repository }}/issues/8312)) +- Configure Performance cops ([#8369]({{ site.repository }}/issues/8369)) +- Update rubocop gem to 0.90.0 ([#8313]({{ site.repository }}/issues/8313)) +- Refactor `Jekyll::Utils::Platforms` ([#7236]({{ site.repository }}/issues/7236)) +- Bump RuboCop to v0.91.x ([#8391]({{ site.repository }}/issues/8391)) +- Add workflow to build and profile third-party repo ([#8398]({{ site.repository }}/issues/8398)) +- Bump RuboCop to v0.92.x +- Update cucumber gem version to 5.1.2 ([#8413]({{ site.repository }}/issues/8413)) +- Fix test suite compatibility with JRuby ([#8418]({{ site.repository }}/issues/8418)) +- chore(deps): bump Rubocop to 0.93.0 ([#8430]({{ site.repository }}/issues/8430)) +- Use Ruby 2.7.1 in GitHub Actions ([#8444]({{ site.repository }}/issues/8444)) +- Test that Liquid expressions are not deeply evaled ([#8292]({{ site.repository }}/issues/8292)) +- Test rendering arbitrary Liquid variables by default ([#7414]({{ site.repository }}/issues/7414)) +- Migrate TravisCI jobs to GitHub Actions ([#8492]({{ site.repository }}/issues/8492)) + +### Documentation + +- Update pointer to special permalink variables for collections ([#8274]({{ site.repository }}/issues/8274)) +- Fix special treatment for 'page 1' in docs of pagination ([#8230]({{ site.repository }}/issues/8230)) +- Add Formcake to forms section ([#8283]({{ site.repository }}/issues/8283)) +- Add a note on the rendering process in the docs ([#8291]({{ site.repository }}/issues/8291)) +- Add refactoring type to PULL_REQUEST_TEMPLATE ([#8297]({{ site.repository }}/issues/8297)) +- Update resources.md ([#7864]({{ site.repository }}/issues/7864)) +- Extra apostrophes in an URL ([#8319]({{ site.repository }}/issues/8319)) +- Clarify target of subordinate clause ([#8320]({{ site.repository }}/issues/8320)) +- Cherry-pick commits from conflicting branch `docs-40` +- Update documentation on third party site ([#8352]({{ site.repository }}/issues/8352)) +- Update default.md with info requested in [#8314]({{ site.repository }}/issues/8314) ([#8353]({{ site.repository }}/issues/8353)) +- Clarify description of `safe` option ([#8354]({{ site.repository }}/issues/8354)) +- Simplifying the Git post-receive hook-example ([#8358]({{ site.repository }}/issues/8358)) +- Add missing doc for build and serve commands ([#8365]({{ site.repository }}/issues/8365)) +- Docs Review: Getting Started ([#8372]({{ site.repository }}/issues/8372)) +- Add note about rebooting system after installation ([#8359]({{ site.repository }}/issues/8359)) +- Use data file to render table at `/docs/configuration/options/#global-configuration` ([#8377]({{ site.repository }}/issues/8377)) +- Use data file(s) to render table(s) at `/docs/configuration/options/` ([#8380]({{ site.repository }}/issues/8380)) +- Improve maintainability of config option data ([#8383]({{ site.repository }}/issues/8383)) +- Remove CircleCI v1 docs ([#8410]({{ site.repository }}/issues/8410)) +- Remove `NOKOGIRI_USE_SYSTEM_LIBRARIES` from Travis CI docs ([#8409]({{ site.repository }}/issues/8409)) +- Add links to all Jekyll themes on GitHub tagged with #jekyll-theme ([#8447]({{ site.repository }}/issues/8447)) +- Document initializing project Gemfile from scratch ([#8450]({{ site.repository }}/issues/8450)) +- Document installation of additional dependencies for installing Jekyll on Fedora ([#8456]({{ site.repository }}/issues/8456)) +- Improve documentation on Hooks in Jekyll ([#8467]({{ site.repository }}/issues/8467)) +- Build docs site with GitHub Actions ([#8201]({{ site.repository }}/issues/8201)) +- Add link to Assets page from `_sass` section in `_docs/structure.md` ([#8486]({{ site.repository }}/issues/8486)) + +### Site Enhancements +{: #site-enhancements-v4-2-0} + +- Fix rendering of *showcase* images ([#8504]({{ site.repository }}/issues/8504)) + + +## 4.1.1 / 2020-06-24 +{: #v4-1-1} + +### Bug Fixes +{: #bug-fixes-v4-1-1} + +- Disable page excerpts by default ([#8222]({{ site.repository }}/issues/8222)) +- Revert introduction of PageDrop ([#8221]({{ site.repository }}/issues/8221)) +- Don't generate excerpts for non-html pages ([#8234]({{ site.repository }}/issues/8234)) +- Make page excerpts consistent with doc excerpts ([#8236]({{ site.repository }}/issues/8236)) + +### Documentation + +- Replace deprecated 'show' command with 'info' ([#8235]({{ site.repository }}/issues/8235)) +- Change name to ▲Vercel ([#8247]({{ site.repository }}/issues/8247)) +- Add language and examples to describe how to use the configuration op… ([#8249]({{ site.repository }}/issues/8249)) +- Fix missing yaml front matter colon and adjust/add clarifying language. ([#8250]({{ site.repository }}/issues/8250)) +- correct typo ([#8261]({{ site.repository }}/issues/8261)) +- Allow hyperlinks to specific filter documentation ([#8231]({{ site.repository }}/issues/8231)) +- Update link to Netlify step-by-step guide ([#8264]({{ site.repository }}/issues/8264)) +- Fix grammar in documentation section ([#8265]({{ site.repository }}/issues/8265)) + +### Site Enhancements +{: #site-enhancements-v4-1-1} + +- Including correct Sketch website ([#8241]({{ site.repository }}/issues/8241)) +- Release post for v4.1.1 ([#8243]({{ site.repository }}/issues/8243)) + +### Development Fixes +{: #development-fixes-v4-1-1} + +- Bump RuboCop to v0.85.x ([#8223]({{ site.repository }}/issues/8223)) +- Expect drive letter only on vanilla windows ([#8227]({{ site.repository }}/issues/8227)) + + +## 4.1.0 / 2020-05-27 +{: #v4-1-0} + +### Bug Fixes +{: #bug-fixes-v4-1-0} + +- Memoize `absolute_url` and `relative_url` filters ([#7793]({{ site.repository }}/issues/7793)) +- Fix documentation comment for `Jekyll::Converters::Identity` ([#7883]({{ site.repository }}/issues/7883)) +- Optimize `Jekyll::Filters#item_property` ([#7696]({{ site.repository }}/issues/7696)) +- Allow multiple binary operators in `where_exp` filter ([#8047]({{ site.repository }}/issues/8047)) +- Fix documents custom-ordering logic ([#8028]({{ site.repository }}/issues/8028)) +- Use `layout.path` when rendering the Liquid layout ([#8069]({{ site.repository }}/issues/8069)) +- Reduce array allocations from `StaticFile#path` ([#8083]({{ site.repository }}/issues/8083)) +- Simplify `Jekyll::Renderer#validate_layout` ([#8064]({{ site.repository }}/issues/8064)) +- Add static file's basename to its `url_placeholder` ([#7908]({{ site.repository }}/issues/7908)) +- Clear cached Liquid template scope before render ([#7967]({{ site.repository }}/issues/7967)) +- Cache `URLFilter` results of string inputs per site ([#7990]({{ site.repository }}/issues/7990)) +- Use `platforms` instead of `install_if` in Gemfile ([#8140]({{ site.repository }}/issues/8140)) +- Config include trailing slash ([#8113]({{ site.repository }}/issues/8113)) +- Improve path normalization in liquid_renderer ([#8075]({{ site.repository }}/issues/8075)) +- Switch slugify regex to support more Unicode character groups ([#8167]({{ site.repository }}/issues/8167)) +- Check if entry is a directory once per enumerator ([#8177]({{ site.repository }}/issues/8177)) +- Filter out exclusively excluded entries sooner ([#7482]({{ site.repository }}/issues/7482)) +- Return `relative_url` if site.url is an empty string ([#7988]({{ site.repository }}/issues/7988)) +- Configure kramdown toc_levels as array by default ([#8015]({{ site.repository }}/issues/8015)) +- Reduce `Pathname` objects from front matter defaults ([#8067]({{ site.repository }}/issues/8067)) +- Simplify `Jekyll::Hooks.trigger` logic ([#8044]({{ site.repository }}/issues/8044)) +- Quicker categories for documents without superdirs ([#7987]({{ site.repository }}/issues/7987)) +- Reduce `Jekyll::Renderer` instances during a build ([#7570]({{ site.repository }}/issues/7570)) +- Escape regex characters in paths to match ([#8138]({{ site.repository }}/issues/8138)) +- Provide invokables for common drop query keys ([#8165]({{ site.repository }}/issues/8165)) +- Optimize path sanitization of default front matter ([#8154]({{ site.repository }}/issues/8154)) +- Initialize static files' data hash only if needed ([#8188]({{ site.repository }}/issues/8188)) +- Initialize include-files as Jekyll objects ([#8158]({{ site.repository }}/issues/8158)) + +### Minor Enhancements +{: #minor-enhancements-v4-1-0} + +- serve: add support for ECC certificates ([#7768]({{ site.repository }}/issues/7768)) +- Update `item_property` to recognize integers ([#7878]({{ site.repository }}/issues/7878)) +- Include `_config.yml` in a new theme's gemspec ([#7865]({{ site.repository }}/issues/7865)) +- Add an option to easily disable disk-cache ([#7928]({{ site.repository }}/issues/7928)) +- Optimize markdown parsing with Kramdown by reusing the options and parser objects ([#8013]({{ site.repository }}/issues/8013)) +- Add `PageDrop` to provide Liquid templates with data ([#7992]({{ site.repository }}/issues/7992)) +- Optimize `Kramdown::JekyllDocument#to_html` calls ([#8041]({{ site.repository }}/issues/8041)) +- Configure default language for syntax-highlighting ([#8035]({{ site.repository }}/issues/8035)) +- Remove dev dependencies from new theme-gem gemspec ([#8042]({{ site.repository }}/issues/8042)) +- Allow disabling import of theme configuration ([#8131]({{ site.repository }}/issues/8131)) +- Allow excerpts to be generated for `Page` objects ([#7642]({{ site.repository }}/issues/7642)) +- Profile various stages of a site's build process ([#6760]({{ site.repository }}/issues/6760)) +- Add find filters to optimize where-first chains ([#8171]({{ site.repository }}/issues/8171)) +- Make `number_of_words` filter respect CJK characters ([#7813]({{ site.repository }}/issues/7813)) +- Allow extensionless document in a strict site ([#7950]({{ site.repository }}/issues/7950)) +- Add `:slugified_categories` URL placeholder ([#8094]({{ site.repository }}/issues/8094)) + +### Documentation + +- Add dropped 'title: Staff' to the code ([#7805]({{ site.repository }}/issues/7805)) +- Clarify docs for static files in collection ([#7812]({{ site.repository }}/issues/7812)) +- Rephrase the CircleCI v2 section ([#7815]({{ site.repository }}/issues/7815)) +- Update old GitHub wiki URL with new one ([#7823]({{ site.repository }}/issues/7823)) +- Update JekyllConf page with 2019 talks ([#7826]({{ site.repository }}/issues/7826)) +- link for memberships ([#7825]({{ site.repository }}/issues/7825)) +- Doc: minor fix, should be greater or equal to min version ([#7856]({{ site.repository }}/issues/7856)) +- Update third-party.md - Fix broken link ([#7857]({{ site.repository }}/issues/7857)) +- clarify _config.yml/collections type ([#7873]({{ site.repository }}/issues/7873)) +- Replace backticks with HTML tags in data file ([#7879]({{ site.repository }}/issues/7879)) +- add new theme source ([#7875]({{ site.repository }}/issues/7875)) +- fixed grammatical error (it's --> its) ([#7887]({{ site.repository }}/issues/7887)) +- Docs: Clarify organizing pages into subfolders ([#7896]({{ site.repository }}/issues/7896)) +- Disambiguate the placeholder of permalink ([#7906]({{ site.repository }}/issues/7906)) +- docs: add short serve command for livereload ([#7919]({{ site.repository }}/issues/7919)) +- docs: add options for watch and force polling ([#7918]({{ site.repository }}/issues/7918)) +- add install instructions for ArchLinux and openSUSE ([#7920]({{ site.repository }}/issues/7920)) +- Improve index page of Jekyll documentation ([#7926]({{ site.repository }}/issues/7926)) +- Include path in `jekyll new` commands (Usage docs) ([#7931]({{ site.repository }}/issues/7931)) +- Change `affect` to `effect` in the collections docs ([#7937]({{ site.repository }}/issues/7937)) +- Changed deprecated command in themes documentation ([#7941]({{ site.repository }}/issues/7941)) +- Adds some documentation for the `:clean`, `:on_obsolete` hook ([#7954]({{ site.repository }}/issues/7954)) +- docs: fix broken link ([#7955]({{ site.repository }}/issues/7955)) +- Corrected typo ([#7975]({{ site.repository }}/issues/7975)) +- docs: remove watch option in config ([#7940]({{ site.repository }}/issues/7940)) +- Correct a sentence in the documentation ([#7978]({{ site.repository }}/issues/7978)) +- Fix YAML representation of `group_by` result ([#7979]({{ site.repository }}/issues/7979)) +- Move `--baseurl` to build command options ([#7985]({{ site.repository }}/issues/7985)) +- Correct documentation of filters ([#7989]({{ site.repository }}/issues/7989)) +- Document sorting two documents by their `date` ([#7870]({{ site.repository }}/issues/7870)) +- Fix English grammar error ([#7994]({{ site.repository }}/issues/7994)) +- Update 03-front-matter.md ([#7996]({{ site.repository }}/issues/7996)) +- Add Kentico Kontent CMS integration to resources ([#8000]({{ site.repository }}/issues/8000)) +- Update 07-assets.md ([#7413]({{ site.repository }}/issues/7413)) +- Fix file references in Step by Step Tutorial's Assets step ([#8007]({{ site.repository }}/issues/8007)) +- docs: improve highlighting of code blocks ([#8017]({{ site.repository }}/issues/8017)) +- remove leading slash from Sass file location ([#8021]({{ site.repository }}/issues/8021)) +- [Docs] Fix asset link ref in step-by-step tutorial ([#8026]({{ site.repository }}/issues/8026)) +- Corrected command to modify PATH ([#8029]({{ site.repository }}/issues/8029)) +- Corrected command to modify PATH ([#8030]({{ site.repository }}/issues/8030)) +- Docs: Render full contents of just the latest post ([#8032]({{ site.repository }}/issues/8032)) +- docs: improvements for note boxes ([#8037]({{ site.repository }}/issues/8037)) +- Non-deprecated `vendor/bundle` path configuration ([#8048]({{ site.repository }}/issues/8048)) +- Update 09-collections.md ([#8060]({{ site.repository }}/issues/8060)) +- Remove extra paragraph tags ([#8063]({{ site.repository }}/issues/8063)) +- Add default front matter for tutorials collection ([#8081]({{ site.repository }}/issues/8081)) +- Create CSV to table tutorial ([#8090]({{ site.repository }}/issues/8090)) +- Add version badge for Custom Sorting of Documents ([#8098]({{ site.repository }}/issues/8098)) +- Docs: Fix grammar in `_docs/front-matter.md` ([#8097]({{ site.repository }}/issues/8097)) +- Update variables.md ([#8106]({{ site.repository }}/issues/8106)) +- Add help about Gentoo/Linux ([#8002]({{ site.repository }}/issues/8002)) +- Update documentation on third party site ([#8122]({{ site.repository }}/issues/8122)) +- Added Clear Linux ([#8132]({{ site.repository }}/issues/8132)) +- Added note about OS specific installation instructions. ([#8135]({{ site.repository }}/issues/8135)) +- Fix broken URL in the Resources Page on the Documentation Site ([#8136]({{ site.repository }}/issues/8136)) +- Docs: Deploy Jekyll site with GitHub Actions ([#8119]({{ site.repository }}/issues/8119)) +- Clarify `bundle config` in Bundler tutorial ([#8150]({{ site.repository }}/issues/8150)) +- docs: update your-first-plugin.md ([#8147]({{ site.repository }}/issues/8147)) +- Fix typo in documentation on GitHub Actions ([#8162]({{ site.repository }}/issues/8162)) +- Ease discovery of CLI commands (in their entirety) ([#8178]({{ site.repository }}/issues/8178)) +- Remove `sudo` from Travis CI tutorial ([#8187]({{ site.repository }}/issues/8187)) +- Add GitLab Pages to 3rd party list ([#8191]({{ site.repository }}/issues/8191)) +- docs: add 21yunbox for deployment ([#8193]({{ site.repository }}/issues/8193)) +- Improve documentation on tags and categories ([#8196]({{ site.repository }}/issues/8196)) + +### Development Fixes +{: #development-fixes-v4-1-0} + +- Ci/GitHub actions ([#7822]({{ site.repository }}/issues/7822)) +- Rubocop version upgrade ([#7846]({{ site.repository }}/issues/7846)) +- Split action steps to avoid using `&&` on Windows ([#7885]({{ site.repository }}/issues/7885)) +- Upgrade rake to use version 13 ([#7910]({{ site.repository }}/issues/7910)) +- Update dependency constraint to allow RuboCop v0.76 ([#7893]({{ site.repository }}/issues/7893)) +- Use bash executable consistently ([#7909]({{ site.repository }}/issues/7909)) +- Test with JRuby 9.2.9.0 ([#7779]({{ site.repository }}/issues/7779)) +- Bump RuboCop to v0.79.x ([#7970]({{ site.repository }}/issues/7970)) +- Remove post-install message from gemspec ([#7974]({{ site.repository }}/issues/7974)) +- Attain Ruby 3.0 compatibility ([#7948]({{ site.repository }}/issues/7948)) +- Test `where` filter handling numeric property values ([#7821]({{ site.repository }}/issues/7821)) +- chore(deps): rubocop 0.80.0 ([#8012]({{ site.repository }}/issues/8012)) +- Update unit tests for Kramdown-based converter ([#8014]({{ site.repository }}/issues/8014)) +- Add Visual Studio Code Development Container ([#8016]({{ site.repository }}/issues/8016)) +- chore: simplify require for `Jekyll::VERSION` ([#8057]({{ site.repository }}/issues/8057)) +- Remove version-constraint relaxation for i18n gem ([#8055]({{ site.repository }}/issues/8055)) +- Mirror `spec.homepage` as `metadata["homepage_uri"]` ([#8056]({{ site.repository }}/issues/8056)) +- Bump Ruby versions on Travis builds ([#8088]({{ site.repository }}/issues/8088)) +- chore(ci): cache dependencies ([#8168]({{ site.repository }}/issues/8168)) + +### Site Enhancements +{: #site-enhancements-v4-1-0} + +- Optimize rendering of the documentation site ([#8020]({{ site.repository }}/issues/8020)) +- Utilize `relative_url` filter in documentation site ([#8089]({{ site.repository }}/issues/8089)) +- Render tutorial metadata in documentation site ([#8092]({{ site.repository }}/issues/8092)) +- Improve syntax-highlighting in documentation site ([#8079]({{ site.repository }}/issues/8079)) +- Site: Filter through just the *docs* collection ([#8170]({{ site.repository }}/issues/8170)) + + +## 4.0.1 / 2020-05-08 +{: #v4-0-1} + +### Bug Fixes +{: #bug-fixes-v4-0-1} + +- Prevent console warning with Ruby 2.7 ([#8124]({{ site.repository }}/issues/8124)) +- Clear cached Liquid template scope before render ([#8141]({{ site.repository }}/issues/8141)) +- Add static file's basename to its url_placeholder ([#8142]({{ site.repository }}/issues/8142)) +- Update item_property to recognize integers ([#8160]({{ site.repository }}/issues/8160)) + +### Development Fixes +{: #development-fixes-v4-0-1} + +- Fix Kramdown converter based tests for v4.0.x ([#8143]({{ site.repository }}/issues/8143)) + + +## 3.9.2 / 2022-03-27 +{: #v3-9-2} + +### Bug Fixes +{: #bug-fixes-v3-9-2} + +- Lock `http_parser.rb` gem to `v0.6.x` on JRuby ([#8943]({{ site.repository }}/issues/8943)) +- Backport [#8756]({{ site.repository }}/issues/8756) for v3.9.x: Respect collections_dir config within include tag ([#8795]({{ site.repository }}/issues/8795)) +- Backport [#8965]({{ site.repository }}/issues/8965) for v3.9.x: Fix response header for content served via `jekyll serve` ([#8976]({{ site.repository }}/issues/8976)) + +### Development Fixes +{: #development-fixes-v3-9-2} + +- Update and fix CI for `3.9-stable` on Ruby 3.x ([#8942]({{ site.repository }}/issues/8942)) +- Fix CI for commits to `3.9-stable` branch ([#8788]({{ site.repository }}/issues/8788)) + + +## 3.9.1 / 2021-04-08 +{: #v3-9-1} + +### Bug Fixes +{: #bug-fixes-v3-9-1} + +- Backport [#8618]({{ site.repository }}/issues/8618) for v3.9.x: Update include tag to be more permissive ([#8629]({{ site.repository }}/issues/8629)) + + +## 3.9.0 / 2020-08-05 +{: #v3-9-0} + +### Minor Enhancements +{: #minor-enhancements-v3-9-0} + +- Allow use of kramdown v2 ([#8322]({{ site.repository }}/issues/8322)) +- Add default language for kramdown syntax highlighting ([#8325]({{ site.repository }}/issues/8325)) + + +## 3.8.7 / 2020-05-08 +{: #v3-8-7} + +### Bug Fixes +{: #bug-fixes-v3-8-7} + +- Prevent console warnings with Ruby 2.7 ([#8125]({{ site.repository }}/issues/8125)) + + +## 4.0.0 / 2019-08-19 +{: #v4-0-0} + +### Major Enhancements +{: #major-enhancements-v4-0-0} + +- Drop ruby 2.3 ([#7454]({{ site.repository }}/issues/7454)) +- Drop support for Ruby 2.1 and 2.2 ([#6560]({{ site.repository }}/issues/6560)) +- Drop support for older versions of Rouge ([#6978]({{ site.repository }}/issues/6978)) +- Drop support for pygments as syntax-highlighter ([#7118]({{ site.repository }}/issues/7118)) +- Drop support for Redcarpet ([#6987]({{ site.repository }}/issues/6987)) +- Drop support for rdiscount ([#6988]({{ site.repository }}/issues/6988)) +- Drop support for `jekyll-watch-1.4.0` and older ([#7287]({{ site.repository }}/issues/7287)) +- Incorporate `relative_url` filter in `link` tag ([#6727]({{ site.repository }}/issues/6727)) +- Upgrade kramdown dependency to v2.x ([#7492]({{ site.repository }}/issues/7492)) +- Upgrade jekyll-sass-converter to v2.x - Sassc + sourcemaps ([#7778]({{ site.repository }}/issues/7778)) +- Upgrade i18n to v1.x ([#6931]({{ site.repository }}/issues/6931)) +- Add `Jekyll::Cache` class to handle caching on disk ([#7169]({{ site.repository }}/issues/7169)) +- Cache converted markdown ([#7159]({{ site.repository }}/issues/7159)) +- Cache: Do not dump undumpable objects ([#7190]({{ site.repository }}/issues/7190)) +- Cache matched defaults sets for given parameters ([#6888]({{ site.repository }}/issues/6888)) +- Ignore cache directory ([#7184]({{ site.repository }}/issues/7184)) +- Add `Site#in_cache_dir` helper method ([#7160]({{ site.repository }}/issues/7160)) +- Remove 'cache_dir' during `jekyll clean` ([#7158]({{ site.repository }}/issues/7158)) +- Cache parsed Liquid templates in memory ([#7136]({{ site.repository }}/issues/7136)) +- Only read layouts from source_dir or theme_dir ([#6788]({{ site.repository }}/issues/6788)) +- Allow custom sorting of collection documents ([#7427]({{ site.repository }}/issues/7427)) +- Always exclude certain paths from being processed ([#7188]({{ site.repository }}/issues/7188)) +- Remove Jekyll::Utils#strip_heredoc in favor of a Ruby > 2.3 built in ([#7584]({{ site.repository }}/issues/7584)) +- Incorporate `relative_url` within `post_url` tag ([#7589]({{ site.repository }}/issues/7589)) +- Remove patch to modify config for kramdown ([#7699]({{ site.repository }}/issues/7699)) + +### Minor Enhancements +{: #minor-enhancements-v4-0-0} + +- Enhance `--blank` scaffolding ([#7310]({{ site.repository }}/issues/7310)) +- Use `jekyll-compose` if installed ([#6932]({{ site.repository }}/issues/6932)) +- Disable Liquid via front matter ([#6824]({{ site.repository }}/issues/6824)) +- Configure cache_dir ([#7232]({{ site.repository }}/issues/7232)) +- ISO week date drops ([#5981]({{ site.repository }}/issues/5981)) +- Fix custom 404 page for GitHub pages ([#7132]({{ site.repository }}/issues/7132)) +- Load config file from within current theme-gem ([#7304]({{ site.repository }}/issues/7304)) +- Suggest re-running command with `--trace` on fail ([#6551]({{ site.repository }}/issues/6551)) +- Support for binary operators in where_exp filter ([#6998]({{ site.repository }}/issues/6998)) +- Automatically load `_config.toml` ([#7299]({{ site.repository }}/issues/7299)) +- Add vendor folder to a newly installed site's .gitignore ([#6968]({{ site.repository }}/issues/6968)) +- Output Jekyll Version while debugging ([#7173]({{ site.repository }}/issues/7173)) +- Memoize computing excerpt's relative_path ([#6951]({{ site.repository }}/issues/6951)) +- Skip processing posts that can not be read ([#7302]({{ site.repository }}/issues/7302)) +- Memoize the return value of Site#documents ([#7273]({{ site.repository }}/issues/7273)) +- Cache globbed paths in front matter defaults ([#7345]({{ site.repository }}/issues/7345)) +- Cache computed item property ([#7301]({{ site.repository }}/issues/7301)) +- Cleanup Markdown converter ([#7519]({{ site.repository }}/issues/7519)) +- Do not process Liquid in post excerpt when disabled in front matter ([#7146]({{ site.repository }}/issues/7146)) +- Liquefied link tag ([#6269]({{ site.repository }}/issues/6269)) +- Update item_property to return numbers as numbers instead of strings ([#6608]({{ site.repository }}/issues/6608)) +- Use `.markdown` extension for page templates ([#7126]({{ site.repository }}/issues/7126)) +- Add support for `*.xhtml` files ([#6854]({{ site.repository }}/issues/6854)) +- Allow i18n v0.9.5 and higher ([#7044]({{ site.repository }}/issues/7044)) +- Ignore permission error of /proc/version ([#7267]({{ site.repository }}/issues/7267)) +- Strip extra slashes via `Jekyll.sanitized_path` ([#7182]({{ site.repository }}/issues/7182)) +- Site template: remove default config for markdown ([#7285]({{ site.repository }}/issues/7285)) +- Add a custom inspect string for StaticFile objects ([#7422]({{ site.repository }}/issues/7422)) +- Remind user to include gem in the Gemfile on error ([#7476]({{ site.repository }}/issues/7476)) +- Search Front matter defaults for Page objects with relative_path ([#7261]({{ site.repository }}/issues/7261)) +- Lock use of `tzinfo` gem to v1.x ([#7521]({{ site.repository }}/issues/7521), [#7562]({{ site.repository }}/issues/7562)) +- Utilize absolute paths of user-provided file paths ([#7450]({{ site.repository }}/issues/7450)) +- Detect `nil` and empty values in objects with `where` filter ([#7580]({{ site.repository }}/issues/7580)) +- Initialize mutations for Drops only if necessary ([#7657]({{ site.repository }}/issues/7657)) +- Reduce Array allocations via Jekyll::Cleaner ([#7659]({{ site.repository }}/issues/7659)) +- Encode and unencode urls only as required ([#7654]({{ site.repository }}/issues/7654)) +- Reduce string allocations with better alternatives ([#7643]({{ site.repository }}/issues/7643)) +- Reduce allocations from Jekyll::Document instances ([#7625]({{ site.repository }}/issues/7625)) +- Add `type` attribute to Document instances ([#7406]({{ site.repository }}/issues/7406)) +- Reduce allocations from where-filter ([#7653]({{ site.repository }}/issues/7653)) +- Memoize SiteDrop#documents to reduce allocations ([#7697]({{ site.repository }}/issues/7697)) +- Add PathManager class to cache interim paths ([#7732]({{ site.repository }}/issues/7732)) +- Remove warnings and fixes for deprecated config ([#7440]({{ site.repository }}/issues/7440)) +- Delegate --profile tabulation to `terminal-table` ([#7627]({{ site.repository }}/issues/7627)) + +### Bug Fixes +{: #bug-fixes-v4-0-0} + +- Security: fix `include` bypass of `EntryFilter#filter` symlink check ([#7226]({{ site.repository }}/issues/7226)) +- Theme gems: ensure directories aren't symlinks ([#7419]({{ site.repository }}/issues/7419)) +- Add call to unused method `validate_options` in `commands/serve.rb` ([#7122]({{ site.repository }}/issues/7122)) +- Check if scope applies to type before given path ([#7263]({{ site.repository }}/issues/7263)) +- Document two methods, simplify one of the methods ([#7270]({{ site.repository }}/issues/7270)) +- Check key in collections only if it isn't "posts" ([#7277]({{ site.repository }}/issues/7277)) +- Interpolate Jekyll::Page subclass on inspection ([#7203]({{ site.repository }}/issues/7203)) +- Measure the no. of times a template gets rendered ([#7316]({{ site.repository }}/issues/7316)) +- Reduce array traversal in Jekyll::Reader ([#7157]({{ site.repository }}/issues/7157)) +- Re-implement handling Liquid blocks in excerpts ([#7250]({{ site.repository }}/issues/7250)) +- Documents should be able to render their date ([#7404]({{ site.repository }}/issues/7404)) +- Fix Interpreter warning from Jekyll::Renderer ([#7448]({{ site.repository }}/issues/7448)) +- Loggers should accept both numbers and symbols ([#6967]({{ site.repository }}/issues/6967)) +- Replace regex arg to :gsub with a string arg ([#7189]({{ site.repository }}/issues/7189)) +- Dont write static files from unrendered collection ([#7410]({{ site.repository }}/issues/7410)) +- Excerpt handling of custom and intermediate tags ([#7382]({{ site.repository }}/issues/7382)) +- Change future post loglevel to warn to help user narrow down issues ([#7527]({{ site.repository }}/issues/7527)) +- Handle files with trailing dots in their basename ([#7315]({{ site.repository }}/issues/7315)) +- Fix unnecessary allocations via StaticFileReader ([#7572]({{ site.repository }}/issues/7572)) +- Don't check if site URL is absolute if it is nil ([#7498]({{ site.repository }}/issues/7498)) +- Avoid unnecessary duplication of pages array ([#7272]({{ site.repository }}/issues/7272)) +- Memoize Site#post_attr_hash ([#7276]({{ site.repository }}/issues/7276)) +- Memoize Document#excerpt_separator ([#7569]({{ site.repository }}/issues/7569)) +- Optimize Document::DATE_FILENAME_MATCHER to match valid filenames ([#7292]({{ site.repository }}/issues/7292)) +- Escape valid special chars in a site's path name ([#7568]({{ site.repository }}/issues/7568)) +- Replace `name` in Page#inspect with relative_path ([#7434]({{ site.repository }}/issues/7434)) +- Log a warning when the slug is empty ([#7357]({{ site.repository }}/issues/7357)) +- Push Markdown link refs to excerpt only as required ([#7577]({{ site.repository }}/issues/7577)) +- Fix broken include_relative usage in excerpt ([#7633]({{ site.repository }}/issues/7633)) +- Initialize and reset glob_cache only as necessary ([#7658]({{ site.repository }}/issues/7658)) +- Revert memoizing Site#docs_to_write and #documents ([#7684]({{ site.repository }}/issues/7684)) +- Backport [#7684]({{ site.repository }}/issues/7684) for v3.8.x: Revert memoizing Site#docs_to_write and refactor #documents ([#7689]({{ site.repository }}/issues/7689)) +- Backport [#7213]({{ site.repository }}/issues/7213) and [#7633]({{ site.repository }}/issues/7633) for v3.8.x: Fix broken include_relative usage in excerpt ([#7690]({{ site.repository }}/issues/7690)) +- Don't read symlinks in site.include in safe mode ([#7711]({{ site.repository }}/issues/7711)) +- Replace `String#=~` with `String#match?` ([#7723]({{ site.repository }}/issues/7723)) +- Update log output for an invalid theme directory ([#7679]({{ site.repository }}/issues/7679)) +- Remove configuration of theme sass files from Core ([#7290]({{ site.repository }}/issues/7290)) +- Actually conditionally include liquid-c ([#7792]({{ site.repository }}/issues/7792)) +- Test number_like regex on stringified property ([#7788]({{ site.repository }}/issues/7788)) + +### Development Fixes +{: #development-fixes-v4-0-0} + +- Upgrade liquid-c to v4.0 ([#7375]({{ site.repository }}/issues/7375)) +- Bump RuboCop to v0.71.0 ([#7687]({{ site.repository }}/issues/7687)) +- Target Ruby 2.4 syntax ([#7583]({{ site.repository }}/issues/7583)) +- Fix: RuboCop offenses ([#7769]({{ site.repository }}/issues/7769)) +- Use communicative method parameters ([#7566]({{ site.repository }}/issues/7566)) +- Scan `assert_equal` methods and rectify any offenses with a custom RuboCop cop ([#7130]({{ site.repository }}/issues/7130)) +- CI: Test with Ruby 2.6 ([#7438]({{ site.repository }}/issues/7438)) +- CI: Test with Ruby 2.6 on AppVeyor ([#7518]({{ site.repository }}/issues/7518)) +- CI: Update RuboCop config ([#7050]({{ site.repository }}/issues/7050)) +- CI: Add a script to profile docs ([#7540]({{ site.repository }}/issues/7540)) +- CI(Appveyor): shallow clone with 5 last commits ([#7312]({{ site.repository }}/issues/7312)) +- CI: Test with oldest and latest Ruby only ([#7412]({{ site.repository }}/issues/7412)) +- CI: Update excludes for CodeClimate Analyses ([#7365]({{ site.repository }}/issues/7365)) +- CI: Lock Travis to Bundler-1.16.2 ([#7144]({{ site.repository }}/issues/7144)) +- CI: Bump tested version of JRuby to 9.2.7.0 ([#7612]({{ site.repository }}/issues/7612)) +- CI: Do not install docs on updating gems on Travis ([#7706]({{ site.repository }}/issues/7706)) +- Update gemspec ([#7425]({{ site.repository }}/issues/7425)) +- deps: relax version constraint on classifier-reborn gem ([#7471]({{ site.repository }}/issues/7471)) +- deps: update yajl-ruby ([#7278]({{ site.repository }}/issues/7278)) +- deps: bump yajl-ruby to v1.4.0 ([#6976]({{ site.repository }}/issues/6976)) +- Create symlink only if target is accessible ([#7429]({{ site.repository }}/issues/7429)) +- Switch to `:install_if` for wdm gem ([#7372]({{ site.repository }}/issues/7372)) +- Add cucumber feature to test include_relative tag ([#7213]({{ site.repository }}/issues/7213)) +- Small benchmark refactoring ([#7211]({{ site.repository }}/issues/7211)) +- Fix incorrectly passed arguments to assert_equal ([#7134]({{ site.repository }}/issues/7134)) +- fix up refute_equal call ([#7133]({{ site.repository }}/issues/7133)) +- Fix RuboCop offences in test files ([#7128]({{ site.repository }}/issues/7128)) +- Use assert_include ([#7093]({{ site.repository }}/issues/7093)) +- Remember to release docs gem ([#7066]({{ site.repository }}/issues/7066)) +- Useless privates removed ([#6768]({{ site.repository }}/issues/6768)) +- Load Rouge for TestKramdown ([#7007]({{ site.repository }}/issues/7007)) +- Update instructions for releasing docs Gem ([#6975]({{ site.repository }}/issues/6975)) +- We are not using Ruby 2.2 anymore ([#6977]({{ site.repository }}/issues/6977)) +- Remove unnecessary Jekyll::Page constant ([#6770]({{ site.repository }}/issues/6770)) +- Remove unused error class ([#6511]({{ site.repository }}/issues/6511)) +- Add a Cucumber feature for post_url tag ([#7586]({{ site.repository }}/issues/7586)) +- Generate a "TOTAL" row for build-profile table ([#7614]({{ site.repository }}/issues/7614)) +- Refactor Jekyll::Cache ([#7532]({{ site.repository }}/issues/7532)) +- Store list of expected extnames in a constant ([#7638]({{ site.repository }}/issues/7638)) +- Profile allocations from a build session ([#7646]({{ site.repository }}/issues/7646)) +- Update small typo in contributing.md ([#7671]({{ site.repository }}/issues/7671)) +- Remove override to Jekyll::Document#respond_to? ([#7695]({{ site.repository }}/issues/7695)) +- Update TestTags in sync with Rouge v3.4 ([#7709]({{ site.repository }}/issues/7709)) +- Use regexp to filter special entries ([#7702]({{ site.repository }}/issues/7702)) +- Reduce Array objects generated from utility method ([#7749]({{ site.repository }}/issues/7749)) +- Update mime.types ([#7756]({{ site.repository }}/issues/7756)) +- Replace redundant Array#map with Array#each ([#7761]({{ site.repository }}/issues/7761)) +- Reduce allocations by using #each_with_object ([#7758]({{ site.repository }}/issues/7758)) +- Memoize fallback_data for Drop ([#7728]({{ site.repository }}/issues/7728)) +- Use String#end_with? to check if entry is a backup ([#7701]({{ site.repository }}/issues/7701)) + +### Documentation + +- Refactor docs ([#7205]({{ site.repository }}/issues/7205)) +- Add a link to Giraffe Academy's tutorial ([#7325]({{ site.repository }}/issues/7325)) +- Do not advise users to install Jekyll outside of Bundler ([#6927]({{ site.repository }}/issues/6927)) +- Remove documentation for using Redcarpet ([#6990]({{ site.repository }}/issues/6990)) +- Install Docs that Work on MacOS 10.14 ([#7561]({{ site.repository }}/issues/7561)) +- Add Installation Instructions for Ubuntu ([#6925]({{ site.repository }}/issues/6925)) +- Don't prompt for sudo when installing with Ubuntu WSL ([#6781]({{ site.repository }}/issues/6781)) +- Installation instructions for Fedora ([#7198]({{ site.repository }}/issues/7198)) +- Update Windows install docs ([#6926]({{ site.repository }}/issues/6926)) +- List all standard liquid filters ([#7333]({{ site.repository }}/issues/7333)) +- List all static files variables ([#7002]({{ site.repository }}/issues/7002)) +- Improve how to include Rouge stylesheets ([#7752]({{ site.repository }}/issues/7752)) +- Mention CommonMark plugins ([#7418]({{ site.repository }}/issues/7418)) +- Add TSV to list of supported _data files. ([#7168]({{ site.repository }}/issues/7168)) +- How to deploy using pre-push git hook ([#7179]({{ site.repository }}/issues/7179)) +- Hosting with AWS Amplify ([#7510]({{ site.repository }}/issues/7510)) +- CircleCI deployment through CircleCI v2 ([#7024]({{ site.repository }}/issues/7024)) +- GitHub Pages: use themes from other repos ([#7112]({{ site.repository }}/issues/7112)) +- Document page.dir and page.name ([#7373]({{ site.repository }}/issues/7373)) +- Document custom tag blocks ([#7359]({{ site.repository }}/issues/7359)) +- Document converter methods ([#7289]({{ site.repository }}/issues/7289)) +- Document {% raw %}`{{ page.collection }}`{% endraw %} ([#7430]({{ site.repository }}/issues/7430)) +- Document Jekyll Filters with YAML data ([#7335]({{ site.repository }}/issues/7335)) +- Document where Jekyll looks for layouts in a site ([#7564]({{ site.repository }}/issues/7564)) +- plugin: liquid tag jekyll-flickr ([#6946]({{ site.repository }}/issues/6946)) +- plugin: jekyll-target-blank ([#7046]({{ site.repository }}/issues/7046)) +- plugin: json-get. ([#7086]({{ site.repository }}/issues/7086)) +- plugin: `jekyll-info` ([#7091]({{ site.repository }}/issues/7091)) +- plugin: jekyll-xml-source ([#7114]({{ site.repository }}/issues/7114)) +- plugin: jekyll-firstimage filter ([#7127]({{ site.repository }}/issues/7127)) +- plugin: CAT ([#7011]({{ site.repository }}/issues/7011)) +- Resources: Statictastic ([#7593]({{ site.repository }}/issues/7593)) +- Resources: Bonsai Search ([#7543]({{ site.repository }}/issues/7543)) +- Resources: Formspark ([#7601]({{ site.repository }}/issues/7601)) +- Resources: Jekpack([#7598]({{ site.repository }}/issues/7598)) +- Resources: formX ([#7536]({{ site.repository }}/issues/7536)) +- Resources: 99inbound's Jekyll post ([#7348]({{ site.repository }}/issues/7348)) +- Resources: CloudSh ([#7497]({{ site.repository }}/issues/7497)) +- Community: DEV Community's Jekyll tag ([#7139]({{ site.repository }}/issues/7139)) +- Showcase: developer.spotify.com ([#7217]({{ site.repository }}/issues/7217)) +- Showcase: Isomer ([#7300]({{ site.repository }}/issues/7300)) +- Add version number for group_by_exp doc ([#6956]({{ site.repository }}/issues/6956)) +- Updated nginx configuration for custom-404-page documentation ([#6994]({{ site.repository }}/issues/6994)) +- Clarify definition of 'draft' ([#7037]({{ site.repository }}/issues/7037)) +- _drafts need to be contained within the custom collection directory ([#6985]({{ site.repository }}/issues/6985)) +- Updated to supported version ([#7031]({{ site.repository }}/issues/7031)) +- Add Hints for some Improved Travis Config in Doc ([#7049]({{ site.repository }}/issues/7049)) +- Update travis-ci.md to point out "this is an example Gemfile" ([#7089]({{ site.repository }}/issues/7089)) +- Instructions to view theme’s files under Linux ([#7095]({{ site.repository }}/issues/7095)) +- Use a real theme in the example ([#7125]({{ site.repository }}/issues/7125)) +- Update docs about post creation ([#7138]({{ site.repository }}/issues/7138)) +- Initialize upgrading doc for v4.0 ([#7140]({{ site.repository }}/issues/7140)) +- Add version badge for date filters with ordinal ([#7162]({{ site.repository }}/issues/7162)) +- Corrected sample usage of postfiles ([#7181]({{ site.repository }}/issues/7181)) +- Resolve "Unable to locate package ruby2.4" error ([#7196]({{ site.repository }}/issues/7196)) +- Correct stylesheet url in tutorial step 7 ([#7210]({{ site.repository }}/issues/7210)) +- Removes quotes from markdown for assets ([#7223]({{ site.repository }}/issues/7223)) +- Clarified front matter requirement ([#7234]({{ site.repository }}/issues/7234)) +- Explicit location of where to create blog.html ([#7241]({{ site.repository }}/issues/7241)) +- Reference the build command options that allows multiple config files ([#7266]({{ site.repository }}/issues/7266)) +- Add more issue template(s) and pull request template ([#7269]({{ site.repository }}/issues/7269)) +- Suggest sites use OpenSSL instead of GnuTLS for their site's CI ([#7010]({{ site.repository }}/issues/7010)) +- Fix broken Contributors link in README.markdown ([#7200]({{ site.repository }}/issues/7200)) +- Add title tag to item in RSS template ([#7282]({{ site.repository }}/issues/7282)) +- Add link tag to item in RSS template ([#7291]({{ site.repository }}/issues/7291)) +- Remove redundant instruction comment ([#7342]({{ site.repository }}/issues/7342)) +- Textile is only supported through a converter plugin ([#7003]({{ site.repository }}/issues/7003)) +- Add recursive navigation tutorial ([#7720]({{ site.repository }}/issues/7720)) +- Remove installation instructions with Homebrew ([#7381]({{ site.repository }}/issues/7381)) +- Fix dead link and misleading prose ([#7383]({{ site.repository }}/issues/7383)) +- Fix content management section ([#7385]({{ site.repository }}/issues/7385)) +- Apply ruby official guide documents ([#7393]({{ site.repository }}/issues/7393)) +- Fix group_by_exp filter example ([#7394]({{ site.repository }}/issues/7394)) +- Remove alt attribute from a tags ([#7407]({{ site.repository }}/issues/7407)) +- Fix BASH code-block in ubuntu.md ([#7420]({{ site.repository }}/issues/7420)) +- zlib is missing ([#7428]({{ site.repository }}/issues/7428)) +- Fixed unnecessary articles and pronouns ([#7466]({{ site.repository }}/issues/7466)) +- Store SSL key and cert in site source ([#7473]({{ site.repository }}/issues/7473)) +- Fix typo in tutorial for converting existing site ([#7524]({{ site.repository }}/issues/7524)) +- Check if var exists before include tag ([#7530]({{ site.repository }}/issues/7530)) +- Clarify docs on collections regarding the need for front matter ([#7538]({{ site.repository }}/issues/7538)) +- Fix incorrect Windows path in themes.md ([#7525]({{ site.repository }}/issues/7525)) +- Addresses bundle not found. ([#7351]({{ site.repository }}/issues/7351)) +- Update the contribution docs for draft pull requests ([#7619]({{ site.repository }}/issues/7619)) +- Data file section adds TSV ([#7640]({{ site.repository }}/issues/7640)) +- Indicate where the _sass folder is by default ([#7644]({{ site.repository }}/issues/7644)) +- Docs: add version tags to new placeholders ([#5981]({{ site.repository }}/issues/5981)) for permalinks ([#7647]({{ site.repository }}/issues/7647)) +- Solve "GitHub Page build failure" in 10-deployment.md ([#7648]({{ site.repository }}/issues/7648)) +- fix link to Site Source config ([#7708]({{ site.repository }}/issues/7708)) +- Introduce frontmatter in step 2 ([#7704]({{ site.repository }}/issues/7704)) +- Add @ashmaroli to Core Team listing ([#7398]({{ site.repository }}/issues/7398)) +- Link to Tidelift in site's footer ([#7377]({{ site.repository }}/issues/7377)) +- Link to OpenCollective backing ([#7378]({{ site.repository }}/issues/7378) +- Link to sponsor listing in README ([#7405]({{ site.repository }}/issues/7405)) +- Adjust team page listings ([#7395]({{ site.repository }}/issues/7395)) +- Updates to CODE OF CONDUCT (v1.4.0) ([#7105]({{ site.repository }}/issues/7105)) +- More inclusive writing ([#7283]({{ site.repository }}/issues/7283)) +- Update Ruby version used in Travis-CI example ([#7783]({{ site.repository }}/issues/7783)) +- Documentation for binary operators in where_exp ([#7786]({{ site.repository }}/issues/7786)) +- Adding SmartForms as Forms service ([#7794]({{ site.repository }}/issues/7794)) + +### Site Enhancements +{: #site-enhancements-v4-0-0} + +- Better Performance ([#7388]({{ site.repository }}/issues/7388)) +- Add some minor improvements to image loading in Showcase page ([#7214]({{ site.repository }}/issues/7214)) +- Simplify assigning classname to docs' aside-links ([#7609]({{ site.repository }}/issues/7609)) +- Simplify couple of includes in the docs site ([#7607]({{ site.repository }}/issues/7607)) +- Avoid generating empty classnames ([#7610]({{ site.repository }}/issues/7610)) +- Minimize rendering count ([#7343]({{ site.repository }}/issues/7343)) + +### Release + +- Jekyll v4.0 release ([#7782]({{ site.repository }}/issues/7782)) +- Release post for v4.0.0 beta1 ([#7716]({{ site.repository }}/issues/7716)) +- Release post for v4.0.0.pre.alpha1 ([#7574]({{ site.repository }}/issues/7574)) +- Release post for v3.8.0 ([#6849]({{ site.repository }}/issues/6849)) +- Release post for v3.6.3, v3.7.4 and v3.8.4 ([#7259]({{ site.repository }}/issues/7259)) +- Post: v4.0 development ([#6934]({{ site.repository }}/issues/6934)) + + +## 3.8.6 / 2019-07-02 +{: #v3-8-6} + +### Bug Fixes +{: #bug-fixes-v3-8-6} + +- Update log output for an invalid theme directory ([#7734]({{ site.repository }}/issues/7734)) +- Memoize `SiteDrop#documents` to reduce allocations ([#7722]({{ site.repository }}/issues/7722)) +- Excerpt handling of custom and intermediate tags ([#7467]({{ site.repository }}/issues/7467)) +- Escape valid special chars in a site's path name ([#7573]({{ site.repository }}/issues/7573)) +- Revert memoizing `Site#docs_to_write` and refactor `#documents` ([#7689]({{ site.repository }}/issues/7689)) +- Fix broken `include_relative` usage in excerpt ([#7690]({{ site.repository }}/issues/7690)) +- Install platform-specific gems as required (3c06609406) + +### Security Fixes +{: #security-fixes-v3-8-6} + +- Theme gems: ensure directories aren't symlinks ([#7424]({{ site.repository }}/issues/7424)) + + +## 3.8.5 / 2018-11-04 +{: #v3-8-5} + +### Bug Fixes +{: #bug-fixes-v3-8-5} + +- Re-implement handling Liquid blocks in excerpts ([#7250]({{ site.repository }}/issues/7250)) + + +## 3.8.4 / 2018-09-18 +{: #v3-8-4} + +### Bug Fixes +{: #bug-fixes-v3-8-4} + +- 3.8.x: security: fix `include` bypass of `EntryFilter#filter` symlink check ([#7228]({{ site.repository }}/issues/7228)) + + +## 3.8.3 / 2018-06-05 +{: #v3-8-3} + +### Bug Fixes +{: #bug-fixes-v3-8-3} + +- Fix --unpublished not affecting collection documents ([#7027]({{ site.repository }}/issues/7027)) + + +## 3.8.2 / 2018-05-18 +{: #v3-8-2} + +### Development Fixes +{: #development-fixes-v3-8-2} + +- Update rubocop version ([#7016]({{ site.repository }}/issues/7016)) + +### Bug Fixes +{: #bug-fixes-v3-8-2} + +- Add whitespace control to LIQUID_TAG_REGEX ([#7015]({{ site.repository }}/issues/7015)) + + +## 3.8.1 / 2018-05-01 +{: #v3-8-1} + +### Bug Fixes +{: #bug-fixes-v3-8-1} + +- Fix rendering Liquid constructs in excerpts ([#6945]({{ site.repository }}/issues/6945)) +- Liquify documents unless published == false ([#6959]({{ site.repository }}/issues/6959)) + + +## 3.8.0 / 2018-04-19 +{: #v3-8-0} + +### Development Fixes +{: #development-fixes-v3-8-0} + +- move duplicate code to a single private method ([#6593]({{ site.repository }}/issues/6593)) +- Test against Ruby 2.5 on AppVeyor ([#6668]({{ site.repository }}/issues/6668)) +- Replace simple regex with a native Ruby method ([#6732]({{ site.repository }}/issues/6732)) +- Codeclimate: exclude livereload.js ([#6776]({{ site.repository }}/issues/6776)) +- Add a cucumber feature to test link tag ([#6777]({{ site.repository }}/issues/6777)) +- Fix theme gem feature ([#6784]({{ site.repository }}/issues/6784)) +- Replace simple regex with equivalent Ruby methods ([#6736]({{ site.repository }}/issues/6736)) +- Rewrite `script/rubyprof` as a Ruby script ([#6813]({{ site.repository }}/issues/6813)) +- Add debug output to theme rendering ([#5195]({{ site.repository }}/issues/5195)) +- fix minitest deprecation warning in test ([#6839]({{ site.repository }}/issues/6839)) +- Memoize `Site#site_data` ([#6809]({{ site.repository }}/issues/6809)) +- Memoize document output extension ([#6814]({{ site.repository }}/issues/6814)) +- Access document permalink attribute efficiently ([#6740]({{ site.repository }}/issues/6740)) +- Minimize array allocations in the `where` filter ([#6860]({{ site.repository }}/issues/6860)) +- Bump JRuby ([#6878]({{ site.repository }}/issues/6878)) +- Assert existence of <collection>.files ([#6907]({{ site.repository }}/issues/6907)) +- Bump RuboCop to 0.54.x ([#6915]({{ site.repository }}/issues/6915)) +- Regenerate unconditionally unless its an incremental build ([#6917]({{ site.repository }}/issues/6917)) +- Centralize require statements ([#6910]({{ site.repository }}/issues/6910)) +- Bump to RuboCop 0.55 ([#6929]({{ site.repository }}/issues/6929)) +- Refactor private method `HighlightBlock#parse_options` ([#6822]({{ site.repository }}/issues/6822)) + +### Minor Enhancements +{: #minor-enhancements-v3-8-0} + +- Two massive performance improvements for large sites ([#6730]({{ site.repository }}/issues/6730)) +- Cache the list of documents to be written ([#6741]({{ site.repository }}/issues/6741)) +- Allow Jekyll Doctor to detect stray posts dir ([#6681]({{ site.repository }}/issues/6681)) +- Excerpt relative-path should match its path ([#6597]({{ site.repository }}/issues/6597)) +- Remind user to resolve conflict in `jekyll new` with `--force` ([#6801]({{ site.repository }}/issues/6801)) +- Memoize helper methods in site-cleaner ([#6808]({{ site.repository }}/issues/6808)) +- Compute document's relative_path faster ([#6767]({{ site.repository }}/issues/6767)) +- Create a single instance of PostReader per site ([#6759]({{ site.repository }}/issues/6759)) +- Allow date filters to output ordinal days ([#6773]({{ site.repository }}/issues/6773)) +- Change regex to sanitize and normalize filenames passed to LiquidRenderer ([#6610]({{ site.repository }}/issues/6610)) +- Allow passing :strict_variables and :strict_filters options to Liquid's renderer ([#6726]({{ site.repository }}/issues/6726)) +- Debug writing files during the build process ([#6696]({{ site.repository }}/issues/6696)) +- Improve regex usage in `Tags::IncludeTag` ([#6848]({{ site.repository }}/issues/6848)) +- Improve comment included in the starter index.md ([#6916]({{ site.repository }}/issues/6916)) +- Store and retrieve converter instances for Jekyll::Filters via a hash ([#6856]({{ site.repository }}/issues/6856)) +- Implement a cache within the `where` filter ([#6868]({{ site.repository }}/issues/6868)) +- Store regexp in a constant ([#6887]({{ site.repository }}/issues/6887)) +- Optimize computing filename in LiquidRenderer ([#6841]({{ site.repository }}/issues/6841)) + +### Documentation + +- Adding the jekyll-algolia plugin to the list of plugins ([#6737]({{ site.repository }}/issues/6737)) +- Added Premonition plugin to list of plugins ([#6750]({{ site.repository }}/issues/6750)) +- Add document on releasing a new version ([#6745]({{ site.repository }}/issues/6745)) +- Mention Talkyard, a new commenting system for Jekyll and others. ([#6752]({{ site.repository }}/issues/6752)) +- Add 'jekyll-fontello' to plugins ([#6757]({{ site.repository }}/issues/6757)) +- Install dh-autoreconf on Windows ([#6765]({{ site.repository }}/issues/6765)) +- Fix common typos ([#6764]({{ site.repository }}/issues/6764)) +- Fix documentation for {% raw %}`{{ page.excerpt }}`{% endraw %} ([#6779]({{ site.repository }}/issues/6779)) +- Update docs on permalink configuration ([#6775]({{ site.repository }}/issues/6775)) +- Propose fix some typos ([#6785]({{ site.repository }}/issues/6785)) +- Say hello to Jekyll's New Lead Developer ([#6790]({{ site.repository }}/issues/6790)) +- Add reference to Liquid to plugin docs ([#6794]({{ site.repository }}/issues/6794)) +- Draft a release post for v3.7.3 ([#6803]({{ site.repository }}/issues/6803)) +- add missing step for gem-based theme conversion ([#6802]({{ site.repository }}/issues/6802)) +- Update windows.md to explain an issue with jekyll new. ([#6838]({{ site.repository }}/issues/6838)) +- Add Bundler Installation Instructions ([#6828]({{ site.repository }}/issues/6828)) +- Docs: describe difference between tags and categories ([#6882]({{ site.repository }}/issues/6882)) +- Add `jekyll-random` plugin to docs ([#6833]({{ site.repository }}/issues/6833)) +- Fixed typo in description of categories and tags ([#6896]({{ site.repository }}/issues/6896)) +- Add missing ul-tag ([#6897]({{ site.repository }}/issues/6897)) +- doc: add liquid tag plugin jekyll-onebox for html previews ([#6898]({{ site.repository }}/issues/6898)) +- Add `jekyll-w2m` to plugins ([#6855]({{ site.repository }}/issues/6855)) +- Fix tutorials navigation HTML ([#6919]({{ site.repository }}/issues/6919)) +- add Arch Linux installation troubleshoot ([#6782]({{ site.repository }}/issues/6782)) +- Docs: Install Jekyll on macOS ([#6881]({{ site.repository }}/issues/6881)) +- Fix CodeClimate badges [ci skip] ([#6930]({{ site.repository }}/issues/6930)) +- Update index.md ([#6933]({{ site.repository }}/issues/6933)) + +### Site Enhancements +{: #site-enhancements-v3-8-0} + +- Remove links to Gists ([#6751]({{ site.repository }}/issues/6751)) +- Always load Google Fonts over HTTPS ([#6792]({{ site.repository }}/issues/6792)) +- always load analytics.js over HTTPS ([#6807]({{ site.repository }}/issues/6807)) + +### Bug Fixes +{: #bug-fixes-v3-8-0} + +- Append appropriate closing tag to Liquid block in an excerpt ### -minor ([#6724]({{ site.repository }}/issues/6724)) +- Bypass rendering via Liquid unless required ([#6735]({{ site.repository }}/issues/6735)) +- Delegated methods after `private` keyword are meant to be private ([#6819]({{ site.repository }}/issues/6819)) +- Improve handling non-default collection documents rendering and writing ([#6795]({{ site.repository }}/issues/6795)) +- Fix passing multiline params to include tag when using the variable syntax ([#6858]({{ site.repository }}/issues/6858)) +- `include_relative` tag should find related documents in collections gathered within custom `collections_dir` ([#6818]({{ site.repository }}/issues/6818)) +- Handle liquid tags in excerpts robustly ([#6891]({{ site.repository }}/issues/6891)) +- Allow front matter defaults to be applied properly to documents gathered under custom `collections_dir` ([#6885]({{ site.repository }}/issues/6885)) + + +## 3.7.4 / 2018-09-07 +{: #v3-7-4} + +### Bug Fixes +{: #bug-fixes-v3-7-4} + +- Security: fix `include` bypass of EntryFilter#filter symlink check ([#7224]({{ site.repository }}/issues/7224)) + + +## 3.7.3 / 2018-02-25 +{: #v3-7-3} + +### Bug Fixes +{: #bug-fixes-v3-7-3} + +- Do not hardcode locale unless certainly necessary ([#6791]({{ site.repository }}/issues/6791)) + + +## 3.7.2 / 2018-01-25 +{: #v3-7-2} + +### Development Fixes +{: #development-fixes-v3-7-2} + +- CI: Test against Ruby 2.5.0 ([#6664]({{ site.repository }}/issues/6664)) +- Bump rdoc to 6.0 ([#6600]({{ site.repository }}/issues/6600)) +- Lint file and bump theme dependencies ([#6698]({{ site.repository }}/issues/6698)) +- Write a Rubocop Cop to ensure no `#p` or `#puts` calls get committed to master. ([#6615]({{ site.repository }}/issues/6615)) +- Remove redgreen gem ([#6720]({{ site.repository }}/issues/6720)) + +### Site Enhancements +{: #site-enhancements-v3-7-2} + +- Display latest version in header ([#6676]({{ site.repository }}/issues/6676)) +- Update version in `config.yml` via YAML load / dump ([#6677]({{ site.repository }}/issues/6677)) + +### Documentation + +- Fix: Add note about posts in context of collections_dir ([#6680]({{ site.repository }}/issues/6680)) +- Update deploy-script in documentation ([#6666]({{ site.repository }}/issues/6666)) +- Add note about naming of collections_dir ([#6703]({{ site.repository }}/issues/6703)) +- Update installation.md ([#6694]({{ site.repository }}/issues/6694)) +- Add `jekyll-html` to plugins. ([#6654]({{ site.repository }}/issues/6654)) +- Update plugins.md ([#6716]({{ site.repository }}/issues/6716)) +- Release v3.7.1 ([#6695]({{ site.repository }}/issues/6695)) + +### Bug Fixes +{: #bug-fixes-v3-7-2} + +- inform that symlinks are not allowed in safe mode ([#6670]({{ site.repository }}/issues/6670)) +- Glob scope path only if configured with a pattern ([#6692]({{ site.repository }}/issues/6692)) +- Add gem "wdm" to all newly generated Gemfiles ([#6711]({{ site.repository }}/issues/6711)) +- Fix timezone inconsistencies between different ruby version ([#6697]({{ site.repository }}/issues/6697)) +- Refactor collections_dir feature for consistency ([#6685]({{ site.repository }}/issues/6685)) + +### Minor Enhancements +{: #minor-enhancements-v3-7-2} + +- Require external library only if necessary ([#6596]({{ site.repository }}/issues/6596)) + + +## 3.7.0 / 2018-01-02 +{: #v3-7-0} + +### Minor Enhancements +{: #minor-enhancements-v3-7-0} + +- Add LiveReload functionality to Jekyll. ([#5142]({{ site.repository }}/issues/5142)) +- Add Utils::Internet.connected? to determine whether host machine has internet connection. ([#5870]({{ site.repository }}/issues/5870)) +- Disable default layouts for Pages with a `layout: none` declaration ([#6182]({{ site.repository }}/issues/6182)) +- Scope path glob ([#6268]({{ site.repository }}/issues/6268)) +- Allow the user to set collections_dir to put all collections under one subdirectory ([#6331]({{ site.repository }}/issues/6331)) +- Upgrade to Rouge 3 ([#6381]({{ site.repository }}/issues/6381)) +- Allow URL filters to work directly with documents ([#6478]({{ site.repository }}/issues/6478)) +- filter relative_url should keep absolute urls with scheme/authority ([#6490]({{ site.repository }}/issues/6490)) +- `.sass-cache` doesn't *always* land in `options['source']` ([#6500]({{ site.repository }}/issues/6500)) +- Allow plugins to modify the obsolete files. ([#6502]({{ site.repository }}/issues/6502)) +- Add latin mode to `slugify` ([#6509]({{ site.repository }}/issues/6509)) +- Log Kramdown warnings if log level is WARN ([#6522]({{ site.repository }}/issues/6522)) +- Add an option to configure kramdown warning output ([#6554]({{ site.repository }}/issues/6554)) +- Add `json` extension to list of directory indices ([#6550]({{ site.repository }}/issues/6550)) +- Dependency: Bump jekyll-watch to 2.0 ([#6589]({{ site.repository }}/issues/6589)) +- Remove paginate check ([#6606]({{ site.repository }}/issues/6606)) +- update classifier-reborn to 2.2.0 ([#6631]({{ site.repository }}/issues/6631)) +- Switch to an actively-maintained TOML parser. ([#6652]({{ site.repository }}/issues/6652)) +- Do not coerce layout paths in theme-gem to the source directory ([#6603]({{ site.repository }}/issues/6603)) + +### Bug Fixes +{: #bug-fixes-v3-7-0} + +- Raise when theme root directory is not available ([#6455]({{ site.repository }}/issues/6455)) +- Avoid block parser warning in SmartyPants ([#6565]({{ site.repository }}/issues/6565)) +- Fail gracefully if "sass" gem cannot be loaded ([#6573]({{ site.repository }}/issues/6573)) +- return correct file in dir if dir has same name as file ([#6569]({{ site.repository }}/issues/6569)) +- Register reload hooks in Server#process ([#6605]({{ site.repository }}/issues/6605)) +- Memoize path to metadata file ([#6602]({{ site.repository }}/issues/6602)) +- Use `require_relative` to load Jekyll classes ([#6609]({{ site.repository }}/issues/6609)) + +### Development Fixes +{: #development-fixes-v3-7-0} + +- Added direct collection access to future collection item feature test([#6151]({{ site.repository }}/issues/6151)) +- add failing test for non-utf8 encoding ([#6339]({{ site.repository }}/issues/6339)) +- Upgrade to Cucumber 3.0 ([#6395]({{ site.repository }}/issues/6395)) +- Provide a better default hash for tracking liquid stats ([#6417]({{ site.repository }}/issues/6417)) +- Add configuration for first-timers bot ([#6431]({{ site.repository }}/issues/6431)) +- Do not linkify escaped characters as PRs in History ([#6468]({{ site.repository }}/issues/6468)) +- Rely on jekyll-mentions for linking usernames ([#6469]({{ site.repository }}/issues/6469)) +- Update first-timers-issue-template.md ([#6472]({{ site.repository }}/issues/6472)) +- Enable `Lint/RescueWithoutErrorClass` Cop ([#6482]({{ site.repository }}/issues/6482)) +- Clean up Rubocop config ([#6495]({{ site.repository }}/issues/6495)) +- Use Gem to discover the location of bundler ([#6499]({{ site.repository }}/issues/6499)) +- Remove unnecessary encoding comment ([#6513]({{ site.repository }}/issues/6513)) +- Suggest using Rubocop to automatically fix errors ([#6514]({{ site.repository }}/issues/6514)) +- Assert raising Psych::SyntaxError when`"strict_front_matter"=>true` ([#6520]({{ site.repository }}/issues/6520)) +- Use Kernel#Array instead of explicit Array check ([#6525]({{ site.repository }}/issues/6525)) +- RuboCop: Enable `Style/UnneededCapitalW` cop ([#6526]({{ site.repository }}/issues/6526)) +- Refactor method to reduce ABC Metric size ([#6529]({{ site.repository }}/issues/6529)) +- Remove parentheses around arguments to raise ([#6532]({{ site.repository }}/issues/6532)) +- Use double-quotes around gem name ([#6535]({{ site.repository }}/issues/6535)) +- Dependencies: upgrade to toml 0.2.0 ([#6541]({{ site.repository }}/issues/6541)) +- Lock to cucumber 3.0.1 on Ruby 2.1 ([#6546]({{ site.repository }}/issues/6546)) +- Bump JRuby version in Travis config ([#6561]({{ site.repository }}/issues/6561)) +- Rescue from Psych::SyntaxError instead of SyntaxError after parsing YAML([#5828]({{ site.repository }}/issues/5828)) +- Drop forwarding to private methods by exposing those methods as public([#6577]({{ site.repository }}/issues/6577)) +- Upgrade pygments to v1.x ([#5937]({{ site.repository }}/issues/5937)) +- Bump yajl-ruby ([#6582]({{ site.repository }}/issues/6582)) +- Cleanup test_redcarpet.rb ([#6584]({{ site.repository }}/issues/6584)) +- Add PageWithoutAFile class from jekyll plugins ([#6556]({{ site.repository }}/issues/6556)) +- Cleanup LiveReloadReactor ([#6607]({{ site.repository }}/issues/6607)) + +### Documentation + +- Add formester to the list of saas form backend ([#6059]({{ site.repository }}/issues/6059)) +- GitHub Pages instructions ([#6384]({{ site.repository }}/issues/6384)) +- Improve documentation for theme-gem installation ([#6387]({{ site.repository }}/issues/6387)) +- Fix diff syntax-highlighting ([#6388]({{ site.repository }}/issues/6388)) +- Update instructions ([#6396]({{ site.repository }}/issues/6396)) +- Fix code-block highlighting in docs ([#6398]({{ site.repository }}/issues/6398)) +- Filtering Posts with categories, tags, or other variables ([#6399]({{ site.repository }}/issues/6399)) +- Fixes formatting on pre-formatted text. ([#6405]({{ site.repository }}/issues/6405)) +- Added new tutorial to tutorials section on docs ([#6406]({{ site.repository }}/issues/6406)) +- Updates ([#6407]({{ site.repository }}/issues/6407)) +- Fix `collections_dir` example ([#6408]({{ site.repository }}/issues/6408)) +- Renaming duplicate of "Scenario 6" to "Scenario 7" ([#6411]({{ site.repository }}/issues/6411)) +- Mark `collection_dir` as unreleased ([#6412]({{ site.repository }}/issues/6412)) +- Fix link to SUPPORT ([#6415]({{ site.repository }}/issues/6415)) +- Fix list appearance by adding missing `ol` tag ([#6421]({{ site.repository }}/issues/6421)) +- Explain how to override output collection index page ([#6424]({{ site.repository }}/issues/6424)) +- Added github-cards to the list of plugins ([#6425]({{ site.repository }}/issues/6425)) +- CoC violation correspondents ([#6429]({{ site.repository }}/issues/6429)) +- Add a note about Liquid and syntax highlighting ([#6466]({{ site.repository }}/issues/6466)) +- Remove `sudo` from macOS troubleshooting instructions ([#6486]({{ site.repository }}/issues/6486)) +- Add a note on `:jekyll_plugins` group in the docs ([#6488]({{ site.repository }}/issues/6488)) +- Updated custom-404-page.md ([#6489]({{ site.repository }}/issues/6489)) +- Fix a few minor issues in the docs ([#6494]({{ site.repository }}/issues/6494)) +- Add jekyll-pwa-plugin ([#6533]({{ site.repository }}/issues/6533)) +- Remove Jekyll-Smartify from plugins directory ([#6548]({{ site.repository }}/issues/6548)) +- Updated Jekyll-Pug listing to include official website ([#6555]({{ site.repository }}/issues/6555)) +- Remove link to severely outdated asset plugin ([#6613]({{ site.repository }}/issues/6613)) +- Default time zone depends upon server ([#6617]({{ site.repository }}/issues/6617)) +- Add `disqus-for-jekyll` to plugins. ([#6618]({{ site.repository }}/issues/6618)) +- Update "Requirements" for Ruby version ([#6623]({{ site.repository }}/issues/6623)) +- Fix: Update link to i18n_filter plugin ([#6638]({{ site.repository }}/issues/6638)) +- Correct WordPress capitalization ([#6645]({{ site.repository }}/issues/6645)) +- Add Tweetsert, Stickyposts, Paginate::Content ([#6651]({{ site.repository }}/issues/6651)) +- Post: Jekyll 3.7.0 released ([#6634]({{ site.repository }}/issues/6634)) + +### Site Enhancements +{: #site-enhancements-v3-7-0} + +- Add special styling for code-blocks run in shell ([#6389]({{ site.repository }}/issues/6389)) +- Add post about diversity ([#6447]({{ site.repository }}/issues/6447)) +- Update list of files excluded from Docs site ([#6457]({{ site.repository }}/issues/6457)) +- Update site History ([#6460]({{ site.repository }}/issues/6460)) +- Add default twitter card image ([#6476]({{ site.repository }}/issues/6476)) +- Update normalize.css to v7.0.0 ([#6491]({{ site.repository }}/issues/6491)) +- Optimize images ([#6519]({{ site.repository }}/issues/6519)) +- Back to original main navigation ([#6544]({{ site.repository }}/issues/6544)) +- Styles: mobile-docs select element ([#6545]({{ site.repository }}/issues/6545)) +- Search with DocSearch by @Algolia ([#6557]({{ site.repository }}/issues/6557)) +- Site header redesign ([#6567]({{ site.repository }}/issues/6567)) +- Move logo above site navigation on small screens ([#6570]({{ site.repository }}/issues/6570)) +- Docs: Include version badge for latest features ([#6574]({{ site.repository }}/issues/6574)) +- Use version-badge on an existing feature intro ([#6575]({{ site.repository }}/issues/6575)) +- Add jekyll-category-pages plugin ([#6632]({{ site.repository }}/issues/6632)) +- Improve docs styling for code to be run in shell ([#6641]({{ site.repository }}/issues/6641)) +- Fix permalink icon markup in news-item layout ([#6639]({{ site.repository }}/issues/6639)) + + +## 3.6.3 / 2018-09-18 +{: #v3-6-3} + +### Bug Fixes +{: #bug-fixes-v3-6-3} + +- 3.6.x: security: fix `include` bypass of `EntryFilter#filter` symlink check ([#7229]({{ site.repository }}/issues/7229)) + + +## 3.6.2 / 2017-10-21 +{: #v3-6-2} + +### Development Fixes +{: #development-fixes-v3-6-2} + +- Update Rubocop to 0.51.0 ([#6444]({{ site.repository }}/issues/6444)) +- Add test for layout as string ([#6445]({{ site.repository }}/issues/6445)) + +### Bug Fixes +{: #bug-fixes-v3-6-2} + +- Problematic UTF+bom files ([#6322]({{ site.repository }}/issues/6322)) +- Always treat `data.layout` as a string ([#6442]({{ site.repository }}/issues/6442)) + + +## 3.6.1 / 2017-10-20 +{: #v3-6-1} + +### Documentation + +- Doc y_day in docs/permalinks ([#6244]({{ site.repository }}/issues/6244)) +- Update frontmatter.md ([#6371]({{ site.repository }}/issues/6371)) +- Elaborate on excluding items from processing ([#6136]({{ site.repository }}/issues/6136)) +- Style lists in tables ([#6379]({{ site.repository }}/issues/6379)) +- Remove duplicate "available" ([#6380]({{ site.repository }}/issues/6380)) + +### Development Fixes +{: #development-fixes-v3-6-1} + +- Bump rubocop to use `v0.50.x` ([#6368]({{ site.repository }}/issues/6368)) + + +## 3.6.0 / 2017-09-21 +{: #v3-6-0} + +### Minor Enhancements +{: #minor-enhancements-v3-6-0} + +- Ignore final newline in folded YAML string ([#6054]({{ site.repository }}/issues/6054)) +- Add URL checks to Doctor ([#5760]({{ site.repository }}/issues/5760)) +- Fix serving files that clash with directories ([#6222]({{ site.repository }}/issues/6222)) ([#6231]({{ site.repository }}/issues/6231)) +- Bump supported Ruby version to `>= 2.1.0` ([#6220]({{ site.repository }}/issues/6220)) +- set `LiquidError#template_name` for errors in included file ([#6206]({{ site.repository }}/issues/6206)) +- Access custom config array throughout session ([#6200]({{ site.repository }}/issues/6200)) +- Add support for Rouge 2, in addition to Rouge 1 ([#5919]({{ site.repository }}/issues/5919)) +- Allow `yield` to logger methods & bail early on no-op messages ([#6315]({{ site.repository }}/issues/6315)) +- Update mime-types. ([#6336]({{ site.repository }}/issues/6336)) +- Use a Schwartzian transform with custom sorting ([#6342]({{ site.repository }}/issues/6342)) +- Alias `Drop#invoke_drop` to `Drop#[]` ([#6338]({{ site.repository }}/issues/6338)) + +### Bug Fixes +{: #bug-fixes-v3-6-0} + +- `Deprecator`: fix typo for `--serve` command ([#6229]({{ site.repository }}/issues/6229)) +- `Reader#read_directories`: guard against an entry not being a directory ([#6226]({{ site.repository }}/issues/6226)) +- kramdown: symbolize keys in-place ([#6247]({{ site.repository }}/issues/6247)) +- Call `to_s` on site.url before attempting to concatenate strings ([#6253]({{ site.repository }}/issues/6253)) +- Enforce Style/FrozenStringLiteralComment ([#6265]({{ site.repository }}/issues/6265)) +- Update theme-template README to note 'assets' directory ([#6257]({{ site.repository }}/issues/6257)) +- Memoize the return value of `Document#url` ([#6266]({{ site.repository }}/issues/6266)) +- delegate `StaticFile#to_json` to `StaticFile#to_liquid` ([#6273]({{ site.repository }}/issues/6273)) +- Fix `Drop#key?` so it can handle a nil argument ([#6281]({{ site.repository }}/issues/6281)) +- Guard against type error in absolute url ([#6280]({{ site.repository }}/issues/6280)) +- Mutable drops should fallback to their own methods when a mutation isn't present ([#6350]({{ site.repository }}/issues/6350)) +- Skip adding binary files as posts ([#6344]({{ site.repository }}/issues/6344)) +- Don't break if bundler is not installed ([#6377]({{ site.repository }}/issues/6377)) + +### Documentation + +- Fix a typo in `custom-404-page.md` ([#6218]({{ site.repository }}/issues/6218)) +- Docs: fix links to issues in History.markdown ([#6255]({{ site.repository }}/issues/6255)) +- Update deprecated gems key to plugins. ([#6262]({{ site.repository }}/issues/6262)) +- Fixes minor typo in post text ([#6283]({{ site.repository }}/issues/6283)) +- Execute build command using bundle. ([#6274]({{ site.repository }}/issues/6274)) +- name unification - buddy details ([#6317]({{ site.repository }}/issues/6317)) +- name unification - application index ([#6318]({{ site.repository }}/issues/6318)) +- trim and relocate plugin info across docs ([#6311]({{ site.repository }}/issues/6311)) +- update Jekyll's README ([#6321]({{ site.repository }}/issues/6321)) +- add SUPPORT file for GitHub ([#6324]({{ site.repository }}/issues/6324)) +- Rename CODE_OF_CONDUCT to show in banner ([#6325]({{ site.repository }}/issues/6325)) +- Docs : illustrate page.id for a collection's document ([#6329]({{ site.repository }}/issues/6329)) +- Docs: post's date can be overridden in front matter ([#6334]({{ site.repository }}/issues/6334)) +- Docs: `site.url` behavior on development and production environments ([#6270]({{ site.repository }}/issues/6270)) +- Fix typo in site.url section of variables.md :-[ ([#6337]({{ site.repository }}/issues/6337)) +- Docs: updates ([#6343]({{ site.repository }}/issues/6343)) +- Fix precedence docs ([#6346]({{ site.repository }}/issues/6346)) +- add note to contributing docs about `script/console` ([#6349]({{ site.repository }}/issues/6349)) +- Docs: Fix permalink example ([#6375]({{ site.repository }}/issues/6375)) + +### Site Enhancements +{: #site-enhancements-v3-6-0} + +- Adding DevKit helpers ([#6225]({{ site.repository }}/issues/6225)) +- Customizing url in collection elements clarified ([#6264]({{ site.repository }}/issues/6264)) +- Plugins is the new gems ([#6326]({{ site.repository }}/issues/6326)) + +### Development Fixes +{: #development-fixes-v3-6-0} + +- Strip unnecessary leading whitespace in template ([#6228]({{ site.repository }}/issues/6228)) +- Users should be installing patch versions. ([#6198]({{ site.repository }}/issues/6198)) +- Fix tests ([#6240]({{ site.repository }}/issues/6240)) +- Define path with `__dir__` ([#6087]({{ site.repository }}/issues/6087)) +- exit site.process sooner ([#6239]({{ site.repository }}/issues/6239)) +- make flakey test more robust ([#6277]({{ site.repository }}/issues/6277)) +- Add a quick test for DataReader ([#6284]({{ site.repository }}/issues/6284)) +- script/backport-pr: commit message no longer includes the `#` ([#6289]({{ site.repository }}/issues/6289)) +- Add Add CODEOWNERS file to help automate reviews. ([#6320]({{ site.repository }}/issues/6320)) +- Fix builds on codeclimate ([#6333]({{ site.repository }}/issues/6333)) +- Bump rubies on Travis ([#6366]({{ site.repository }}/issues/6366)) + + +## 3.5.2 / 2017-08-12 +{: #v3-5-2} + +### Bug Fixes +{: #bug-fixes-v3-5-2} + +- Backport [#6281]({{ site.repository }}/issues/6281) for v3.5.x: Fix `Drop#key?` so it can handle a nil argument ([#6288]({{ site.repository }}/issues/6288)) +- Backport [#6280]({{ site.repository }}/issues/6280) for v3.5.x: Guard against type error in `absolute_url` ([#6287]({{ site.repository }}/issues/6287)) +- Backport [#6266]({{ site.repository }}/issues/6266) for v3.5.x: Memoize the return value of `Document#url` ([#6301]({{ site.repository }}/issues/6301)) +- Backport [#6273]({{ site.repository }}/issues/6273) for v3.5.x: delegate `StaticFile#to_json` to `StaticFile#to_liquid` ([#6302]({{ site.repository }}/issues/6302)) +- Backport [#6226]({{ site.repository }}/issues/6226) for v3.5.x: `Reader#read_directories`: guard against an entry not being a directory ([#6304]({{ site.repository }}/issues/6304)) +- Backport [#6247]({{ site.repository }}/issues/6247) for v3.5.x: kramdown: symbolize keys in-place ([#6303]({{ site.repository }}/issues/6303)) + + +## 3.5.1 / 2017-07-17 +{: #v3-5-1} + +### Minor Enhancements +{: #minor-enhancements-v3-5-1} + +- Use Warn for deprecation messages ([#6192]({{ site.repository }}/issues/6192)) +- site template: Use plugins key instead of gems ([#6045]({{ site.repository }}/issues/6045)) + +### Bug Fixes +{: #bug-fixes-v3-5-1} + +- Backward compatibilize URLFilters module ([#6163]({{ site.repository }}/issues/6163)) +- Static files contain front matter default keys when `to_liquid`'d ([#6162]({{ site.repository }}/issues/6162)) +- Always normalize the result of the `relative_url` filter ([#6185]({{ site.repository }}/issues/6185)) + +### Documentation + +- Update reference to trouble with OS X/macOS ([#6139]({{ site.repository }}/issues/6139)) +- 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)) +- 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)) +- Small correction in documentation for includes ([#6193]({{ site.repository }}/issues/6193)) +- Fix docs site page margin ([#6214]({{ site.repository }}/issues/6214)) + +### Development Fixes +{: #development-fixes-v3-5-1} + +- Add jekyll doctor to GitHub Issue Template ([#6169]({{ site.repository }}/issues/6169)) +- Test with Ruby 2.4.1-1 on AppVeyor ([#6176]({{ site.repository }}/issues/6176)) +- set minimum requirement for jekyll-feed ([#6184]({{ site.repository }}/issues/6184)) + + +## 3.5.0 / 2017-06-18 +{: #v3-5-0} + +### Minor Enhancements +{: #minor-enhancements-v3-5-0} + +- Upgrade to Liquid v4 ([#4362]({{ site.repository }}/issues/4362)) +- Convert StaticFile liquid representation to a Drop & add front matter defaults support to StaticFiles ([#5871]({{ site.repository }}/issues/5871)) +- Add support for Tab-Separated Values data files (`*.tsv`) ([#5985]({{ site.repository }}/issues/5985)) +- Specify version constraint in subcommand error message. ([#5974]({{ site.repository }}/issues/5974)) +- Add a template for custom 404 page ([#5945]({{ site.repository }}/issues/5945)) +- Require `runtime_dependencies` of a Gem-based theme from its `.gemspec` file ([#5914]({{ site.repository }}/issues/5914)) +- Don't raise an error if URL contains a colon ([#5889]({{ site.repository }}/issues/5889)) +- Date filters should never raise an exception ([#5722]({{ site.repository }}/issues/5722)) +- add `plugins` config key as replacement for `gems` ([#5130]({{ site.repository }}/issues/5130)) +- create configuration from options only once in the boot process ([#5487]({{ site.repository }}/issues/5487)) +- Add option to fail a build with front matter syntax errors ([#5832]({{ site.repository }}/issues/5832)) +- Disable default layouts for documents with a `layout: none` declaration ([#5933]({{ site.repository }}/issues/5933)) +- In `jekyll new`, make copied site template user-writable ([#6072]({{ site.repository }}/issues/6072)) +- Add top-level `layout` liquid variable to Documents ([#6073]({{ site.repository }}/issues/6073)) +- Address reading non-binary static files in themes ([#5918]({{ site.repository }}/issues/5918)) +- Allow filters to sort & select based on subvalues ([#5622]({{ site.repository }}/issues/5622)) +- Add strip_index filter ([#6075]({{ site.repository }}/issues/6075)) + +### Documentation + +- Install troubleshooting on Ubuntu ([#5817]({{ site.repository }}/issues/5817)) +- Add Termux section on troubleshooting ([#5837]({{ site.repository }}/issues/5837)) +- fix ial css classes in theme doc ([#5876]({{ site.repository }}/issues/5876)) +- Update installation.md ([#5880]({{ site.repository }}/issues/5880)) +- Update Aerobatic docs ([#5883]({{ site.repository }}/issues/5883)) +- Add note to collections doc on hard-coded collections. ([#5882]({{ site.repository }}/issues/5882)) +- Makes uri_escape template docs more specific. ([#5887]({{ site.repository }}/issues/5887)) +- Remove duplicate footnote_nr from default config ([#5891]({{ site.repository }}/issues/5891)) +- Fixed tutorial for publishing gem to include repo. ([#5900]({{ site.repository }}/issues/5900)) +- update broken links ([#5905]({{ site.repository }}/issues/5905)) +- Fix typo in contribution information ([#5910]({{ site.repository }}/issues/5910)) +- update plugin repo URL to reflect repo move ([#5916]({{ site.repository }}/issues/5916)) +- Update exclude array in configuration.md ([#5947]({{ site.repository }}/issues/5947)) +- Fixed path in "Improve this page" link in Tutorials section ([#5951]({{ site.repository }}/issues/5951)) +- Corrected permalink ([#5949]({{ site.repository }}/issues/5949)) +- Included more details about adding defaults to static files ([#5971]({{ site.repository }}/issues/5971)) +- Create buddyworks ([#5962]({{ site.repository }}/issues/5962)) +- added (buddyworks) to ci list ([#5965]({{ site.repository }}/issues/5965)) +- Add a tutorial on serving custom Error 404 page ([#5946]({{ site.repository }}/issues/5946)) +- add custom 404 to tutorial navigation ([#5978]({{ site.repository }}/issues/5978)) +- Add link to order of interpretation tutorial in Tutorials nav ([#5952]({{ site.repository }}/issues/5952)) +- Document Jekyll's Philosophy ([#5792]({{ site.repository }}/issues/5792)) +- Require Ruby > 2.1.0 ([#5983]({{ site.repository }}/issues/5983)) +- Fix broken link ([#5994]({{ site.repository }}/issues/5994)) +- Default options for script/proof ([#5995]({{ site.repository }}/issues/5995)) +- Mention Bash on Ubuntu on Windows ([#5960]({{ site.repository }}/issues/5960)) +- Document `--unpublished` flag introduced in 91e9ecf ([#5959]({{ site.repository }}/issues/5959)) +- Update upgrading.md to mention usage of `bundle update` ([#5604]({{ site.repository }}/issues/5604)) +- Fix missing quotation mark ([#6002]({{ site.repository }}/issues/6002)) +- New tutorial: Convert an HTML site to Jekyll ([#5881]({{ site.repository }}/issues/5881)) +- Revamp Permalink section ([#5912]({{ site.repository }}/issues/5912)) +- Fixup tutorial on creating theme from existing HTML templates ([#6006]({{ site.repository }}/issues/6006)) +- Standardise on "URLs" without apostrophe in docs ([#6018]({{ site.repository }}/issues/6018)) +- Added txtpen in tutorial ([#6021]({{ site.repository }}/issues/6021)) +- fix typo using past participle ([#6026]({{ site.repository }}/issues/6026)) +- changed formatting to fit the style of the documentation ([#6027]({{ site.repository }}/issues/6027)) +- doc fix typo word usage ([#6028]({{ site.repository }}/issues/6028)) +- corrected reference to layout in index.md ([#6032]({{ site.repository }}/issues/6032)) +- (Minor) Update MathJax CDN ([#6013]({{ site.repository }}/issues/6013)) +- Add MvvmCross to samples ([#6035]({{ site.repository }}/issues/6035)) +- Update travis-ci.md to correct procedure ([#6043]({{ site.repository }}/issues/6043)) +- fix sentence in documentation ([#6048]({{ site.repository }}/issues/6048)) +- rephrase a sentence in posts.md to be more direct ([#6049]({{ site.repository }}/issues/6049)) +- Compress Website Sass output ([#6009]({{ site.repository }}/issues/6009)) +- doc correct spelling error ([#6050]({{ site.repository }}/issues/6050)) +- adjusted date-format in sitemap ([#6053]({{ site.repository }}/issues/6053)) +- Typo fix (welcomed change -> welcome change). ([#6070]({{ site.repository }}/issues/6070)) +- Fixed documentation inconsistency ([#6068]({{ site.repository }}/issues/6068)) +- Add own plugin -> Jekyll Brand Social Wall ([#6064]({{ site.repository }}/issues/6064)) +- Added plugin jekyll-analytics ([#6042]({{ site.repository }}/issues/6042)) +- Use more precise language when explaining links ([#6078]({{ site.repository }}/issues/6078)) +- Update plugins.md ([#6088]({{ site.repository }}/issues/6088)) +- windows 10 tutorial ([#6100]({{ site.repository }}/issues/6100)) +- Explain how to override theme styles ([#6107]({{ site.repository }}/issues/6107)) +- updated Bash on Ubuntu on Windows link in tutorial ([#6111]({{ site.repository }}/issues/6111)) +- Fix wording in `_docs/templates.md` links section ([#6114]({{ site.repository }}/issues/6114)) +- Update windows.md ([#6115]({{ site.repository }}/issues/6115)) +- Added windows to docs.yml ([#6109]({{ site.repository }}/issues/6109)) +- Be more specific on what to upload ([#6119]({{ site.repository }}/issues/6119)) +- Remove Blank Newlines from "Jekyll on Windows" Page ([#6126]({{ site.repository }}/issues/6126)) +- Link the troubleshooting page in the quickstart page ([#6134]({{ site.repository }}/issues/6134)) +- add documentation about the "pinned" label ([#6147]({{ site.repository }}/issues/6147)) +- docs(JekyllOnWindows): Add a new Installation way ([#6141]({{ site.repository }}/issues/6141)) +- corrected windows.md ([#6149]({{ site.repository }}/issues/6149)) +- Refine documentation for Windows ([#6153]({{ site.repository }}/issues/6153)) + +### Development Fixes +{: #development-fixes-v3-5-0} + +- 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)) +- Prefer .yaml over .toml ([#5966]({{ site.repository }}/issues/5966)) +- Fix Appveyor with DST-aware cucumber steps ([#5961]({{ site.repository }}/issues/5961)) +- Use Rubocop v0.47.1 till we're ready for v0.48 ([#5989]({{ site.repository }}/issues/5989)) +- Test against Ruby 2.4.0 ([#5687]({{ site.repository }}/issues/5687)) +- rubocop: lib/jekyll/renderer.rb complexity fixes ([#5052]({{ site.repository }}/issues/5052)) +- Use yajl-ruby 1.2.2 (now with 2.4 support) ([#6007]({{ site.repository }}/issues/6007)) +- Bump Rubocop to v0.48 ([#5997]({{ site.repository }}/issues/5997)) +- doc use example.com ([#6031]({{ site.repository }}/issues/6031)) +- fix typo ([#6040]({{ site.repository }}/issues/6040)) +- Fix CI ([#6044]({{ site.repository }}/issues/6044)) +- Remove `ruby RUBY_VERSION` from generated Gemfile ([#5803]({{ site.repository }}/issues/5803)) +- Test if hidden collections output a document with a future date ([#6103]({{ site.repository }}/issues/6103)) +- Add test for uri_escape on reserved characters ([#6086]({{ site.repository }}/issues/6086)) +- Allow you to specify the rouge version via an environment variable for testing ([#6138]({{ site.repository }}/issues/6138)) +- Bump Rubocop to 0.49.1 ([#6093]({{ site.repository }}/issues/6093)) +- Lock nokogiri to 1.7.x for Ruby 2.1 ([#6140]({{ site.repository }}/issues/6140)) + +### Site Enhancements +{: #site-enhancements-v3-5-0} + +- Corrected date for version 3.4.0 ([#5842]({{ site.repository }}/issues/5842)) +- Add the correct year to the 3.4.0 release date ([#5858]({{ site.repository }}/issues/5858)) +- Add documentation about order of interpretation ([#5834]({{ site.repository }}/issues/5834)) +- Documentation on how to build navigation ([#5698]({{ site.repository }}/issues/5698)) +- Navigation has been moved out from docs ([#5927]({{ site.repository }}/issues/5927)) +- Make links in sidebar for current page more prominent ([#5820]({{ site.repository }}/issues/5820)) +- Update normalize.css to v6.0.0 ([#6008]({{ site.repository }}/issues/6008)) +- Docs: rename `gems` to `plugins` ([#6082]({{ site.repository }}/issues/6082)) +- plugins -> gems ([#6110]({{ site.repository }}/issues/6110)) +- Document difference between cgi_escape and uri_escape [#5970]({{ site.repository }}/issues/5970) ([#6081]({{ site.repository }}/issues/6081)) + +### Bug Fixes +{: #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)) +- 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)) +- Re-surface missing public methods in `Jekyll::Document` ([#5975]({{ site.repository }}/issues/5975)) +- absolute_url should not mangle URL if called more than once ([#5789]({{ site.repository }}/issues/5789)) +- patch URLFilters to prevent `//` ([#6058]({{ site.repository }}/issues/6058)) +- add test to ensure variables work in `where_exp` condition ([#5315]({{ site.repository }}/issues/5315)) +- Read explicitly included dot-files in collections. ([#6092]({{ site.repository }}/issues/6092)) +- Default `baseurl` to `nil` instead of empty string ([#6137]({{ site.repository }}/issues/6137)) +- Filters#time helper: Duplicate time before calling #localtime. ([#5996]({{ site.repository }}/issues/5996)) + + +## 3.4.5 / 2017-06-30 +{: #v3-4-5} + +- Backport [#6185]({{ site.repository }}/issues/6185) for v3.4.x: Always normalize the result of the `relative_url` filter ([#6186]({{ site.repository }}/issues/6186)) + + +## 3.4.4 / 2017-06-17 +{: #v3-4-4} + +- Backport [#6137]({{ site.repository }}/issues/6137) for v3.4.x: Default `baseurl` to `nil` instead of empty string ([#6146]({{ site.repository }}/issues/6146)) + + +## 3.4.3 / 2017-03-21 +{: #v3-4-3} + +- Backport [#5957]({{ site.repository }}/issues/5957) for v3.4.x: Allow colons in `uri_escape` filter ([#5968]({{ site.repository }}/issues/5968)) + + +## 3.4.2 / 2017-03-09 +{: #v3-4-2} + +- Backport [#5871]({{ site.repository }}/issues/5871) for v3.4.x: Convert StaticFile liquid representation to a Drop & add front matter defaults support to StaticFiles ([#5940]({{ site.repository }}/issues/5940)) + + +## 3.4.1 / 2017-03-02 +{: #v3-4-1} + +- Backport [#5920]({{ site.repository }}/issues/5920) for v3.4.x: Allow abbreviated post dates ([#5924]({{ site.repository }}/issues/5924)) + + +## 3.4.0 / 2017-01-27 +{: #v3-4-0} + +### Minor Enhancements +{: #minor-enhancements-v3-4-0} + +- Add connector param to `array_to_sentence_string` filter ([#5597]({{ site.repository }}/issues/5597)) +- Adds `group_by_exp` filter ([#5513]({{ site.repository }}/issues/5513)) +- Use Addressable instead of URI to decode ([#5726]({{ site.repository }}/issues/5726)) +- throw IncludeTagError if error occurs in included file ([#5767]({{ site.repository }}/issues/5767)) +- Write Jekyll::Utils::Exec.run for running shell commands. ([#5640]({{ site.repository }}/issues/5640)) +- Use the current year for the LICENSE of theme ([#5712]({{ site.repository }}/issues/5712)) +- Update License ([#5713]({{ site.repository }}/issues/5713)) + +### Bug Fixes +{: #bug-fixes-v3-4-0} + +- Escaped regular expressions when using `post_url`. ([#5605]({{ site.repository }}/issues/5605)) +- fix date parsing in file names to be stricter ([#5609]({{ site.repository }}/issues/5609)) +- Add a module to re-define `ENV["TZ"]` in Windows ([#5612]({{ site.repository }}/issues/5612)) +- Use each instead of map to actually return nothing ([#5668]({{ site.repository }}/issues/5668)) +- include: fix 'no implicit conversion of nil to String' ([#5750]({{ site.repository }}/issues/5750)) +- Don't include the theme's includes_path if it is nil. ([#5780]({{ site.repository }}/issues/5780)) +- test double slash when input = '/' ([#5542]({{ site.repository }}/issues/5542)) +- use logger.info for related posts ([#5822]({{ site.repository }}/issues/5822)) + +### Site Enhancements +{: #site-enhancements-v3-4-0} + +- Use only the used Font Awesome icons. ([#5530]({{ site.repository }}/issues/5530)) +- 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)) +- 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)) + +### Development Fixes +{: #development-fixes-v3-4-0} + +- clean unit-test names in `test/test_tags.rb` ([#5608]({{ site.repository }}/issues/5608)) +- Add cucumber feature to test for bonafide theme gems ([#5384]({{ site.repository }}/issues/5384)) +- Use `assert_nil` instead of `assert_equal nil` ([#5652]({{ site.repository }}/issues/5652)) +- Rubocop -a on lib/jekyll ([#5666]({{ site.repository }}/issues/5666)) +- Bump to rake 12.0 ([#5670]({{ site.repository }}/issues/5670)) +- Rubocop Gemfile ([#5671]({{ site.repository }}/issues/5671)) +- update Classifier-Reborn to 2.1.0 ([#5711]({{ site.repository }}/issues/5711)) +- Rubocop: fix Rakefile and gemspec ([#5745]({{ site.repository }}/issues/5745)) +- Use `assert_nil` ([#5725]({{ site.repository }}/issues/5725)) +- Sort gems in `jekyll.gemspec` ([#5746]({{ site.repository }}/issues/5746)) +- Rubocop: Require consistent comma in multiline literals ([#5761]({{ site.repository }}/issues/5761)) +- Bump rubocop ([#5765]({{ site.repository }}/issues/5765)) +- New rubocop security checks ([#5768]({{ site.repository }}/issues/5768)) +- test/helper: fix flaky plugin path test by removing calls to Dir.chdir without a block ([#5779]({{ site.repository }}/issues/5779)) +- Use latest jemoji gem ([#5782]({{ site.repository }}/issues/5782)) +- Bump htmlproofer ([#5781]({{ site.repository }}/issues/5781)) +- Bump rubies we test against ([#5784]({{ site.repository }}/issues/5784)) +- Bump rdoc to v5.0 ([#5797]({{ site.repository }}/issues/5797)) +- Bump codeclimate-test-reporter to v1.0.5 ([#5798]({{ site.repository }}/issues/5798)) + +### Documentation + +- Improve quickstart docs ([#5689]({{ site.repository }}/issues/5689)) +- Add Jekyll-Post to list of plugins ([#5705]({{ site.repository }}/issues/5705)) +- Add jekyll-numbered-headings ([#5688]({{ site.repository }}/issues/5688)) +- Docs: move permalinks from documents into config ([#5544]({{ site.repository }}/issues/5544)) +- Improve collections docs ([#5691]({{ site.repository }}/issues/5691)) +- Fix [#5730]({{ site.repository }}/issues/5730): add gcc and make to the list of requirements ([#5731]({{ site.repository }}/issues/5731)) +- Remove instructions to install Jekyll 2 on Windows ([#5582]({{ site.repository }}/issues/5582)) +- Fix example URL inconsistency ([#5592]({{ site.repository }}/issues/5592)) +- Replace backticks within HTML blocks with HTML tags ([#5435]({{ site.repository }}/issues/5435)) +- Add jekyll-migrate-permalink ([#5600]({{ site.repository }}/issues/5600)) +- Fix bad config YAML in collections example ([#5587]({{ site.repository }}/issues/5587)) +- Bring documentation on 'Directory Structure' up-to-date ([#5573]({{ site.repository }}/issues/5573)) +- Fixed typo ([#5632]({{ site.repository }}/issues/5632)) +- use backticks for Gemfile for consistency since in the next sentence … ([#5641]({{ site.repository }}/issues/5641)) +- Update Core team list in the README file ([#5643]({{ site.repository }}/issues/5643)) +- Improve Permalinks documentation. ([#5653]({{ site.repository }}/issues/5653)) +- Fix typo in Variables doc page ([#5657]({{ site.repository }}/issues/5657)) +- Fix a couple of typos in the docs ([#5658]({{ site.repository }}/issues/5658)) +- Update windows.md ([#5683]({{ site.repository }}/issues/5683)) +- Improve permalinks docs ([#5693]({{ site.repository }}/issues/5693)) +- Document --unpublished build option ([#5720]({{ site.repository }}/issues/5720)) +- Improve pages docs ([#5692]({{ site.repository }}/issues/5692)) +- Added new includes.md topic to docs ([#5696]({{ site.repository }}/issues/5696)) +- Replace a dead link with a web-archived one ([#5738]({{ site.repository }}/issues/5738)) +- Remove duplicate paragraph. ([#5740]({{ site.repository }}/issues/5740)) +- Addition of a sample "typical post" ([#5473]({{ site.repository }}/issues/5473)) +- Fix a minor grammatical mistake on themes' document ### -dev ([#5748]({{ site.repository }}/issues/5748)) +- Correct comments in data_reader.rb ([#5621]({{ site.repository }}/issues/5621)) +- Add jekyll-pre-commit to plugins list ([#5752]({{ site.repository }}/issues/5752)) +- 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)) +- 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)) +- Got that diaper money? ([#5810]({{ site.repository }}/issues/5810)) +- Sort content by popularity using Google Analytics ([#5812]({{ site.repository }}/issues/5812)) +- Rework CI doc to include multiple providers. ([#5815]({{ site.repository }}/issues/5815)) +- Improve theme docs ([#5690]({{ site.repository }}/issues/5690)) +- Add mention of classifier-reborn for LSI ([#5811]({{ site.repository }}/issues/5811)) +- Added note about --blank flag ([#5802]({{ site.repository }}/issues/5802)) +- Fixed inaccuracy in "Built-in permalink styles" docs ([#5819]({{ site.repository }}/issues/5819)) + + +## 3.3.1 / 2016-11-14 +{: #v3-3-1} + +### Minor Enhancements +{: #minor-enhancements-v3-3-1} + +- Collapse `gsub` for performance ([#5494]({{ site.repository }}/issues/5494)) +- URL: warn if key doesn't exist in url drop ([#5524]({{ site.repository }}/issues/5524)) + +### Bug Fixes +{: #bug-fixes-v3-3-1} + +- Fix typo in `theme_template` README ([#5472]({{ site.repository }}/issues/5472)) +- Do not swallow all exceptions on render ([#5495]({{ site.repository }}/issues/5495)) +- Site template: fixed `_config.yml` comment typo ([#5511]({{ site.repository }}/issues/5511)) +- `jekyll new-theme` should specify Jekyll as a runtime dependency for the theme ([#5457]({{ site.repository }}/issues/5457)) +- Be much more specific about ignoring specific vendored directories. ([#5564]({{ site.repository }}/issues/5564)) +- Only warn about auto-regeneration bug on Bash On Windows. ([#5464]({{ site.repository }}/issues/5464)) +- Allow permalink template to have underscores ([#5572]({{ site.repository }}/issues/5572)) + +### Site Enhancements +{: #site-enhancements-v3-3-1} + +- Documentation: `link` Liquid tag ([#5449]({{ site.repository }}/issues/5449)) +- Updating install instruction link for Jekyll 3 on Windows ([#5475]({{ site.repository }}/issues/5475)) +- Update normalize.css to v5.0.0 ([#5471]({{ site.repository }}/issues/5471)) +- Add jekyll-data to the list of plugins ([#5491]({{ site.repository }}/issues/5491)) +- Add info about checking version + updating ([#5497]({{ site.repository }}/issues/5497)) +- Add jekyll-include-absolute-plugin to list of third-party plugins ([#5492]({{ site.repository }}/issues/5492)) +- Remove jekyll-hook from deployment methods ([#5502]({{ site.repository }}/issues/5502)) +- Update deployment-methods.md ([#5504]({{ site.repository }}/issues/5504)) +- Ubuntu users should install ruby2.3-dev ([#5512]({{ site.repository }}/issues/5512)) +- Remove Glynn as deployment option ([#5519]({{ site.repository }}/issues/5519)) +- Fix broken forum link ([#5466]({{ site.repository }}/issues/5466)) +- Move documentation to docs folder ([#5459]({{ site.repository }}/issues/5459)) +- Fix broken links in CONTRIBUTING ([#5533]({{ site.repository }}/issues/5533)) +- Update documentation on jekyllrb.com ([#5540]({{ site.repository }}/issues/5540)) +- Fix HTML rendering ([#5536]({{ site.repository }}/issues/5536)) +- Remove outdated deployment information ([#5557]({{ site.repository }}/issues/5557)) +- no more invalid US-ASCII on lines 30 and 97 ([#5520]({{ site.repository }}/issues/5520)) +- Add permalinks to docs in '/maintaining/' ([#5532]({{ site.repository }}/issues/5532)) +- Add jekyll-pinboard to list of third-party plugins ([#5514]({{ site.repository }}/issues/5514)) +- Fix formatting in 2-to-3.md ([#5507]({{ site.repository }}/issues/5507)) +- Add two plugins to the plugins page ([#5493]({{ site.repository }}/issues/5493)) +- Use site.baseurl before link and post_url tags ([#5559]({{ site.repository }}/issues/5559)) +- Fix link to jekyll-pinboard plugin ([#5570]({{ site.repository }}/issues/5570)) +- mention `docs` folder as a way to deploy on GitHub Pages ([#5571]({{ site.repository }}/issues/5571)) + +### Development Fixes +{: #development-fixes-v3-3-1} + +- fix rubocop errors on testing with Rubocop 0.44 ([#5489]({{ site.repository }}/issues/5489)) +- script/test: add missing whitespace ([#5479]({{ site.repository }}/issues/5479)) +- Restrict Rubocop version ([#5496]({{ site.repository }}/issues/5496)) +- include a hashbang for all benchmark scripts & make them executable ([#5505]({{ site.repository }}/issues/5505)) +- Update source in script/proof ([#5538]({{ site.repository }}/issues/5538)) +- Collections.feature: conditional steps to have it pass on Windows ([#5546]({{ site.repository }}/issues/5546)) +- Fix tests to get script/test to pass on Windows ([#5526]({{ site.repository }}/issues/5526)) + + +## 3.3.0 / 2016-10-06 +{: #v3-3-0} + +### Minor Enhancements +{: #minor-enhancements-v3-3-0} + +- Colorize interpolated output in logger.info ([#5239]({{ site.repository }}/issues/5239)) +- Site template: exclude Gemfile and Gemfile.lock in site config ([#5293]({{ site.repository }}/issues/5293)) +- Fix [#5233]({{ site.repository }}/issues/5233): Increase our ability to detect Windows. ([#5235]({{ site.repository }}/issues/5235)) +- update gitignore template to ignore theme gems built by user ([#5326]({{ site.repository }}/issues/5326)) +- Adds ability to link to all files ([#5199]({{ site.repository }}/issues/5199)) +- Exclude vendor by default ([#5361]({{ site.repository }}/issues/5361)) +- Add ThemeAssetsReader which reads assets from a theme ([#5364]({{ site.repository }}/issues/5364)) +- Add bundle install to jekyll new command ([#5237]({{ site.repository }}/issues/5237)) +- Add absolute_url and relative_url filters. ([#5399]({{ site.repository }}/issues/5399)) +- Site template: remove `css/` from new site scaffolding ([#5402]({{ site.repository }}/issues/5402)) +- Site template: Move contents of the index.html page to the 'home' layout ([#5420]({{ site.repository }}/issues/5420)) +- Exclude node_modules by default ([#5210]({{ site.repository }}/issues/5210)) +- Run hooks in priority order. ([#5157]({{ site.repository }}/issues/5157)) +- Add `static_file.name` and `.basename` Liquid attributes ([#5264]({{ site.repository }}/issues/5264)) +- set site.url in dev environment to `http://localhost:4000` ([#5431]({{ site.repository }}/issues/5431)) +- Add support for indented link references on excerpt ([#5212]({{ site.repository }}/issues/5212)) + +### Bug Fixes +{: #bug-fixes-v3-3-0} + +- Use jekyll-feed to generate the default site's RSS feed ([#5196]({{ site.repository }}/issues/5196)) +- Site#configure_theme: do not set theme unless it's a string ([#5189]({{ site.repository }}/issues/5189)) +- Convertible: set self.output in #render_all_layouts and #do_layout ([#5337]({{ site.repository }}/issues/5337)) +- Only complain about `kramdown.coderay` if it is actually in the config ([#5380]({{ site.repository }}/issues/5380)) +- Clarify documentation in theme gem's README template ([#5376]({{ site.repository }}/issues/5376)) +- Allow underscore in highlighter language ([#5375]({{ site.repository }}/issues/5375)) +- Site template: set empty url in config file by default ([#5338]({{ site.repository }}/issues/5338)) +- Site template config: prepend 'jekyll serve' with 'bundle exec' ([#5430]({{ site.repository }}/issues/5430)) +- Don't call `File.utime` for StaticFiles if it's a symlink ([#5427]({{ site.repository }}/issues/5427)) +- Fix handling of non-ASCII characters in new `*_url` filters ([#5410]({{ site.repository }}/issues/5410)) +- Remove autoload of Draft which no longer exists. ([#5441]({{ site.repository }}/issues/5441)) +- Fix issue where Windows drive name is stripped from Jekyll.sanitized_path incorrectly ([#5256]({{ site.repository }}/issues/5256)) +- Fix bug where `post_url` tag matched incorrect post with subdirectory ([#4873]({{ site.repository }}/issues/4873)) +- Fix loading data from subdir with a period in name ([#5433]({{ site.repository }}/issues/5433)) +- Revert Commands::Serve#server_address signature change. ([#5456]({{ site.repository }}/issues/5456)) + +### Site Enhancements +{: #site-enhancements-v3-3-0} + +- Document `to_integer` and `inspect` filters ([#5185]({{ site.repository }}/issues/5185)) +- Fix path in the prompt ([#5194]({{ site.repository }}/issues/5194)) +- need subcommand build ([#5190]({{ site.repository }}/issues/5190)) +- Add the Jekyll Cloudinary plugin ([#5183]({{ site.repository }}/issues/5183)) +- Documentation : `new-theme` command ([#5205]({{ site.repository }}/issues/5205)) +- Document `link` Liquid tag ([#5182]({{ site.repository }}/issues/5182)) +- Remove mention of page for link tag in release post ([#5214]({{ site.repository }}/issues/5214)) +- fixed typo ([#5226]({{ site.repository }}/issues/5226)) +- Add missing comma ([#5222]({{ site.repository }}/issues/5222)) +- Maintain aspect ratio with `height: auto;` ([#5254]({{ site.repository }}/issues/5254)) +- Fix a link in deployment-methods.md ([#5244]({{ site.repository }}/issues/5244)) +- Documentation: improve highlight in `Creating a theme` ([#5249]({{ site.repository }}/issues/5249)) +- Bundler isn't installed by default ([#5258]({{ site.repository }}/issues/5258)) +- Update troubleshooting documentation to include fix for issue with vendored gems ([#5271]({{ site.repository }}/issues/5271)) +- Link `--lsi` option's description to Wikipedia docs on LSI ([#5274]({{ site.repository }}/issues/5274)) +- Document `--profile` option on the configuration page ([#5279]({{ site.repository }}/issues/5279)) +- Update homepage to sync with merge of [#5258]({{ site.repository }}/issues/5258) ([#5287]({{ site.repository }}/issues/5287)) +- Add post about Jekyll Admin initial release ([#5291]({{ site.repository }}/issues/5291)) +- Replace liquid highlight tag with backticks ([#5262]({{ site.repository }}/issues/5262)) +- Word update ([#5294]({{ site.repository }}/issues/5294)) +- Site documentation section links always point to https://jekyllrb.com ([#5281]({{ site.repository }}/issues/5281)) +- Missing `:site, :post_render` payload documentation on site ([#5280]({{ site.repository }}/issues/5280)) +- 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)) +- 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)) +- .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)) +- Add jekyll-menus to the list of plugins. ([#5397]({{ site.repository }}/issues/5397)) +- macOS and one grammar fix ([#5403]({{ site.repository }}/issues/5403)) +- 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)) +- 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)) + +### Development Fixes +{: #development-fixes-v3-3-0} + +- Update appveyor.yml and fix optional deps for Ruby x64 ([#5180]({{ site.repository }}/issues/5180)) +- Improve tests for Jekyll::PluginManager ([#5167]({{ site.repository }}/issues/5167)) +- Update Ruby versions in travis.yml ([#5221]({{ site.repository }}/issues/5221)) +- Avoid installing unnecessary gems for site testing ([#5272]({{ site.repository }}/issues/5272)) +- Proposal: Affinity teams and their captains ([#5273]({{ site.repository }}/issues/5273)) +- Replace duplicate with positive local test in issue template ([#5286]({{ site.repository }}/issues/5286)) +- Update AppVeyor config. ([#5240]({{ site.repository }}/issues/5240)) +- Execute jekyll from clone instead of defined binary when running 'script/default-site' ([#5295]({{ site.repository }}/issues/5295)) +- rubocop: lib/jekyll/document.rb complexity fixes ([#5045]({{ site.repository }}/issues/5045)) +- Proxy a number of Convertible methods to Renderer ([#5308]({{ site.repository }}/issues/5308)) +- Run executable for Cucumber via Ruby instead of Shell ([#5383]({{ site.repository }}/issues/5383)) +- Appease Rubocop ([#5381]({{ site.repository }}/issues/5381)) +- remove features' directories on windows with proper access ([#5389]({{ site.repository }}/issues/5389)) +- `site_configuration.feature`: use UTC format in timezone ([#5416]({{ site.repository }}/issues/5416)) +- swallow bundle output from `jekyll new` while in CI ([#5408]({{ site.repository }}/issues/5408)) +- Add .editorconfig ([#5412]({{ site.repository }}/issues/5412)) + + +## 3.2.1 / 2016-08-02 +{: #v3-2-1} + +### Bug Fixes +{: #bug-fixes-v3-2-1} + +- Include theme directories in default gemspec ([#5152]({{ site.repository }}/issues/5152)) +- Fix for symlinked themes ([#5156]({{ site.repository }}/issues/5156)) +- Layout: set `relative_path` without using Pathname ([#5164]({{ site.repository }}/issues/5164)) + +### Development Fixes +{: #development-fixes-v3-2-1} + +- Add test to build the default site ([#5154]({{ site.repository }}/issues/5154)) +- script/default-site: specify `BUNDLE_GEMFILE` for new site ([#5178]({{ site.repository }}/issues/5178)) +- script/default-site: read Jekyll source from local clone ([#5188]({{ site.repository }}/issues/5188)) + +### Site Enhancements +{: #site-enhancements-v3-2-1} + +- Enable site excerpts ([#5150]({{ site.repository }}/issues/5150)) +- Initial 404 page ([#5143]({{ site.repository }}/issues/5143)) +- Remove the "this feature is unreleased" warning from the theme docs ([#5158]({{ site.repository }}/issues/5158)) +- Future True on GitHub Pages note ([#5173]({{ site.repository }}/issues/5173)) +- Minor updates and corrections ([#5177]({{ site.repository }}/issues/5177)) +- index.html: update instructions to require bundler ([#5169]({{ site.repository }}/issues/5169)) +- docs/quickstart: update instructions to require bundler ([#5168]({{ site.repository }}/issues/5168)) + + +## 3.2.0 / 2016-07-26 +{: #v3-2-0} + +### Minor Enhancements +{: #minor-enhancements-v3-2-0} + +- Stop testing with Ruby 2.0.x EOL ([#4381]({{ site.repository }}/issues/4381)) +- Allow collections to have documents that have no file extension ([#4545]({{ site.repository }}/issues/4545)) +- Add size property to `group_by` result ([#4557]({{ site.repository }}/issues/4557)) +- Site Template: Removed unnecessary nesting from `_base.scss` ([#4637]({{ site.repository }}/issues/4637)) +- Adding a debug log statement for skipped future documents. ([#4558]({{ site.repository }}/issues/4558)) +- Site Template: Changed main `<div>` to `<main>` and added accessibility info ([#4636]({{ site.repository }}/issues/4636)) +- Add array support to `where` filter ([#4555]({{ site.repository }}/issues/4555)) +- 'jekyll clean': also remove .sass-cache ([#4652]({{ site.repository }}/issues/4652)) +- Clean up `Tags::PostUrl` a bit, including better errors and date parsing ([#4670]({{ site.repository }}/issues/4670)) +- Use `String#encode` for `xml_escape` filter instead of `CGI.escapeHTML` ([#4694]({{ site.repository }}/issues/4694)) +- Add `show_dir_listing` option for serve command and fix index file names ([#4533]({{ site.repository }}/issues/4533)) +- Site Template: write a Gemfile which is educational to the new site ([#4542]({{ site.repository }}/issues/4542)) +- Site template: add explanation of site variables in the example `_config.yml` ([#4704]({{ site.repository }}/issues/4704)) +- Adds `link` Liquid tag to make generation of URLs easier ([#4624]({{ site.repository }}/issues/4624)) +- Allow static files to be symlinked in unsafe mode or non-prod environments ([#4640]({{ site.repository }}/issues/4640)) +- Add `:after_init` hook & add `Site#config=` to make resetting config easy ([#4703]({{ site.repository }}/issues/4703)) +- DocumentDrop: add `#<=>` which sorts by date (falling back to path) ([#4741]({{ site.repository }}/issues/4741)) +- Add a `where_exp` filter for filtering by expression ([#4478]({{ site.repository }}/issues/4478)) +- Globalize Jekyll's Filters. ([#4792]({{ site.repository }}/issues/4792)) +- Gem-based themes ([#4595]({{ site.repository }}/issues/4595)) +- Allow symlinks if they point to stuff inside `site.source` ([#4710]({{ site.repository }}/issues/4710)) +- Update colorator dependency to v1.x ([#4855]({{ site.repository }}/issues/4855)) +- Move EntryFilter to use Pathutil & fix `glob_include?` ([#4859]({{ site.repository }}/issues/4859)) +- Add 'jekyll new-theme' command to help users get up and running creating a theme ([#4848]({{ site.repository }}/issues/4848)) +- `markdownify` and `smartify` should convert input to string before conversion ([#4958]({{ site.repository }}/issues/4958)) +- Run `Site#generate` for 'jekyll doctor' to catch plugin issues ([#5005]({{ site.repository }}/issues/5005)) +- Add `normalize_whitespace` filter ([#4917]({{ site.repository }}/issues/4917)) +- Move bin/jekyll to exe/jekyll to prevent collision with binstubs ([#5014]({{ site.repository }}/issues/5014)) +- Cleaning up site template & theme updates. ([#4922]({{ site.repository }}/issues/4922)) +- Add fetch method to Drops ([#5056]({{ site.repository }}/issues/5056)) +- Extract tag name to class method ([#5063]({{ site.repository }}/issues/5063)) +- check if relative URL contains a colon ([#5069]({{ site.repository }}/issues/5069)) +- Enable strict (or lax) liquid parsing via a config variable. ([#5053]({{ site.repository }}/issues/5053)) +- Add filter: `to_integer` ([#5101]({{ site.repository }}/issues/5101)) +- watcher: pass site instance to watch plugin ([#5109]({{ site.repository }}/issues/5109)) +- Show liquid warnings. ([#5129]({{ site.repository }}/issues/5129)) +- Add `--baseurl` to `build` subcommand ([#5135]({{ site.repository }}/issues/5135)) + +### Bug Fixes +{: #bug-fixes-v3-2-0} + +- Site Template: Added a default lang attribute ([#4633]({{ site.repository }}/issues/4633)) +- Site template: Escape title and description where it is used in HTML ([#4606]({{ site.repository }}/issues/4606)) +- `Document#date`: drafts which have no date should use source file mtime ([#4611]({{ site.repository }}/issues/4611)) +- `Filters#time`: clone an input Time so as to be non-destructive ([#4590]({{ site.repository }}/issues/4590)) +- Doctor: fix issue where `--config` wasn't a recognized flag ([#4598]({{ site.repository }}/issues/4598)) +- Ensures `related_posts` are only set for a post ([#4620]({{ site.repository }}/issues/4620)) +- `EntryFilter#special?`: ignore filenames which begin with `~` ([#4491]({{ site.repository }}/issues/4491)) +- Cleaner: `keep_files` should only apply to the beginning of paths, not substrings with index > 0 ([#3849]({{ site.repository }}/issues/3849)) +- Use SSLEnable instead of EnableSSL and make URL HTTPS. ([#4693]({{ site.repository }}/issues/4693)) +- convertible: use `Document::YAML_FRONT_MATTER_REGEXP` to parse transformable files ([#4786]({{ site.repository }}/issues/4786)) +- Example in the site template should be IANA-approved example.com ([#4793]({{ site.repository }}/issues/4793)) +- 3.2.x/master: Fix defaults for Documents (posts/collection docs) ([#4808]({{ site.repository }}/issues/4808)) +- Don't rescue LoadError or bundler load errors for Bundler. ([#4857]({{ site.repository }}/issues/4857)) +- `Serve.process` should receive same config as `Build.process` ([#4953]({{ site.repository }}/issues/4953)) +- Prevent reset of page in Liquid payload right before rendering layouts ([#5009]({{ site.repository }}/issues/5009)) +- Add missing fields to ExcerptDrop ([#5067]({{ site.repository }}/issues/5067)) +- Stringify configuration overrides before first use ([#5060]({{ site.repository }}/issues/5060)) +- hooks: move `after_init` hook call at the end of `Site#initialize` ([#5106]({{ site.repository }}/issues/5106)) +- filters: raise error if no input given to date filters ([#5127]({{ site.repository }}/issues/5127)) +- `where_exp` filter should filter posts ([#4860]({{ site.repository }}/issues/4860)) + +### Forward Ports +{: #forward-ports-v3-2-0} + +- From v3.1.4: Add ExcerptDrop and remove excerpt's ability to refer to itself in Liquid ([#4941]({{ site.repository }}/issues/4941)) +- From v3.1.4: Configuration permalink fix and addition of Configuration.from and sorting `site.collections` by label ([#4942]({{ site.repository }}/issues/4942)) +- From v3.1.4: Fix {% raw %}`{{ layout }}`{% endraw %} oddities (proper inheritance & fixing overflow of old data) ([#4943]({{ site.repository }}/issues/4943)) +- From v3.1.5: Sort the results of the `require_all` glob ([#4944]({{ site.repository }}/issues/4944)) +- From v3.1.6: Add ability to render drops as JSON ([#4945]({{ site.repository }}/issues/4945)) + +### Development Fixes +{: #development-fixes-v3-2-0} + +- Add project maintainer profile links ([#4591]({{ site.repository }}/issues/4591)) +- Fix state leakage in Kramdown test ([#4618]({{ site.repository }}/issues/4618)) +- Unify method for copying special files from repo to site ([#4601]({{ site.repository }}/issues/4601)) +- Refresh the contributing file ([#4596]({{ site.repository }}/issues/4596)) +- change smartify doc from copy/paste of markdownify doc ([#4653]({{ site.repository }}/issues/4653)) +- Update Rake & disable warnings when running tests ([#4720]({{ site.repository }}/issues/4720)) +- Fix many warnings ([#4537]({{ site.repository }}/issues/4537)) +- Don't blindly assume the last system when determining "open" cmd ([#4717]({{ site.repository }}/issues/4717)) +- Fix "locally" typo in contributing documentation ([#4756]({{ site.repository }}/issues/4756)) +- Update Rubocop rules ([#4886]({{ site.repository }}/issues/4886)) +- Flesh out the issue template to be much more detailed ([#4849]({{ site.repository }}/issues/4849)) +- Fixing rubocop offenses in lib/jekyll/cleaner.rb ([#4892]({{ site.repository }}/issues/4892)) +- Update `jekyll/commands*` to pass rubocop rules ([#4888]({{ site.repository }}/issues/4888)) +- Clean up many test files to pass Rubocop rules ([#4902]({{ site.repository }}/issues/4902)) +- Rubocop cleanup for some utils and further test files ([#4916]({{ site.repository }}/issues/4916)) +- Rubocop: Low hanging fruit ([#4936]({{ site.repository }}/issues/4936)) +- Rubocop: `Drop` changes from v3.1 forward-ports ([#4949]({{ site.repository }}/issues/4949)) +- Rubocop: cleanup for misc files ([#4946]({{ site.repository }}/issues/4946)) +- Rubocop: Stevenson ([#4951]({{ site.repository }}/issues/4951)) +- Rubocop: lib/jekyll/entry_filter.rb ([#4950]({{ site.repository }}/issues/4950)) +- Rubocop: `test/*` ([#4947]({{ site.repository }}/issues/4947)) +- Rubocop: features ([#4934]({{ site.repository }}/issues/4934)) +- Rubocop: Liquid renderer ([#4933]({{ site.repository }}/issues/4933)) +- Rubocop: converters ([#4931]({{ site.repository }}/issues/4931)) +- Rubocop: Site Drop ([#4948]({{ site.repository }}/issues/4948)) +- Rubocop: tags ([#4938]({{ site.repository }}/issues/4938)) +- Rubocop: Readers ([#4932]({{ site.repository }}/issues/4932)) +- rubocop: jekyll/lib/frontmatter_defaults.rb ([#4974]({{ site.repository }}/issues/4974)) +- rubocop: features/step_definitions.rb ([#4956]({{ site.repository }}/issues/4956)) +- Rubocop theme and url jekyll libs ([#4959]({{ site.repository }}/issues/4959)) +- Rubocop jekyll.rb ([#4966]({{ site.repository }}/issues/4966)) +- Rubocop: use %r for all regular expressions. ([#4979]({{ site.repository }}/issues/4979)) +- Cleanup and make misc files compliant with Rubocop. ([#4940]({{ site.repository }}/issues/4940)) +- Rubocop: jekyll/lib/site.rb ([#4973]({{ site.repository }}/issues/4973)) +- Add timings for each scenario in cucumber & print worst offenders ([#4908]({{ site.repository }}/issues/4908)) +- rubocop: jekyll/lib/filters.rb ([#4993]({{ site.repository }}/issues/4993)) +- Fix rubocop offenses in exe/jekyll ([#5017]({{ site.repository }}/issues/5017)) +- Rubocop: lib/jekyll/command.rb ([#5018]({{ site.repository }}/issues/5018)) +- rubocop: lib/jekyll/static_file.rb ([#5019]({{ site.repository }}/issues/5019)) +- rubocop: lib/jekyll/utils.rb ([#5026]({{ site.repository }}/issues/5026)) +- rubocop: lib/jekyll/regenerator.rb ([#5025]({{ site.repository }}/issues/5025)) +- rubocop: lib/jekyll/configuration.rb ([#5024]({{ site.repository }}/issues/5024)) +- rubocop: lib/jekyll/renderer.rb style fixes ([#5032]({{ site.repository }}/issues/5032)) +- rubocop: lib/jekyll/convertible.rb style fixes ([#5031]({{ site.repository }}/issues/5031)) +- rubocop: lib/jekyll/document.rb style fixes ([#5030]({{ site.repository }}/issues/5030)) +- Remove ruby-head from Travis matrix & fix jruby failures ([#5015]({{ site.repository }}/issues/5015)) +- Remove useless statement from Configuration test ([#5065]({{ site.repository }}/issues/5065)) +- Change baseurl to example.com for some test cases ([#5068]({{ site.repository }}/issues/5068)) +- use activesupport < 5 if we are on a Ruby < 2.2.2 ([#5100]({{ site.repository }}/issues/5100)) +- Internal documentation for working on Jekyll ([#5011]({{ site.repository }}/issues/5011)) +- rubocop: lib/jekyll/collection.rb ([#5022]({{ site.repository }}/issues/5022)) +- tests: Typo fixes. ([#5114]({{ site.repository }}/issues/5114)) +- Normalize yml files. ([#5116]({{ site.repository }}/issues/5116)) +- Whitespace cleanup. ([#5113]({{ site.repository }}/issues/5113)) +- Add AppVeyor support. ([#5115]({{ site.repository }}/issues/5115)) +- appveyor.yml: drop Ruby 2.0.0. ([#5119]({{ site.repository }}/issues/5119)) +- Fix indentation in benchmark ([#5124]({{ site.repository }}/issues/5124)) +- Style fixes for Rubocop 0.42.0 - var == 0 becomes var.zero? - when defining method_missing, also define respond_to_missing? ([#5137]({{ site.repository }}/issues/5137)) + +### Site Enhancements +{: #site-enhancements-v3-2-0} + +- Add jekyll-seo-tag, jekyll-avatar, and jekyll-sitemap to the site ([#4553]({{ site.repository }}/issues/4553)) +- Add Google search query to /docs/help/ ([#4589]({{ site.repository }}/issues/4589)) +- Upgrading, documentation ([#4597]({{ site.repository }}/issues/4597)) +- Add 'view source' entry ([#4602]({{ site.repository }}/issues/4602)) +- Add jekyll-video-embed to list of third-party plugins. ([#4621]({{ site.repository }}/issues/4621)) +- Adding Aerobatic to list of deployment options ([#4630]({{ site.repository }}/issues/4630)) +- Update documentation: HTMLProofer CLI command ([#4641]({{ site.repository }}/issues/4641)) +- Document that subdirectories of `_posts` are no longer categories ([#4639]({{ site.repository }}/issues/4639)) +- Update continuous-integration docs with sudo: false information ([#4628]({{ site.repository }}/issues/4628)) +- Blog post on refreshed contributing file and new affinity teams ([#4645]({{ site.repository }}/issues/4645)) +- Fixes typo on collections ([#4647]({{ site.repository }}/issues/4647)) +- Documentation: future option also works for collections ([#4682]({{ site.repository }}/issues/4682)) +- Additional package needed for Fedora 23 Workspace ([#4685]({{ site.repository }}/issues/4685)) +- Fix typo on Chocolatey name in Windows documentation ([#4686]({{ site.repository }}/issues/4686)) +- Use the correct URL, Fixes [#4698]({{ site.repository }}/issues/4698) ([#4699]({{ site.repository }}/issues/4699)) +- Add jekyll-paspagon plugin ([#4700]({{ site.repository }}/issues/4700)) +- Bold-italicize note in assets documentation about needing front matter ([#4706]({{ site.repository }}/issues/4706)) +- Highlight the `script/` calls in the Contributing documentation ([#4712]({{ site.repository }}/issues/4712)) +- Add Hawkins to the list of third-party plugins ([#4755]({{ site.repository }}/issues/4755)) +- Fix a typo in pagination doc ([#4763]({{ site.repository }}/issues/4763)) +- Switch second GitHub Pages link to HTTPS ([#4760]({{ site.repository }}/issues/4760)) +- Explain data file format requirements more clearly in documentation ([#4781]({{ site.repository }}/issues/4781)) +- Add jekyll-i18n_tags to list of third-party plugins ([#4775]({{ site.repository }}/issues/4775)) +- Remove Leonard Lamprecht's website from Sites page ([#4771]({{ site.repository }}/issues/4771)) +- Updates documentation for collections to include `date` property ([#4769]({{ site.repository }}/issues/4769)) +- Added an explicit rerun note to configuration.md, defaults section ([#4734]({{ site.repository }}/issues/4734)) +- Update Rack-Jekyll Heroku deployment blog post url ([#4789]({{ site.repository }}/issues/4789)) +- Added missing single quote on rsync client side command ([#4813]({{ site.repository }}/issues/4813)) +- Organize Form Platforms-as-a-Service into unified list & add FormSpree.io ([#4754]({{ site.repository }}/issues/4754)) +- Fixed typo on Configuration page ([#4804]({{ site.repository }}/issues/4804)) +- Update FormKeep URL on the Resources doc ([#4844]({{ site.repository }}/issues/4844)) +- site: use liquid & reduce some whitespace noise ([#4854]({{ site.repository }}/issues/4854)) +- Add jekyll-breadcrumbs to list of third-party plugins ([#4874]({{ site.repository }}/issues/4874)) +- Added Pug converter to list of third-party plugins ([#4872]({{ site.repository }}/issues/4872)) +- Add jekyll-ideal-image-slider to list of third-party plugins ([#4863]({{ site.repository }}/issues/4863)) +- Add Jekyll Tips and the Cheatsheet to the list of resources ([#4887]({{ site.repository }}/issues/4887)) +- Removed extra `</p>` from `site/_docs/permalinks.md` ([#4890]({{ site.repository }}/issues/4890)) +- Add pubstorm deployment instructions to docs ([#4881]({{ site.repository }}/issues/4881)) +- Corrected pagination docs for hidden: true feature ([#4903]({{ site.repository }}/issues/4903)) +- Remove a Broken Link for Refheap Plugin ([#4971]({{ site.repository }}/issues/4971)) +- Instructions on how to install github-gem on Windows ([#4975]({{ site.repository }}/issues/4975)) +- Minor tweak to fix missing apostrophe ([#4962]({{ site.repository }}/issues/4962)) +- Instructions on how to install github-gem on Windows (v2) ([#4977]({{ site.repository }}/issues/4977)) +- Fix inaccurate HTTP response header field name ([#4976]({{ site.repository }}/issues/4976)) +- Add post about GSoC project ([#4980]({{ site.repository }}/issues/4980)) +- Link to the HTML page instead of Markdown ([#4985]({{ site.repository }}/issues/4985)) +- Update normalize.css to v4.0.0. ([#4989]({{ site.repository }}/issues/4989)) +- Add jekyll-tags-list-plugin to list of third-party plugins ([#5000]({{ site.repository }}/issues/5000)) +- Windows docs: Command needs to be called from blog path ([#5006]({{ site.repository }}/issues/5006)) +- Update text to be consistent with example ([#5010]({{ site.repository }}/issues/5010)) +- Update template links to point to core Liquid site ([#5012]({{ site.repository }}/issues/5012)) +- Add generator-jekyllized to third-party plugins ([#5027]({{ site.repository }}/issues/5027)) +- Add Jekyll Art Gallery generator plugin to list of third-party plugins ([#5043]({{ site.repository }}/issues/5043)) +- Add Formingo to the list of Jekyll form SaaS ([#5054]({{ site.repository }}/issues/5054)) +- Highlight help nav item when navigated to. ([#5058]({{ site.repository }}/issues/5058)) +- Update normalize.css to v4.2.0. ([#5096]({{ site.repository }}/issues/5096)) +- Updates html-proofer code ([#5098]({{ site.repository }}/issues/5098)) +- Jekyll Community ([#5097]({{ site.repository }}/issues/5097)) +- Typo in documentation file templates.md ([#5117]({{ site.repository }}/issues/5117)) +- Slightly, improve PNG compression. ([#5112]({{ site.repository }}/issues/5112)) +- docs: add jekyll-maps plugin reference ([#5123]({{ site.repository }}/issues/5123)) +- docs: fix link to plugins page source ([#5122]({{ site.repository }}/issues/5122)) +- Update the configuration docs to match the code ([#5131]({{ site.repository }}/issues/5131)) +- Removed confusing word repetition. ([#5139]({{ site.repository }}/issues/5139)) +- Add a note about script/fmt ([#5138]({{ site.repository }}/issues/5138)) + + +## 3.1.6 / 2016-05-19 +{: #v3-1-6} + +### Bug Fixes +{: #bug-fixes-v3-1-6} + +- Add ability to `jsonify` Drops such that, e.g. `site | jsonify`, works. ([#4914]({{ site.repository }}/issues/4914)) + + +## 3.1.5 / 2016-05-18 +{: #v3-1-5} + +### Bug Fixes +{: #bug-fixes-v3-1-5} + +- Sort the results of the `require_all` glob (affects Linux only). ([#4912]({{ site.repository }}/issues/4912)) + + +## 3.1.4 / 2016-05-18 +{: #v3-1-4} + +### Bug Fixes +{: #bug-fixes-v3-1-4} + +- Add `ExcerptDrop` and remove excerpt's ability to refer to itself in Liquid ([#4907]({{ site.repository }}/issues/4907)) +- Configuration permalink fix where `collections.posts.permalink` inherits properly from `permalink` only when it doesn't exist ([#4910]({{ site.repository }}/issues/4910)) +- Add `Configuration.from` to make it easier to build configs from just a hash +- Sorting `site.collections` in Liquid by label ([#4910]({{ site.repository }}/issues/4910)) +- Fix bug where `layout` in Liquid would inherit from previously-rendered layouts' metadatas ([#4909]({{ site.repository }}/issues/4909)) +- Fix bug where `layout` in Liquid would override in the wrong direction (more-specific layouts' data were overwritten by their parent layouts' data; this has now been reversed) ([#4909]({{ site.repository }}/issues/4909)) + + +## 3.1.3 / 2016-04-18 +{: #v3-1-3} + +- Fix defaults for Documents to lookup defaults based on `relative_path` instead of `url` ([#4807]({{ site.repository }}/issues/4807)) +- Use SSLEnable instead of EnableSSL and make URL HTTPS (WEBrick) ([#4693]({{ site.repository }}/issues/4693)) + + +## 3.1.2 / 2016-02-19 +{: #v3-1-2} + +### Minor Enhancements +{: #minor-enhancements-v3-1-2} + +- Include `.rubocop.yml` in Gem ([#4437]({{ site.repository }}/issues/4437)) +- `LiquidRenderer#parse`: parse with line numbers. ([#4452]({{ site.repository }}/issues/4452)) +- Add consistency to the no-subcommand deprecation message ([#4505]({{ site.repository }}/issues/4505)) + +### Bug Fixes +{: #bug-fixes-v3-1-2} + +- Fix syntax highlighting in kramdown by making `@config` accessible in the Markdown converter. ([#4428]({{ site.repository }}/issues/4428)) +- `Jekyll.sanitized_path`: sanitizing a questionable path should handle tildes ([#4492]({{ site.repository }}/issues/4492)) +- Fix `titleize` so already capitalized words are not dropped ([#4525]({{ site.repository }}/issues/4525)) +- Permalinks which end in a slash should always output HTML ([#4546]({{ site.repository }}/issues/4546)) + +### Development Fixes +{: #development-fixes-v3-1-2} + +- Require at least cucumber version 2.1.0 ([#4514]({{ site.repository }}/issues/4514)) + +### Site Enhancements +{: #site-enhancements-v3-1-2} + +- Add jekyll-toc plugin ([#4429]({{ site.repository }}/issues/4429)) +- Docs: Quickstart - added documentation about the `--force` option ([#4410]({{ site.repository }}/issues/4410)) +- 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)) +- 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)) +- Update & prune the short list of example sites ([#4374]({{ site.repository }}/issues/4374)) +- Added amp-jekyll plugin to plugins docs ([#4517]({{ site.repository }}/issues/4517)) +- A few grammar fixes ([#4512]({{ site.repository }}/issues/4512)) +- Correct a couple mistakes in structure.md ([#4522]({{ site.repository }}/issues/4522)) + + +## 3.1.1 / 2016-01-29 +{: #v3-1-1} + +### Meta + +- Update the Code of Conduct to the latest version ([#4402]({{ site.repository }}/issues/4402)) + +### Bug Fixes +{: #bug-fixes-v3-1-1} + +- `Page#dir`: ensure it ends in a slash ([#4403]({{ site.repository }}/issues/4403)) +- Add `Utils.merged_file_read_opts` to unify reading & strip the BOM ([#4404]({{ site.repository }}/issues/4404)) +- `Renderer#output_ext`: honor folders when looking for ext ([#4401]({{ site.repository }}/issues/4401)) + +### Development Fixes +{: #development-fixes-v3-1-1} + +- Suppress stdout in liquid profiling test ([#4409]({{ site.repository }}/issues/4409)) + + +## 3.1.0 / 2016-01-23 +{: #v3-1-0} + +### Minor Enhancements +{: #minor-enhancements-v3-1-0} + +- Use `Liquid::Drop`s instead of `Hash`es in `#to_liquid` ([#4277]({{ site.repository }}/issues/4277)) +- Add 'sample' Liquid filter Equivalent to Array#sample functionality ([#4223]({{ site.repository }}/issues/4223)) +- Cache parsed include file to save liquid parsing time. ([#4120]({{ site.repository }}/issues/4120)) +- Slightly speed up url sanitization and handle multiples of ///. ([#4168]({{ site.repository }}/issues/4168)) +- Print debug message when a document is skipped from reading ([#4180]({{ site.repository }}/issues/4180)) +- Include tag should accept multiple variables in the include name ([#4183]({{ site.repository }}/issues/4183)) +- Add `-o` option to serve command which opens server URL ([#4144]({{ site.repository }}/issues/4144)) +- Add CodeClimate platform for better code quality. ([#4220]({{ site.repository }}/issues/4220)) +- General improvements for WEBrick via jekyll serve such as SSL & custom headers ([#4224]({{ site.repository }}/issues/4224), [#4228]({{ site.repository }}/issues/4228)) +- Add a default charset to content-type on webrick. ([#4231]({{ site.repository }}/issues/4231)) +- Switch `PluginManager` to use `require_with_graceful_fail` for better UX ([#4233]({{ site.repository }}/issues/4233)) +- Allow quoted date in front matter defaults ([#4184]({{ site.repository }}/issues/4184)) +- Add a Jekyll doctor warning for URLs that only differ by case ([#3171]({{ site.repository }}/issues/3171)) +- drops: create one base Drop class which can be set as mutable or not ([#4285]({{ site.repository }}/issues/4285)) +- drops: provide `#to_h` to allow for hash introspection ([#4281]({{ site.repository }}/issues/4281)) +- Shim subcommands with indication of gem possibly required so users know how to use them ([#4254]({{ site.repository }}/issues/4254)) +- Add smartify Liquid filter for SmartyPants ([#4323]({{ site.repository }}/issues/4323)) +- Raise error on empty permalink ([#4361]({{ site.repository }}/issues/4361)) +- Refactor Page#permalink method ([#4389]({{ site.repository }}/issues/4389)) + +### Bug Fixes +{: #bug-fixes-v3-1-0} + +- Pass build options into `clean` command ([#4177]({{ site.repository }}/issues/4177)) +- Allow users to use .htm and .xhtml (XHTML5.) ([#4160]({{ site.repository }}/issues/4160)) +- Prevent Shell Injection. ([#4200]({{ site.repository }}/issues/4200)) +- Convertible should make layout data accessible via `layout` instead of `page` ([#4205]({{ site.repository }}/issues/4205)) +- Avoid using `Dir.glob` with absolute path to allow special characters in the path ([#4150]({{ site.repository }}/issues/4150)) +- Handle empty config files ([#4052]({{ site.repository }}/issues/4052)) +- Rename `@options` so that it does not impact Liquid. ([#4173]({{ site.repository }}/issues/4173)) +- utils/drops: update Drop to support `Utils.deep_merge_hashes` ([#4289]({{ site.repository }}/issues/4289)) +- Make sure jekyll/drops/drop is loaded first. ([#4292]({{ site.repository }}/issues/4292)) +- Convertible/Page/Renderer: use payload hash accessor & setter syntax for backwards-compatibility ([#4311]({{ site.repository }}/issues/4311)) +- Drop: fix hash setter precedence ([#4312]({{ site.repository }}/issues/4312)) +- utils: `has_yaml_header?` should accept files with extraneous spaces ([#4290]({{ site.repository }}/issues/4290)) +- Escape html from site.title and page.title in site template ([#4307]({{ site.repository }}/issues/4307)) +- Allow custom file extensions if defined in `permalink` front matter ([#4314]({{ site.repository }}/issues/4314)) +- 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)) +- Extract title from filename successfully when no date. ([#4195]({{ site.repository }}/issues/4195)) + +### Development Fixes +{: #development-fixes-v3-1-0} + +- `jekyll-docs` should be easily release-able ([#4152]({{ site.repository }}/issues/4152)) +- Allow use of Cucumber 2.1 or greater ([#4181]({{ site.repository }}/issues/4181)) +- Modernize Kramdown for Markdown converter. ([#4109]({{ site.repository }}/issues/4109)) +- Change TestDoctorCommand to JekyllUnitTest... ([#4263]({{ site.repository }}/issues/4263)) +- Create namespaced rake tasks in separate `.rake` files under `lib/tasks` ([#4282]({{ site.repository }}/issues/4282)) +- markdown: refactor for greater readability & efficiency ([#3771]({{ site.repository }}/issues/3771)) +- Fix many Rubocop style errors ([#4301]({{ site.repository }}/issues/4301)) +- Fix spelling of "GitHub" in docs and history ([#4322]({{ site.repository }}/issues/4322)) +- Reorganize and cleanup the Gemfile, shorten required depends. ([#4318]({{ site.repository }}/issues/4318)) +- Remove script/rebund. ([#4341]({{ site.repository }}/issues/4341)) +- Implement codeclimate platform ([#4340]({{ site.repository }}/issues/4340)) +- Remove ObjectSpace dumping and start using inherited, it's faster. ([#4342]({{ site.repository }}/issues/4342)) +- Add script/travis so all people can play with Travis-CI images. ([#4338]({{ site.repository }}/issues/4338)) +- Move Cucumber to using RSpec-Expectations and furthering JRuby support. ([#4343]({{ site.repository }}/issues/4343)) +- Rearrange Cucumber and add some flair. ([#4347]({{ site.repository }}/issues/4347)) +- Remove old FIXME ([#4349]({{ site.repository }}/issues/4349)) +- Clean up the Gemfile (and keep all the necessary dependencies) ([#4350]({{ site.repository }}/issues/4350)) + +### Site Enhancements +{: #site-enhancements-v3-1-0} + +- Add three plugins to directory ([#4163]({{ site.repository }}/issues/4163)) +- Add upgrading docs from 2.x to 3.x ([#4157]({{ site.repository }}/issues/4157)) +- Add `protect_email` to the plugins index. ([#4169]({{ site.repository }}/issues/4169)) +- Add `jekyll-deploy` to list of third-party plugins ([#4179]({{ site.repository }}/issues/4179)) +- Clarify plugin docs ([#4154]({{ site.repository }}/issues/4154)) +- Add Kickster to deployment methods in documentation ([#4190]({{ site.repository }}/issues/4190)) +- Add DavidBurela's tutorial for Windows to Windows docs page ([#4210]({{ site.repository }}/issues/4210)) +- Change GitHub code block to highlight tag to avoid it overlaps parent div ([#4121]({{ site.repository }}/issues/4121)) +- Update FormKeep link to be something more specific to Jekyll ([#4243]({{ site.repository }}/issues/4243)) +- Remove example Roger Chapman site, as the domain doesn't exist ([#4249]({{ site.repository }}/issues/4249)) +- Added configuration options for `draft_posts` to configuration docs ([#4251]({{ site.repository }}/issues/4251)) +- Fix checklist in `_assets.md` ([#4259]({{ site.repository }}/issues/4259)) +- Add Markdown examples to Pages docs ([#4275]({{ site.repository }}/issues/4275)) +- Add jekyll-paginate-category to list of third-party plugins ([#4273]({{ site.repository }}/issues/4273)) +- Add `jekyll-responsive_image` to list of third-party plugins ([#4286]({{ site.repository }}/issues/4286)) +- Add `jekyll-commonmark` to list of third-party plugins ([#4299]({{ site.repository }}/issues/4299)) +- Add documentation for incremental regeneration ([#4293]({{ site.repository }}/issues/4293)) +- Add note about removal of relative permalink support in upgrading docs ([#4303]({{ site.repository }}/issues/4303)) +- Add Pro Tip to use front matter variable to create clean URLs ([#4296]({{ site.repository }}/issues/4296)) +- Fix grammar in the documentation for posts. ([#4330]({{ site.repository }}/issues/4330)) +- Add documentation for smartify Liquid filter ([#4333]({{ site.repository }}/issues/4333)) +- Fixed broken link to blog on using mathjax with jekyll ([#4344]({{ site.repository }}/issues/4344)) +- Documentation: correct reference in Precedence section of Configuration docs ([#4355]({{ site.repository }}/issues/4355)) +- Add @jmcglone's guide to github-pages doc page ([#4364]({{ site.repository }}/issues/4364)) +- Added the Wordpress2Jekyll Wordpress plugin ([#4377]({{ site.repository }}/issues/4377)) +- Add Contentful Extension to list of third-party plugins ([#4390]({{ site.repository }}/issues/4390)) +- Correct Minor spelling error ([#4394]({{ site.repository }}/issues/4394)) + + +## 3.0.5 / 2016-04-26 +{: #v3-0-5} + +- Remove call to `#backwards_compatibilize` in `Configuration.from` ([#4846]({{ site.repository }}/issues/4846)) + + +## 3.0.4 / 2016-04-18 +{: #v3-0-4} + +- Fix defaults for Documents to lookup defaults based on `relative_path` instead of `url` ([#4806]({{ site.repository }}/issues/4806)) +- Configuration: allow users to specify a `collections.posts.permalink` directly without `permalink` clobbering it ([#4753]({{ site.repository }}/issues/4753)) + + +## 3.0.3 / 2016-02-08 +{: #v3-0-3} + +### Bug Fixes +{: #bug-fixes-v3-0-3} + +- Fix extension weirdness with folders ([#4493]({{ site.repository }}/issues/4493)) +- EntryFilter: only include 'excluded' log on excluded files ([#4479]({{ site.repository }}/issues/4479)) +- `Jekyll.sanitized_path`: escape tildes before sanitizing a questionable path ([#4468]({{ site.repository }}/issues/4468)) +- `LiquidRenderer#parse`: parse with line numbers ([#4453]({{ site.repository }}/issues/4453)) +- `Document#<=>`: protect against nil comparison in dates. ([#4446]({{ site.repository }}/issues/4446)) + + +## 3.0.2 / 2016-01-20 +{: #v3-0-2} + +### Bug Fixes +{: #bug-fixes-v3-0-2} + +- Document: throw a useful error when an invalid date is given ([#4378]({{ site.repository }}/issues/4378)) + + +## 3.0.1 / 2015-11-17 +{: #v3-0-1} + +### Bug Fixes +{: #bug-fixes-v3-0-1} + +- Document: only superdirectories of the collection are categories ([#4110]({{ site.repository }}/issues/4110)) +- `Convertible#render_liquid` should use `render!` to cause failure on bad Liquid ([#4077]({{ site.repository }}/issues/4077)) +- Don't generate `.jekyll-metadata` in non-incremental build ([#4079]({{ site.repository }}/issues/4079)) +- Set `highlighter` config val to `kramdown.syntax_highlighter` ([#4090]({{ site.repository }}/issues/4090)) +- Align hooks implementation with documentation ([#4104]({{ site.repository }}/issues/4104)) +- Fix the deprecation warning in the doctor command ([#4114]({{ site.repository }}/issues/4114)) +- Fix case in `:title` and add `:slug` which is downcased ([#4100]({{ site.repository }}/issues/4100)) + +### Development Fixes +{: #development-fixes-v3-0-1} + +- Fix test warnings when doing rake {test,spec} or script/test ([#4078]({{ site.repository }}/issues/4078)) + +### Site Enhancements +{: #site-enhancements-v3-0-1} + +- Update normalize.css to v3.0.3. ([#4085]({{ site.repository }}/issues/4085)) +- Update Font Awesome to v4.4.0. ([#4086]({{ site.repository }}/issues/4086)) +- Adds a note about installing the jekyll-gist gem to make gist tag work ([#4101]({{ site.repository }}/issues/4101)) +- Align hooks documentation with implementation ([#4104]({{ site.repository }}/issues/4104)) +- Add Jekyll Flickr Plugin to the list of third party plugins ([#4111]({{ site.repository }}/issues/4111)) +- Remove link to now-deleted blog post ([#4125]({{ site.repository }}/issues/4125)) +- Update the liquid syntax in the pagination docs ([#4130]({{ site.repository }}/issues/4130)) +- Add jekyll-language-plugin to plugins.md ([#4134]({{ site.repository }}/issues/4134)) +- Updated to reflect feedback in [#4129]({{ site.repository }}/issues/4129) ([#4137]({{ site.repository }}/issues/4137)) +- Clarify assets.md based on feedback of [#4129]({{ site.repository }}/issues/4129) ([#4142]({{ site.repository }}/issues/4142)) +- Re-correct the liquid syntax in the pagination docs ([#4140]({{ site.repository }}/issues/4140)) + + +## 3.0.0 / 2015-10-26 +{: #v3-0-0} + +### Major Enhancements +{: #major-enhancements-v3-0-0} + +- Liquid profiler (i.e. know how fast or slow your templates render) ([#3762]({{ site.repository }}/issues/3762)) +- Incremental regeneration ([#3116]({{ site.repository }}/issues/3116)) +- Add Hooks: a new kind of plugin ([#3553]({{ site.repository }}/issues/3553)) +- Upgrade to Liquid 3.0.0 ([#3002]({{ site.repository }}/issues/3002)) +- `site.posts` is now a Collection instead of an Array ([#4055]({{ site.repository }}/issues/4055)) +- Add basic support for JRuby (commit: 0f4477) +- Drop support for Ruby 1.9.3. ([#3235]({{ site.repository }}/issues/3235)) +- Support Ruby v2.2 ([#3234]({{ site.repository }}/issues/3234)) +- Support RDiscount 2 ([#2767]({{ site.repository }}/issues/2767)) +- Remove most runtime deps ([#3323]({{ site.repository }}/issues/3323)) +- Move to Rouge as default highlighter ([#3323]({{ site.repository }}/issues/3323)) +- Mimic GitHub Pages `.html` extension stripping behavior in WEBrick ([#3452]({{ site.repository }}/issues/3452)) +- Always include file extension on output files ([#3490]({{ site.repository }}/issues/3490)) +- Improved permalinks for pages and collections ([#3538]({{ site.repository }}/issues/3538)) +- Sunset (i.e. remove) Maruku ([#3655]({{ site.repository }}/issues/3655)) +- Remove support for relative permalinks ([#3679]({{ site.repository }}/issues/3679)) +- Iterate over `site.collections` as an array instead of a hash. ([#3670]({{ site.repository }}/issues/3670)) +- Adapt StaticFile for collections, config defaults ([#3823]({{ site.repository }}/issues/3823)) +- Add a Code of Conduct for the Jekyll project ([#3925]({{ site.repository }}/issues/3925)) +- Added permalink time variables ([#3990]({{ site.repository }}/issues/3990)) +- Add `--incremental` flag to enable incremental regen (disabled by default) ([#4059]({{ site.repository }}/issues/4059)) + +### Minor Enhancements +{: #minor-enhancements-v3-0-0} + +- Deprecate access to Document#data properties and Collection#docs methods ([#4058]({{ site.repository }}/issues/4058)) +- Sort static files just once, and call `site_payload` once for all collections ([#3204]({{ site.repository }}/issues/3204)) +- Separate `jekyll docs` and optimize external gem handling ([#3241]({{ site.repository }}/issues/3241)) +- Improve `Site#getConverterImpl` and call it `Site#find_converter_instance` ([#3240]({{ site.repository }}/issues/3240)) +- Use relative path for `path` Liquid variable in Documents for consistency ([#2908]({{ site.repository }}/issues/2908)) +- Generalize `Utils#slugify` for any scripts ([#3047]({{ site.repository }}/issues/3047)) +- Added basic microdata to post template in site template ([#3189]({{ site.repository }}/issues/3189)) +- Store log messages in an array of messages. ([#3244]({{ site.repository }}/issues/3244)) +- Allow collection documents to override `output` property in front matter ([#3172]({{ site.repository }}/issues/3172)) +- Keep file modification times between builds for static files ([#3220]({{ site.repository }}/issues/3220)) +- Only downcase mixed-case categories for the URL ([#2571]({{ site.repository }}/issues/2571)) +- Added per post `excerpt_separator` functionality ([#3274]({{ site.repository }}/issues/3274)) +- Allow collections YAML to end with three dots ([#3134]({{ site.repository }}/issues/3134)) +- Add mode parameter to `slugify` Liquid filter ([#2918]({{ site.repository }}/issues/2918)) +- Perf: `Markdown#matches` should avoid regexp ([#3321]({{ site.repository }}/issues/3321)) +- Perf: Use frozen regular expressions for `Utils#slugify` ([#3321]({{ site.repository }}/issues/3321)) +- Split off Textile support into jekyll-textile-converter ([#3319]({{ site.repository }}/issues/3319)) +- Improve the navigation menu alignment in the site template on small screens ([#3331]({{ site.repository }}/issues/3331)) +- Show the regeneration time after the initial generation ([#3378]({{ site.repository }}/issues/3378)) +- Site template: Switch default font to Helvetica Neue ([#3376]({{ site.repository }}/issues/3376)) +- Make the `include` tag a teensy bit faster. ([#3391]({{ site.repository }}/issues/3391)) +- Add `pkill -f jekyll` to ways to kill. ([#3397]({{ site.repository }}/issues/3397)) +- Site template: collapsed, variable-driven font declaration ([#3360]({{ site.repository }}/issues/3360)) +- Site template: Don't always show the scrollbar in code blocks ([#3419]({{ site.repository }}/issues/3419)) +- Site template: Remove undefined `text` class from `p` element ([#3440]({{ site.repository }}/issues/3440)) +- Site template: Optimize text rendering for legibility ([#3382]({{ site.repository }}/issues/3382)) +- Add `draft?` method to identify if Post is a Draft & expose to Liquid ([#3456]({{ site.repository }}/issues/3456)) +- Write regeneration metadata even on full rebuild ([#3464]({{ site.repository }}/issues/3464)) +- Perf: Use `String#end_with?("/")` instead of regexp when checking paths ([#3516]({{ site.repository }}/issues/3516)) +- Docs: document 'ordinal' built-in permalink style ([#3532]({{ site.repository }}/issues/3532)) +- Upgrade liquid-c to 3.x ([#3531]({{ site.repository }}/issues/3531)) +- Use consistent syntax for deprecation warning ([#3535]({{ site.repository }}/issues/3535)) +- Added build --destination and --source flags ([#3418]({{ site.repository }}/issues/3418)) +- Site template: remove unused `page.meta` attribute ([#3537]({{ site.repository }}/issues/3537)) +- Improve the error message when sorting null objects ([#3520]({{ site.repository }}/issues/3520)) +- Added liquid-md5 plugin ([#3598]({{ site.repository }}/issues/3598)) +- Documentation: RR replaced with RSpec Mocks ([#3600]({{ site.repository }}/issues/3600)) +- Documentation: Fix subpath. ([#3599]({{ site.repository }}/issues/3599)) +- Create 'tmp' dir for test_tags if it doesn't exist ([#3609]({{ site.repository }}/issues/3609)) +- Extract reading of data from `Site` to reduce responsibilities. ([#3545]({{ site.repository }}/issues/3545)) +- Removed the word 'Jekyll' a few times from the comments ([#3617]({{ site.repository }}/issues/3617)) +- `bin/jekyll`: with no args, exit with exit code 1 ([#3619]({{ site.repository }}/issues/3619)) +- Incremental build if destination file missing ([#3614]({{ site.repository }}/issues/3614)) +- Static files `mtime` liquid should return a `Time` obj ([#3596]({{ site.repository }}/issues/3596)) +- Use `Jekyll::Post`s for both LSI indexing and lookup. ([#3629]({{ site.repository }}/issues/3629)) +- Add `charset=utf-8` for HTML and XML pages in WEBrick ([#3649]({{ site.repository }}/issues/3649)) +- Set log level to debug when verbose flag is set ([#3665]({{ site.repository }}/issues/3665)) +- Added a mention on the Gemfile to complete the instructions ([#3671]({{ site.repository }}/issues/3671)) +- Perf: Cache `Document#to_liquid` and invalidate where necessary ([#3693]({{ site.repository }}/issues/3693)) +- Perf: `Jekyll::Cleaner#existing_files`: Call `keep_file_regex` and `keep_dirs` only once, not once per iteration ([#3696]({{ site.repository }}/issues/3696)) +- Omit jekyll/jekyll-help from list of resources. ([#3698]({{ site.repository }}/issues/3698)) +- Add basic `jekyll doctor` test to detect fsnotify (OSX) anomalies. ([#3704]({{ site.repository }}/issues/3704)) +- Added talk.jekyllrb.com to "Have questions?" ([#3694]({{ site.repository }}/issues/3694)) +- Performance: Sort files only once ([#3707]({{ site.repository }}/issues/3707)) +- Performance: Marshal metadata ([#3706]({{ site.repository }}/issues/3706)) +- Upgrade highlight wrapper from `div` to `figure` ([#3779]({{ site.repository }}/issues/3779)) +- Upgrade mime-types to `~> 2.6` ([#3795]({{ site.repository }}/issues/3795)) +- Update windows.md with Ruby version info ([#3818]({{ site.repository }}/issues/3818)) +- Make the directory for includes configurable ([#3782]({{ site.repository }}/issues/3782)) +- Rename directory configurations to match `*_dir` convention for consistency ([#3782]({{ site.repository }}/issues/3782)) +- Internal: trigger hooks by owner symbol ([#3871]({{ site.repository }}/issues/3871)) +- Update MIME types from mime-db ([#3933]({{ site.repository }}/issues/3933)) +- Add header to site template `_config.yml` for clarity & direction ([#3997]({{ site.repository }}/issues/3997)) +- Site template: add timezone offset to post date front matter ([#4001]({{ site.repository }}/issues/4001)) +- Make a constant for the regex to find hidden files ([#4032]({{ site.repository }}/issues/4032)) +- Site template: refactor github & twitter icons into includes ([#4049]({{ site.repository }}/issues/4049)) +- Site template: add background to Kramdown Rouge-ified backtick code blocks ([#4053]({{ site.repository }}/issues/4053)) + +### Bug Fixes +{: #bug-fixes-v3-0-0} + +- `post_url`: fix access deprecation warning & fix deprecation msg ([#4060]({{ site.repository }}/issues/4060)) +- Perform jekyll-paginate deprecation warning correctly. ([#3580]({{ site.repository }}/issues/3580)) +- Make permalink parsing consistent with pages ([#3014]({{ site.repository }}/issues/3014)) +- `time()`pre-filter method should accept a `Date` object ([#3299]({{ site.repository }}/issues/3299)) +- Remove unneeded end tag for `link` in site template ([#3236]({{ site.repository }}/issues/3236)) +- Kramdown: Use `enable_coderay` key instead of `use_coderay` ([#3237]({{ site.repository }}/issues/3237)) +- Unescape `Document` output path ([#2924]({{ site.repository }}/issues/2924)) +- Fix nav items alignment when on multiple rows ([#3264]({{ site.repository }}/issues/3264)) +- Highlight: Only Strip Newlines/Carriage Returns, not Spaces ([#3278]({{ site.repository }}/issues/3278)) +- Find variables in front matter defaults by searching with relative file path. ([#2774]({{ site.repository }}/issues/2774)) +- Allow variables (e.g `:categories`) in front matter permalinks ([#3320]({{ site.repository }}/issues/3320)) +- Handle nil URL placeholders in permalinks ([#3325]({{ site.repository }}/issues/3325)) +- Template: Fix nav items alignment when in "burger" mode ([#3329]({{ site.repository }}/issues/3329)) +- Template: Remove `!important` from nav SCSS introduced in [#3329]({{ site.repository }}/issues/3329) ([#3375]({{ site.repository }}/issues/3375)) +- The `:title` URL placeholder for collections should be the filename slug. ([#3383]({{ site.repository }}/issues/3383)) +- Trim the generate time diff to just 3 places past the decimal place ([#3415]({{ site.repository }}/issues/3415)) +- The highlight tag should only clip the newlines before and after the *entire* block, not in between ([#3401]({{ site.repository }}/issues/3401)) +- highlight: fix problem with linenos and rouge. ([#3436]({{ site.repository }}/issues/3436)) +- `Site#read_data_file`: read CSV's with proper file encoding ([#3455]({{ site.repository }}/issues/3455)) +- Ignore `.jekyll-metadata` in site template ([#3496]({{ site.repository }}/issues/3496)) +- Template: Point documentation link to the documentation pages ([#3502]({{ site.repository }}/issues/3502)) +- Removed the trailing slash from the example `/blog` baseurl comment ([#3485]({{ site.repository }}/issues/3485)) +- Clear the regenerator cache every time we process ([#3592]({{ site.repository }}/issues/3592)) +- Readd (bring back) minitest-profile ([#3628]({{ site.repository }}/issues/3628)) +- Add WOFF2 font MIME type to Jekyll server MIME types ([#3647]({{ site.repository }}/issues/3647)) +- Be smarter about extracting the extname in `StaticFile` ([#3632]({{ site.repository }}/issues/3632)) +- Process metadata for all dependencies ([#3608]({{ site.repository }}/issues/3608)) +- Show error message if the front matter on a page/post is invalid. ([#3643]({{ site.repository }}/issues/3643)) +- Upgrade redcarpet to 3.2 (Security fix: OSVDB-120415) ([#3652]({{ site.repository }}/issues/3652)) +- Create #mock_expects that goes directly to RSpec Mocks. ([#3658]({{ site.repository }}/issues/3658)) +- Open `.jekyll-metadata` in binary mode to read binary Marshal data ([#3713]({{ site.repository }}/issues/3713)) +- Incremental regeneration: handle deleted, renamed, and moved dependencies ([#3717]({{ site.repository }}/issues/3717)) +- Fix typo on line 19 of pagination.md ([#3760]({{ site.repository }}/issues/3760)) +- Fix it so that 'blog.html' matches 'blog.html' ([#3732]({{ site.repository }}/issues/3732)) +- Remove occasionally-problematic `ensure` in `LiquidRenderer` ([#3811]({{ site.repository }}/issues/3811)) +- Fixed an unclear code comment in site template SCSS ([#3837]({{ site.repository }}/issues/3837)) +- Fix reading of binary metadata file ([#3845]({{ site.repository }}/issues/3845)) +- Remove var collision with site template header menu iteration variable ([#3838]({{ site.repository }}/issues/3838)) +- Change non-existent `hl_linenos` to `hl_lines` to allow passthrough in safe mode ([#3787]({{ site.repository }}/issues/3787)) +- Add missing flag to disable the watcher ([#3820]({{ site.repository }}/issues/3820)) +- Update CI guide to include more direct explanations of the flow ([#3891]({{ site.repository }}/issues/3891)) +- Set `future` to `false` in the default config ([#3892]({{ site.repository }}/issues/3892)) +- filters: `where` should compare stringified versions of input & comparator ([#3935]({{ site.repository }}/issues/3935)) +- Read build options for `jekyll clean` command ([#3828]({{ site.repository }}/issues/3828)) +- Fix [#3970]({{ site.repository }}/issues/3970): Use Gem::Version to compare versions, not `>`. +- Abort if no subcommand. Fixes confusing message. ([#3992]({{ site.repository }}/issues/3992)) +- Whole-post excerpts should match the post content ([#4004]({{ site.repository }}/issues/4004)) +- Change default font weight to 400 to fix bold/strong text issues ([#4050]({{ site.repository }}/issues/4050)) +- Document: Only auto-generate the excerpt if it's not overridden ([#4062]({{ site.repository }}/issues/4062)) +- Utils: `deep_merge_hashes` should also merge `default_proc` (45f69bb) +- Defaults: compare paths in `applies_path?` as `String`s to avoid confusion (7b81f00) + +### Development Fixes +{: #development-fixes-v3-0-0} + +- Remove loader.rb and "modernize" `script/test`. ([#3574]({{ site.repository }}/issues/3574)) +- Improve the grammar in the documentation ([#3233]({{ site.repository }}/issues/3233)) +- Update the LICENSE text to match the MIT license exactly ([#3253]({{ site.repository }}/issues/3253)) +- Update rake task `site:publish` to fix minor bugs. ([#3254]({{ site.repository }}/issues/3254)) +- Switch to shields.io for the README badges. ([#3255]({{ site.repository }}/issues/3255)) +- Use `FileList` instead of `Dir.glob` in `site:publish` rake task ([#3261]({{ site.repository }}/issues/3261)) +- Fix test script to be platform-independent ([#3279]({{ site.repository }}/issues/3279)) +- Instead of symlinking `/tmp`, create and symlink a local `tmp` in the tests ([#3258]({{ site.repository }}/issues/3258)) +- Fix some spacing ([#3312]({{ site.repository }}/issues/3312)) +- Fix comment typo in `lib/jekyll/frontmatter_defaults.rb` ([#3322]({{ site.repository }}/issues/3322)) +- Move all `regenerate?` checking to `Regenerator` ([#3326]({{ site.repository }}/issues/3326)) +- Factor out a `read_data_file` call to keep things clean ([#3380]({{ site.repository }}/issues/3380)) +- Proof the site with CircleCI. ([#3427]({{ site.repository }}/issues/3427)) +- Update LICENSE to 2015. ([#3477]({{ site.repository }}/issues/3477)) +- Upgrade tests to use Minitest ([#3492]({{ site.repository }}/issues/3492)) +- Remove trailing whitespace ([#3497]({{ site.repository }}/issues/3497)) +- Use `fixture_site` for Document tests ([#3511]({{ site.repository }}/issues/3511)) +- Remove adapters deprecation warning ([#3529]({{ site.repository }}/issues/3529)) +- Minor fixes to `url.rb` to follow GitHub style guide ([#3544]({{ site.repository }}/issues/3544)) +- Minor changes to resolve deprecation warnings ([#3547]({{ site.repository }}/issues/3547)) +- Convert remaining textile test documents to markdown ([#3528]({{ site.repository }}/issues/3528)) +- Migrate the tests to use rspec-mocks ([#3552]({{ site.repository }}/issues/3552)) +- Remove `activesupport` ([#3612]({{ site.repository }}/issues/3612)) +- Added tests for `Jekyll:StaticFile` ([#3633]({{ site.repository }}/issues/3633)) +- Force minitest version to 5.5.1 ([#3657]({{ site.repository }}/issues/3657)) +- Update the way cucumber accesses Minitest assertions ([#3678]({{ site.repository }}/issues/3678)) +- Add `script/rubyprof` to generate cachegrind callgraphs ([#3692]({{ site.repository }}/issues/3692)) +- Upgrade cucumber to 2.x ([#3795]({{ site.repository }}/issues/3795)) +- Update Kramdown. ([#3853]({{ site.repository }}/issues/3853)) +- Updated the scripts shebang for portability ([#3858]({{ site.repository }}/issues/3858)) +- Update JRuby testing to 9K ([3ab386f](https://github.com/jekyll/jekyll/commit/3ab386f1b096be25a24fe038fc70fd0fb08d545d)) +- Organize dependencies into dev and test groups. ([#3852]({{ site.repository }}/issues/3852)) +- Contributing.md should refer to `script/cucumber` ([#3894]({{ site.repository }}/issues/3894)) +- Update contributing documentation to reflect workflow updates ([#3895]({{ site.repository }}/issues/3895)) +- Add script to vendor mime types ([#3933]({{ site.repository }}/issues/3933)) +- Ignore .bundle dir in SimpleCov ([#4033]({{ site.repository }}/issues/4033)) + +### Site Enhancements +{: #site-enhancements-v3-0-0} + +- Add 'info' labels to certain notes in collections docs ([#3601]({{ site.repository }}/issues/3601)) +- Remove extra spaces, make the last sentence less awkward in permalink docs ([#3603]({{ site.repository }}/issues/3603)) +- Update the permalinks documentation to reflect the updates for 3.0 ([#3556]({{ site.repository }}/issues/3556)) +- Add blog post announcing Jekyll Help ([#3523]({{ site.repository }}/issues/3523)) +- Add Jekyll Talk to Help page on site ([#3518]({{ site.repository }}/issues/3518)) +- Change Ajax pagination resource link to use HTTPS ([#3570]({{ site.repository }}/issues/3570)) +- Fixing the default host on docs ([#3229]({{ site.repository }}/issues/3229)) +- Add `jekyll-thumbnail-filter` to list of third-party plugins ([#2790]({{ site.repository }}/issues/2790)) +- Add link to 'Adding Ajax pagination to Jekyll' to Resources page ([#3186]({{ site.repository }}/issues/3186)) +- Add a Resources link to tutorial on building dynamic navbars ([#3185]({{ site.repository }}/issues/3185)) +- Semantic structure improvements to the post and page layouts ([#3251]({{ site.repository }}/issues/3251)) +- Add new AsciiDoc plugin to list of third-party plugins. ([#3277]({{ site.repository }}/issues/3277)) +- Specify that all transformable collection documents must contain front matter ([#3271]({{ site.repository }}/issues/3271)) +- Assorted accessibility fixes ([#3256]({{ site.repository }}/issues/3256)) +- Update configuration docs to mention `keep_files` for `destination` ([#3288]({{ site.repository }}/issues/3288), [#3296]({{ site.repository }}/issues/3296)) +- Break when we successfully generate nav link to save CPU cycles. ([#3291]({{ site.repository }}/issues/3291)) +- Update usage docs to mention `keep_files` and a warning about `destination` cleaning ([#3295]({{ site.repository }}/issues/3295)) +- Add logic to automatically generate the `next_section` and `prev_section` navigation items ([#3292]({{ site.repository }}/issues/3292)) +- Some small fixes for the Plugins TOC. ([#3306]({{ site.repository }}/issues/3306)) +- Added versioning comment to configuration file ([#3314]({{ site.repository }}/issues/3314)) +- Add `jekyll-minifier` to list of third-party plugins ([#3333]({{ site.repository }}/issues/3333)) +- Add blog post about the Jekyll meet-up ([#3332]({{ site.repository }}/issues/3332)) +- Use `highlight` Liquid tag instead of the four-space tabs for code ([#3336]({{ site.repository }}/issues/3336)) +- 3.0.0.beta1 release post ([#3346]({{ site.repository }}/issues/3346)) +- Add `twa` to the list of third-party plugins ([#3384]({{ site.repository }}/issues/3384)) +- Remove extra spaces ([#3388]({{ site.repository }}/issues/3388)) +- Fix small grammar errors on a couple pages ([#3396]({{ site.repository }}/issues/3396)) +- Fix typo on Templates docs page ([#3420]({{ site.repository }}/issues/3420)) +- s/three/four for plugin type list ([#3424]({{ site.repository }}/issues/3424)) +- Release jekyllrb.com as a locally-compiled site. ([#3426]({{ site.repository }}/issues/3426)) +- Add a jekyllrb.com/help page which elucidates places from which to get help ([#3428]({{ site.repository }}/issues/3428)) +- Remove extraneous dash on Plugins doc page which caused a formatting error ([#3431]({{ site.repository }}/issues/3431)) +- Fix broken link to Jordan Thornquest's website. ([#3438]({{ site.repository }}/issues/3438)) +- Change the link to an extension ([#3457]({{ site.repository }}/issues/3457)) +- Fix Twitter link on the help page ([#3466]({{ site.repository }}/issues/3466)) +- Fix wording in code snippet highlighting section ([#3475]({{ site.repository }}/issues/3475)) +- Add a `/` to `paginate_path` in the Pagination documentation ([#3479]({{ site.repository }}/issues/3479)) +- Add a link on all the docs pages to "Improve this page". ([#3510]({{ site.repository }}/issues/3510)) +- Add jekyll-auto-image generator to the list of third-party plugins ([#3489]({{ site.repository }}/issues/3489)) +- Replace link to the proposed `picture` element spec ([#3530]({{ site.repository }}/issues/3530)) +- Add front matter date formatting information ([#3469]({{ site.repository }}/issues/3469)) +- Improve consistency and clarity of plugins options note ([#3546]({{ site.repository }}/issues/3546)) +- Add permalink warning to pagination docs ([#3551]({{ site.repository }}/issues/3551)) +- Fix grammar in Collections docs API stability warning ([#3560]({{ site.repository }}/issues/3560)) +- Restructure `excerpt_separator` documentation for clarity ([#3550]({{ site.repository }}/issues/3550)) +- Fix accidental line break in collections docs ([#3585]({{ site.repository }}/issues/3585)) +- Add information about the `.jekyll-metadata` file ([#3597]({{ site.repository }}/issues/3597)) +- Document addition of variable parameters to an include ([#3581]({{ site.repository }}/issues/3581)) +- Add `jekyll-files` to the list of third-party plugins. ([#3586]({{ site.repository }}/issues/3586)) +- Define the `install` step in the CI example `.travis.yml` ([#3622]({{ site.repository }}/issues/3622)) +- Expand collections documentation. ([#3638]({{ site.repository }}/issues/3638)) +- Add the "warning" note label to excluding `vendor` in the CI docs page ([#3623]({{ site.repository }}/issues/3623)) +- Upgrade pieces of the Upgrading guide for Jekyll 3 ([#3607]({{ site.repository }}/issues/3607)) +- Showing how to access specific data items ([#3468]({{ site.repository }}/issues/3468)) +- Clarify pagination works from within HTML files ([#3467]({{ site.repository }}/issues/3467)) +- Add note to `excerpt_separator` documentation that it can be set globally ([#3667]({{ site.repository }}/issues/3667)) +- Fix some names on Troubleshooting page ([#3683]({{ site.repository }}/issues/3683)) +- Add `remote_file_content` tag plugin to list of third-party plugins ([#3691]({{ site.repository }}/issues/3691)) +- Update the Redcarpet version on the Configuration page. ([#3743]({{ site.repository }}/issues/3743)) +- Update the link in the welcome post to point to Jekyll Talk ([#3745]({{ site.repository }}/issues/3745)) +- Update link for navbars with data attributes tutorial ([#3728]({{ site.repository }}/issues/3728)) +- Add `jekyll-asciinema` to list of third-party plugins ([#3750]({{ site.repository }}/issues/3750)) +- Update pagination example to be agnostic to first pagination dir ([#3763]({{ site.repository }}/issues/3763)) +- Detailed instructions for rsync deployment method ([#3848]({{ site.repository }}/issues/3848)) +- Add Jekyll Portfolio Generator to list of plugins ([#3883]({{ site.repository }}/issues/3883)) +- Add `site.html_files` to variables docs ([#3880]({{ site.repository }}/issues/3880)) +- Add Static Publisher tool to list of deployment methods ([#3865]({{ site.repository }}/issues/3865)) +- Fix a few typos. ([#3897]({{ site.repository }}/issues/3897)) +- Add `jekyll-youtube` to the list of third-party plugins ([#3931]({{ site.repository }}/issues/3931)) +- Add Views Router plugin ([#3950]({{ site.repository }}/issues/3950)) +- Update install docs (Core dependencies, Windows reqs, etc) ([#3769]({{ site.repository }}/issues/3769)) +- Use Jekyll Feed for jekyllrb.com ([#3736]({{ site.repository }}/issues/3736)) +- Add jekyll-umlauts to plugins.md ($3966) +- Troubleshooting: fix broken link, add other mac-specific info ([#3968]({{ site.repository }}/issues/3968)) +- Add a new site for learning purposes ([#3917]({{ site.repository }}/issues/3917)) +- Added documentation for Jekyll environment variables ([#3989]({{ site.repository }}/issues/3989)) +- Fix broken configuration documentation page ([#3994]({{ site.repository }}/issues/3994)) +- Add troubleshooting docs for installing on El Capitan ([#3999]({{ site.repository }}/issues/3999)) +- Add Lazy Tweet Embedding to the list of third-party plugins ([#4015]({{ site.repository }}/issues/4015)) +- Add installation instructions for 2 of 3 options for plugins ([#4013]({{ site.repository }}/issues/4013)) +- Add alternative jekyll gem installation instructions ([#4018]({{ site.repository }}/issues/4018)) +- Fix a few typos and formatting problems. ([#4022]({{ site.repository }}/issues/4022)) +- Fix pretty permalink example ([#4029]({{ site.repository }}/issues/4029)) +- Note that `_config.yml` is not reloaded during regeneration ([#4034]({{ site.repository }}/issues/4034)) +- Apply code block figure syntax to blocks in CONTRIBUTING ([#4046]({{ site.repository }}/issues/4046)) +- Add jekyll-smartify to the list of third-party plugins ([#3572]({{ site.repository }}/issues/3572)) + + +## 2.5.3 / 2014-12-22 +{: #v2-5-3} + +### Bug Fixes +{: #bug-fixes-v2-5-3} + +- When checking a Markdown extname, include position of the `.` ([#3147]({{ site.repository }}/issues/3147)) +- Fix `jsonify` Liquid filter handling of boolean values ([#3154]({{ site.repository }}/issues/3154)) +- Add comma to value of `viewport` meta tag ([#3170]({{ site.repository }}/issues/3170)) +- Set the link type for the RSS feed to `application/rss+xml` ([#3176]({{ site.repository }}/issues/3176)) +- Refactor `#as_liquid` ([#3158]({{ site.repository }}/issues/3158)) + +### Development Fixes +{: #development-fixes-v2-5-3} + +- Exclude built-in bundles from being added to coverage report ([#3180]({{ site.repository }}/issues/3180)) + +### Site Enhancements +{: #site-enhancements-v2-5-3} + +- Add @alfredxing to the @jekyll/core team. :tada: ([#3218]({{ site.repository }}/issues/3218)) +- Document the `-q` option for the `build` and `serve` commands ([#3149]({{ site.repository }}/issues/3149)) +- Fix some minor typos/flow fixes in documentation website content ([#3165]({{ site.repository }}/issues/3165)) +- Add `keep_files` to configuration documentation ([#3162]({{ site.repository }}/issues/3162)) +- Repeat warning about cleaning of the `destination` directory ([#3161]({{ site.repository }}/issues/3161)) +- Add jekyll-500px-embed to list of third-party plugins ([#3163]({{ site.repository }}/issues/3163)) +- Simplified platform detection in Gemfile example for Windows ([#3177]({{ site.repository }}/issues/3177)) +- Add the `jekyll-jalali` plugin added to the list of third-party plugins. ([#3198]({{ site.repository }}/issues/3198)) +- Add Table of Contents to Troubleshooting page ([#3196]({{ site.repository }}/issues/3196)) +- Add `inline_highlight` plugin to list of third-party plugins ([#3212]({{ site.repository }}/issues/3212)) +- Add `jekyll-mermaid` plugin to list of third-party plugins ([#3222]({{ site.repository }}/issues/3222)) + + +## 2.5.2 / 2014-11-17 +{: #v2-5-2} + +### Minor Enhancements +{: #minor-enhancements-v2-5-2} + +- `post_url` should match `post.name` instead of slugs and dates ([#3058]({{ site.repository }}/issues/3058)) + +### Bug Fixes +{: #bug-fixes-v2-5-2} + +- Fix bundle require for `:jekyll_plugins` ([#3119]({{ site.repository }}/issues/3119)) +- Remove duplicate regexp phrase: `^\A` ([#3089]({{ site.repository }}/issues/3089)) +- Remove duplicate `Conversion error:` message in `Convertible` ([#3088]({{ site.repository }}/issues/3088)) +- Print full conversion error message in `Renderer#convert` ([#3090]({{ site.repository }}/issues/3090)) + +### Site Enhancements +{: #site-enhancements-v2-5-2} + +- Change variable names in Google Analytics script ([#3093]({{ site.repository }}/issues/3093)) +- Mention CSV files in the docs for data files ([#3101]({{ site.repository }}/issues/3101)) +- Add trailing slash to `paginate_path` example. ([#3091]({{ site.repository }}/issues/3091)) +- Get rid of noifniof (`excerpt_separator`) ([#3094]({{ site.repository }}/issues/3094)) +- Sass improvements, around nesting mostly. ([#3123]({{ site.repository }}/issues/3123)) +- Add webmentions.io plugin to the list of third-party plugins ([#3127]({{ site.repository }}/issues/3127)) +- Add Sass mixins and use them. ([#2904]({{ site.repository }}/issues/2904)) +- Slightly compress jekyll-sticker.jpg. ([#3133]({{ site.repository }}/issues/3133)) +- Update gridism and separate out related but custom styles. ([#3132]({{ site.repository }}/issues/3132)) +- Add remote-include plugin to list of third-party plugins ([#3136]({{ site.repository }}/issues/3136)) + + +## 2.5.1 / 2014-11-09 +{: #v2-5-1} + +### Bug Fixes +{: #bug-fixes-v2-5-1} + +- Fix path sanitation bug related to Windows drive names ([#3077]({{ site.repository }}/issues/3077)) + +### Development Fixes +{: #development-fixes-v2-5-1} + +- Add development time dependencies on minitest and test-unit to gemspec for cygwin ([#3064]({{ site.repository }}/issues/3064)) +- Use Travis's built-in caching. ([#3075]({{ site.repository }}/issues/3075)) + + +## 2.5.0 / 2014-11-06 +{: #v2-5-0} + +### Minor Enhancements +{: #minor-enhancements-v2-5-0} + +- Require gems in `:jekyll_plugins` Gemfile group unless `JEKYLL_NO_BUNDLER_REQUIRE` is specified in the environment. ([#2865]({{ site.repository }}/issues/2865)) +- Centralize path sanitation in the `Site` object ([#2882]({{ site.repository }}/issues/2882)) +- Allow placeholders in permalinks ([#3031]({{ site.repository }}/issues/3031)) +- Allow users to specify the log level via `JEKYLL_LOG_LEVEL`. ([#3067]({{ site.repository }}/issues/3067)) +- Fancy Indexing with WEBrick ([#3018]({{ site.repository }}/issues/3018)) +- Allow Enumerables to be used with `where` filter. ([#2986]({{ site.repository }}/issues/2986)) +- Meta descriptions in the site template now use `page.excerpt` if it's available ([#2964]({{ site.repository }}/issues/2964)) +- Change indentation in `head.html` of site template to 2 spaces from 4 ([#2973]({{ site.repository }}/issues/2973)) +- Use a `$content-width` variable instead of a fixed value in the site template CSS ([#2972]({{ site.repository }}/issues/2972)) +- Strip newlines in site template `<meta>` description. ([#2982]({{ site.repository }}/issues/2982)) +- Add link to atom feed in `head` of site template files ([#2996]({{ site.repository }}/issues/2996)) +- Performance optimizations ([#2994]({{ site.repository }}/issues/2994)) +- Use `Hash#each_key` instead of `Hash#keys.each` to speed up iteration over hash keys. ([#3017]({{ site.repository }}/issues/3017)) +- Further minor performance enhancements. ([#3022]({{ site.repository }}/issues/3022)) +- Add 'b' and 's' aliases for build and serve, respectively ([#3065]({{ site.repository }}/issues/3065)) + +### Bug Fixes +{: #bug-fixes-v2-5-0} + +- Fix Rouge's RedCarpet plugin interface integration ([#2951]({{ site.repository }}/issues/2951)) +- Remove `--watch` from the site template blog post since it defaults to watching in in 2.4.0 ([#2922]({{ site.repository }}/issues/2922)) +- Fix code for media query mixin in site template ([#2946]({{ site.repository }}/issues/2946)) +- Allow post URLs to have `.htm` extensions ([#2925]({{ site.repository }}/issues/2925)) +- `Utils.slugify`: Don't create new objects when gsubbing ([#2997]({{ site.repository }}/issues/2997)) +- The jsonify filter should deep-convert to Liquid when given an Array. ([#3032]({{ site.repository }}/issues/3032)) +- Apply `jsonify` filter to Hashes deeply and effectively ([#3063]({{ site.repository }}/issues/3063)) +- Use `127.0.0.1` as default host instead of `0.0.0.0` ([#3053]({{ site.repository }}/issues/3053)) +- In the case that a Gemfile does not exist, ensure Jekyll doesn't fail on requiring the Gemfile group ([#3066]({{ site.repository }}/issues/3066)) + +### Development Fixes +{: #development-fixes-v2-5-0} + +- Fix a typo in the doc block for `Jekyll::URL.escape_path` ([#3052]({{ site.repository }}/issues/3052)) +- Add integration test for `jekyll new --blank` in TestUnit ([#2913]({{ site.repository }}/issues/2913)) +- Add unit test for `jekyll new --force` logic ([#2929]({{ site.repository }}/issues/2929)) +- Update outdated comment for `Convertible#transform` ([#2957]({{ site.repository }}/issues/2957)) +- Add Hakiri badge to README. ([#2953]({{ site.repository }}/issues/2953)) +- Add some simple benchmarking tools. ([#2993]({{ site.repository }}/issues/2993)) + +### Site Enhancements +{: #site-enhancements-v2-5-0} + +- `NOKOGIRI_USE_SYSTEM_LIBRARIES=true` **decreases** installation time. ([#3040]({{ site.repository }}/issues/3040)) +- Add FormKeep to resources as Jekyll form backend ([#3010]({{ site.repository }}/issues/3010)) +- Fixing a mistake in the name of the new Liquid tag ([#2969]({{ site.repository }}/issues/2969)) +- Update Font Awesome to v4.2.0. ([#2898]({{ site.repository }}/issues/2898)) +- Fix link to [#2895]({{ site.repository }}/issues/2895) in 2.4.0 release post. ([#2899]({{ site.repository }}/issues/2899)) +- Add Big Footnotes for Kramdown plugin to list of third-party plugins ([#2916]({{ site.repository }}/issues/2916)) +- Remove warning regarding GHP use of singular types for front matter defaults ([#2919]({{ site.repository }}/issues/2919)) +- Fix quote character typo in site documentation for templates ([#2917]({{ site.repository }}/issues/2917)) +- Point Liquid links to Liquid’s GitHub wiki ([#2887]({{ site.repository }}/issues/2887)) +- Add HTTP Basic Auth (.htaccess) plugin to list of third-party plugins ([#2931]({{ site.repository }}/issues/2931)) +- (Minor) Grammar & `_config.yml` filename fixes ([#2911]({{ site.repository }}/issues/2911)) +- Added `mathml.rb` to the list of third-party plugins. ([#2937]({{ site.repository }}/issues/2937)) +- Add `--force_polling` to the list of configuration options ([#2943]({{ site.repository }}/issues/2943)) +- Escape unicode characters in site CSS ([#2906]({{ site.repository }}/issues/2906)) +- Add note about using the github-pages gem via pages.github.com/versions.json ([#2939]({{ site.repository }}/issues/2939)) +- Update usage documentation to reflect 2.4 auto-enabling of `--watch`. ([#2954]({{ site.repository }}/issues/2954)) +- Add `--skip-initial-build` to configuration docs ([#2949]({{ site.repository }}/issues/2949)) +- Fix a minor typo in Templates docs page ([#2959]({{ site.repository }}/issues/2959)) +- Add a ditaa-ditaa plugin under Other section on the Plugins page ([#2967]({{ site.repository }}/issues/2967)) +- Add `build/serve -V` option to configuration documentation ([#2948]({{ site.repository }}/issues/2948)) +- Add 'Jekyll Twitter Plugin' to list of third-party plugins ([#2979]({{ site.repository }}/issues/2979)) +- Docs: Update normalize.css to v3.0.2. ([#2981]({{ site.repository }}/issues/2981)) +- Fix typo in Continuous Integration documentation ([#2984]({{ site.repository }}/issues/2984)) +- Clarify behavior of `:categories` in permalinks ([#3011]({{ site.repository }}/issues/3011)) + + +## 2.4.0 / 2014-09-09 +{: #v2-4-0} + +### Minor Enhancements +{: #minor-enhancements-v2-4-0} + +- Support a new `relative_include` tag ([#2870]({{ site.repository }}/issues/2870)) +- Auto-enable watch on 'serve' ([#2858]({{ site.repository }}/issues/2858)) +- Render Liquid in CoffeeScript files ([#2830]({{ site.repository }}/issues/2830)) +- Array Liquid filters: `push`, `pop`, `unshift`, `shift` ([#2895]({{ site.repository }}/issues/2895)) +- Add `:title` to collection URL template fillers ([#2864]({{ site.repository }}/issues/2864)) +- Add support for CSV files in the `_data` directory ([#2761]({{ site.repository }}/issues/2761)) +- Add the `name` variable to collection permalinks ([#2799]({{ site.repository }}/issues/2799)) +- Add `inspect` liquid filter. ([#2867]({{ site.repository }}/issues/2867)) +- Add a `slugify` Liquid filter ([#2880]({{ site.repository }}/issues/2880)) + +### Bug Fixes +{: #bug-fixes-v2-4-0} + +- Use `Jekyll.sanitized_path` when adding static files to Collections ([#2849]({{ site.repository }}/issues/2849)) +- Fix encoding of `main.scss` in site template ([#2771]({{ site.repository }}/issues/2771)) +- Fix orientation bugs in default site template ([#2862]({{ site.repository }}/issues/2862)) + +### Development Fixes +{: #development-fixes-v2-4-0} + +- Update simplecov gem to 0.9 ([#2748]({{ site.repository }}/issues/2748)) +- Remove `docs/` dir ([#2768]({{ site.repository }}/issues/2768)) +- add class `<< self` idiom to `New` command ([#2817]({{ site.repository }}/issues/2817)) +- Allow Travis to 'parallelize' our tests ([#2859]({{ site.repository }}/issues/2859)) +- Fix test for Liquid rendering in Sass ([#2856]({{ site.repository }}/issues/2856)) +- Fixing "vertycal" typo in site template's `_base.scss` ([#2889]({{ site.repository }}/issues/2889)) + +### Site Enhancements +{: #site-enhancements-v2-4-0} + +- Document the `name` variable for collection permalinks ([#2829]({{ site.repository }}/issues/2829)) +- Adds info about installing jekyll in current dir ([#2839]({{ site.repository }}/issues/2839)) +- Remove deprecated `jekyll-projectlist` plugin from list of third-party plugins ([#2742]({{ site.repository }}/issues/2742)) +- Remove tag plugins that are built in to Jekyll ([#2751]({{ site.repository }}/issues/2751)) +- Add `markdown-writer` package for Atom Editor to list of third-party plugins ([#2763]({{ site.repository }}/issues/2763)) +- Fix typo in site documentation for collections ([#2764]({{ site.repository }}/issues/2764)) +- Fix minor typo on plugins docs page ([#2765]({{ site.repository }}/issues/2765)) +- Replace markdown with HTML in `sass_dir` note on assets page ([#2791]({{ site.repository }}/issues/2791)) +- Fixed "bellow" typo in datafiles docs ([#2879]({{ site.repository }}/issues/2879)) +- Fix code/markdown issue in documentation for variables ([#2877]({{ site.repository }}/issues/2877)) +- Remove Good Include third-party plugin from plugins page ([#2881]({{ site.repository }}/issues/2881)) +- Add some more docs on `include_relative` ([#2884]({{ site.repository }}/issues/2884)) + + +## 2.3.0 / 2014-08-10 +{: #v2-3-0} + +### Minor Enhancements +{: #minor-enhancements-v2-3-0} + +- Allow Convertibles to be converted by >= 1 converters ([#2704]({{ site.repository }}/issues/2704)) +- Allow Sass files to be rendered in Liquid, but never place them in layouts. ([#2733]({{ site.repository }}/issues/2733)) +- Add `jekyll help` command ([#2707]({{ site.repository }}/issues/2707)) +- Use `.scss` for `site_template` styles. ([#2667]({{ site.repository }}/issues/2667)) +- Don't require the `scope` key in front matter defaults ([#2659]({{ site.repository }}/issues/2659)) +- No longer set `permalink: pretty` in the `_config.yml` for the site template ([#2680]({{ site.repository }}/issues/2680)) +- Rework site template to utilize Sass ([#2687]({{ site.repository }}/issues/2687)) +- Notify the user when auto-regeneration is disabled. ([#2696]({{ site.repository }}/issues/2696)) +- Allow partial variables in include tag filename argument ([#2693]({{ site.repository }}/issues/2693)) +- Move instances of `Time.parse` into a Utils method ([#2682]({{ site.repository }}/issues/2682)) +- Ignore subfolders in the `_posts` folder ([#2705]({{ site.repository }}/issues/2705)) REVERTS ([#2633]({{ site.repository }}/issues/2633)) +- Front Matter default types should always be pluralized ([#2732]({{ site.repository }}/issues/2732)) +- Read in static files into `collection.files` as `StaticFile`s ([#2737]({{ site.repository }}/issues/2737)) +- Add `sassify` and `scssify` Liquid filters ([#2739]({{ site.repository }}/issues/2739)) +- Replace `classifier` gem with `classifier-reborn` ([#2721]({{ site.repository }}/issues/2721)) + +### Bug Fixes +{: #bug-fixes-v2-3-0} + +- Use only the last extname when multiple converters exist ([#2722]({{ site.repository }}/issues/2722)) +- Call `#to_liquid` before calling `#to_json` in jsonify filter ([#2729]({{ site.repository }}/issues/2729)) +- Use non padded config in `strftime` to avoid parse string twice ([#2673]({{ site.repository }}/issues/2673)) +- Replace deprecated Ruby methods with undeprecated ones ([#2664]({{ site.repository }}/issues/2664)) +- Catch errors when parsing Post `date` front matter value & produce nice error message ([#2649]({{ site.repository }}/issues/2649)) +- Allow static files in Collections ([#2615]({{ site.repository }}/issues/2615)) +- Fixed typo in `Deprecator#gracefully_require` error message ([#2694]({{ site.repository }}/issues/2694)) +- Remove preemptive loading of the 'classifier' gem. ([#2697]({{ site.repository }}/issues/2697)) +- Use case-insensitive checking for the file extensions when loading config files ([#2718]({{ site.repository }}/issues/2718)) +- When Reading Documents, Respect `encoding` Option ([#2720]({{ site.repository }}/issues/2720)) +- Refactor based on jekyll-watch clean-up. ([#2716]({{ site.repository }}/issues/2716)) +- `Document#to_s` should produce just the content of the document ([#2731]({{ site.repository }}/issues/2731)) + +### Development Fixes +{: #development-fixes-v2-3-0} + +- Only include lib files in the gem ([#2671]({{ site.repository }}/issues/2671)) +- Fix `git diff` command in `proof` script ([#2672]({{ site.repository }}/issues/2672)) +- Make default rake task a multitask so tests run in parallel ([#2735]({{ site.repository }}/issues/2735)) + +### Site Enhancements +{: #site-enhancements-v2-3-0} + +- Use Sass and a Docs Collection ([#2651]({{ site.repository }}/issues/2651)) +- Add `latest_version.txt` file to the site ([#2740]({{ site.repository }}/issues/2740)) +- Be more ambiguous about `page.content`. But more transparent. ([#2522]({{ site.repository }}/issues/2522)) +- Streamlining front matter wording (instead of front-matter/frontmatter) ([#2674]({{ site.repository }}/issues/2674)) +- Add note that source directory cannot be modified in GitHub Pages ([#2669]({{ site.repository }}/issues/2669)) +- Fix links from [#2669]({{ site.repository }}/issues/2669) to be actual HTML. Whoops. ([#2679]({{ site.repository }}/issues/2679)) +- Add link to `jekyll-slim` in list of third-party plugins ([#2689]({{ site.repository }}/issues/2689)) +- Add Barry Clark's Smashing Magazine tutorial to resources page ([#2688]({{ site.repository }}/issues/2688)) +- Reorganize and update default configuration settings ([#2456]({{ site.repository }}/issues/2456)) +- Fixing indentation in the configuration docs about Redcarpet exts ([#2717]({{ site.repository }}/issues/2717)) +- Use `null` in YAML instead of `nil` in default config list ([#2719]({{ site.repository }}/issues/2719)) +- Fix typo in Continuous Integration docs ([#2708]({{ site.repository }}/issues/2708)) + + +## 2.2.0 / 2014-07-29 +{: #v2-2-0} + +### Minor Enhancements +{: #minor-enhancements-v2-2-0} + +- Throw a warning if the specified layout does not exist ([#2620]({{ site.repository }}/issues/2620)) +- Whitelist Pygments options in safe mode ([#2642]({{ site.repository }}/issues/2642)) + +### Bug Fixes +{: #bug-fixes-v2-2-0} + +- Remove unnecessary `Jekyll::Tags::IncludeTag#blank?` method ([#2625]({{ site.repository }}/issues/2625)) +- Categories in the path are ignored ([#2633]({{ site.repository }}/issues/2633)) + +### Development Fixes +{: #development-fixes-v2-2-0} + +- Refactoring Errors & Requires of Third-Party stuff ([#2591]({{ site.repository }}/issues/2591)) +- Add further tests for categories ([#2584]({{ site.repository }}/issues/2584)) +- Proof site with html-proofer on change ([#2605]({{ site.repository }}/issues/2605)) +- Fix up bug in [#2605]({{ site.repository }}/issues/2605) which caused proofing the site not to function ([#2608]({{ site.repository }}/issues/2608)) +- Use `bundle exec` in `script/proof` ([#2610]({{ site.repository }}/issues/2610)) + +### Site Enhancements +{: #site-enhancements-v2-2-0} + +- Update Kramdown urls ([#2588]({{ site.repository }}/issues/2588)) +- Add `Jekyll::AutolinkEmail` and `Jekyll::GitMetadata` to the list of third-party plugins ([#2596]({{ site.repository }}/issues/2596)) +- Fix a bunch of broken links in the site ([#2601]({{ site.repository }}/issues/2601)) +- Replace dead links with working links ([#2611]({{ site.repository }}/issues/2611)) +- Add jekyll-hook to deployment methods ([#2617]({{ site.repository }}/issues/2617)) +- Added kramdown-with-pygments plugin to the list of third-party plugins ([#2623]({{ site.repository }}/issues/2623)) +- Update outdated "Extras" page and remove duplicate documentation ([#2622]({{ site.repository }}/issues/2622)) +- Add co2 plugin to list of third-party plugins ([#2639]({{ site.repository }}/issues/2639)) +- Attempt to clarify the way Sass imports happen ([#2642]({{ site.repository }}/issues/2642)) + + +## 2.1.1 / 2014-07-01 +{: #v2-1-1} + +### Bug Fixes +{: #bug-fixes-v2-1-1} + +- Patch read vulnerabilities for data & confirm none for layouts ([#2563]({{ site.repository }}/issues/2563)) +- Update Maruku dependency to allow use of the latest version ([#2576]({{ site.repository }}/issues/2576)) +- Remove conditional assignment from document URL to prevent stale urls ([#2575]({{ site.repository }}/issues/2575)) + +### Site Enhancements +{: #site-enhancements-v2-1-1} + +- Add vertical margin to `highlight` to separate code blocks ([#2558]({{ site.repository }}/issues/2558)) +- Add `html_pages` to Variables docs ([#2567]({{ site.repository }}/issues/2567)) +- Fixed broken link to Permalinks page ([#2572]({{ site.repository }}/issues/2572)) +- Update link to Windows installation guide ([#2578]({{ site.repository }}/issues/2578)) + + +## 2.1.0 / 2014-06-28 +{: #v2-1-0} + +### Minor Enhancements +{: #minor-enhancements-v2-1-0} + +- Bump to the latest Liquid version, 2.6.1 ([#2495]({{ site.repository }}/issues/2495)) +- Add support for JSON files in the `_data` directory ([#2369]({{ site.repository }}/issues/2369)) +- Allow subclasses to override `EXCERPT_ATTRIBUTES_FOR_LIQUID` ([#2408]({{ site.repository }}/issues/2408)) +- Add `Jekyll.env` and `jekyll.environment` (the Liquid var) ([#2417]({{ site.repository }}/issues/2417)) +- Use `_config.yaml` or `_config.yml` (`.yml` takes precedence) ([#2406]({{ site.repository }}/issues/2406)) +- Override collection url template ([#2418]({{ site.repository }}/issues/2418)) +- Allow subdirectories in `_data` ([#2395]({{ site.repository }}/issues/2395)) +- Extract Pagination Generator into gem: `jekyll-paginate` ([#2455]({{ site.repository }}/issues/2455)) +- Utilize `date_to_rfc822` filter in site template ([#2437]({{ site.repository }}/issues/2437)) +- Add categories, last build datetime, and generator to site template feed ([#2438]({{ site.repository }}/issues/2438)) +- Configurable, replaceable Logger-compliant logger ([#2444]({{ site.repository }}/issues/2444)) +- Extract `gist` tag into a separate gem ([#2469]({{ site.repository }}/issues/2469)) +- Add `collection` attribute to `Document#to_liquid` to access the document's collection label. ([#2436]({{ site.repository }}/issues/2436)) +- Upgrade listen to `2.7.6 <= x < 3.0.0` ([#2492]({{ site.repository }}/issues/2492)) +- Allow configuration of different Twitter and GitHub usernames in site template ([#2485]({{ site.repository }}/issues/2485)) +- Bump Pygments to v0.6.0 ([#2504]({{ site.repository }}/issues/2504)) +- Front matter defaults for documents in collections ([#2419]({{ site.repository }}/issues/2419)) +- Include files with a url which ends in `/` in the `site.html_pages` list ([#2524]({{ site.repository }}/issues/2524)) +- Make `highlight` tag use `language-` prefix in CSS class ([#2511]({{ site.repository }}/issues/2511)) +- Lookup item property via `item#to_liquid` before `#data` or `#[]` in filters ([#2493]({{ site.repository }}/issues/2493)) +- Skip initial build of site on serve with flag ([#2477]({{ site.repository }}/issues/2477)) +- Add support for `hl_lines` in `highlight` tag ([#2532]({{ site.repository }}/issues/2532)) +- Spike out `--watch` flag into a separate gem ([#2550]({{ site.repository }}/issues/2550)) + +### Bug Fixes +{: #bug-fixes-v2-1-0} + +- Liquid `sort` filter should sort even if one of the values is `nil` ([#2345]({{ site.repository }}/issues/2345)) +- Remove padding on `pre code` in the site template CSS ([#2383]({{ site.repository }}/issues/2383)) +- Set `log_level` earlier to silence info level configuration output ([#2393]({{ site.repository }}/issues/2393)) +- Only list pages which have `title` in site template ([#2411]({{ site.repository }}/issues/2411)) +- Accept `Numeric` values for dates, not `Number` values ([#2377]({{ site.repository }}/issues/2377)) +- Prevent code from overflowing container in site template ([#2429]({{ site.repository }}/issues/2429)) +- Encode URLs in UTF-8 when escaping and unescaping ([#2420]({{ site.repository }}/issues/2420)) +- No Layouts or Liquid for Asset Files ([#2431]({{ site.repository }}/issues/2431)) +- Allow front matter defaults to set post categories ([#2373]({{ site.repository }}/issues/2373)) +- Fix command in subcommand deprecation warning ([#2457]({{ site.repository }}/issues/2457)) +- Keep all parent directories of files/dirs in `keep_files` ([#2458]({{ site.repository }}/issues/2458)) +- When using RedCarpet and Rouge without Rouge installed, fixed erroneous error which stated that redcarpet was missing, not rouge. ([#2464]({{ site.repository }}/issues/2464)) +- Ignore *all* directories and files that merit it on auto-generation ([#2459]({{ site.repository }}/issues/2459)) +- Before copying file, explicitly remove the old one ([#2535]({{ site.repository }}/issues/2535)) +- Merge file system categories with categories from YAML. ([#2531]({{ site.repository }}/issues/2531)) +- Deep merge front matter defaults ([#2490]({{ site.repository }}/issues/2490)) +- Ensure exclude and include arrays are arrays of strings ([#2542]({{ site.repository }}/issues/2542)) +- Allow collections to have dots in their filenames ([#2552]({{ site.repository }}/issues/2552)) +- Collections shouldn't try to read in directories as files ([#2552]({{ site.repository }}/issues/2552)) +- Be quiet very quickly. ([#2520]({{ site.repository }}/issues/2520)) + +### Development Fixes +{: #development-fixes-v2-1-0} + +- Test Ruby 2.1.2 instead of 2.1.1 ([#2374]({{ site.repository }}/issues/2374)) +- Add test for sorting UTF-8 characters ([#2384]({{ site.repository }}/issues/2384)) +- Use `https` for GitHub links in documentation ([#2470]({{ site.repository }}/issues/2470)) +- Remove coverage reporting with Coveralls ([#2494]({{ site.repository }}/issues/2494)) +- Fix a bit of missing TomDoc to `Jekyll::Commands::Build#build` ([#2554]({{ site.repository }}/issues/2554)) + +### Site Enhancements +{: #site-enhancements-v2-1-0} + +- Set `timezone` to `America/Los_Angeles` ([#2394]({{ site.repository }}/issues/2394)) +- Improve JavaScript in `anchor_links.html` ([#2368]({{ site.repository }}/issues/2368)) +- Remove note on Quickstart page about default markdown converter ([#2387]({{ site.repository }}/issues/2387)) +- Remove broken link in extras.md to a Maruku fork ([#2401]({{ site.repository }}/issues/2401)) +- Update Font Awesome to v4.1.0. ([#2410]({{ site.repository }}/issues/2410)) +- Fix broken link on Installation page to Templates page ([#2421]({{ site.repository }}/issues/2421)) +- Prevent table from extending parent width in permalink style table ([#2424]({{ site.repository }}/issues/2424)) +- Add collections to info about pagination support ([#2389]({{ site.repository }}/issues/2389)) +- Add `jekyll_github_sample` plugin to list of third-party plugins ([#2463]({{ site.repository }}/issues/2463)) +- Clarify documentation around front matter defaults and add details about defaults for collections. ([#2439]({{ site.repository }}/issues/2439)) +- Add Jekyll Project Version Tag to list of third-party plugins ([#2468]({{ site.repository }}/issues/2468)) +- Use `https` for GitHub links across whole site ([#2470]({{ site.repository }}/issues/2470)) +- Add StickerMule + Jekyll post ([#2476]({{ site.repository }}/issues/2476)) +- Add Jekyll Asset Pipeline Reborn to list of third-party plugins ([#2479]({{ site.repository }}/issues/2479)) +- Add link to jekyll-compress-html to list of third-party plugins ([#2514]({{ site.repository }}/issues/2514)) +- Add Piwigo Gallery to list of third-party plugins ([#2526]({{ site.repository }}/issues/2526)) +- Set `show_drafts` to `false` in default configuration listing ([#2536]({{ site.repository }}/issues/2536)) +- Provide an updated link for Windows installation instructions ([#2544]({{ site.repository }}/issues/2544)) +- Remove `url` from configuration docs ([#2547]({{ site.repository }}/issues/2547)) +- Documentation for Continuous Integration for your Jekyll Site ([#2432]({{ site.repository }}/issues/2432)) + + +## 2.0.3 / 2014-05-08 +{: #v2-0-3} + +### Bug Fixes +{: #bug-fixes-v2-0-3} + +- Properly prefix links in site template with URL or baseurl depending upon need. ([#2319]({{ site.repository }}/issues/2319)) +- Update gist tag comments and error message to require username ([#2326]({{ site.repository }}/issues/2326)) +- Fix `permalink` setting in site template ([#2331]({{ site.repository }}/issues/2331)) +- Don't fail if any of the path objects are nil ([#2325]({{ site.repository }}/issues/2325)) +- Instantiate all descendants for converters and generators, not just direct subclasses ([#2334]({{ site.repository }}/issues/2334)) +- Replace all instances of `site.name` with `site.title` in site template ([#2324]({{ site.repository }}/issues/2324)) +- `Jekyll::Filters#time` now accepts UNIX timestamps in string or number form ([#2339]({{ site.repository }}/issues/2339)) +- Use `item_property` for `where` filter so it doesn't break on collections ([#2359]({{ site.repository }}/issues/2359)) +- Rescue errors thrown so `--watch` doesn't fail ([#2364]({{ site.repository }}/issues/2364)) + +### Site Enhancements +{: #site-enhancements-v2-0-3} + +- Add missing "as" to assets docs page ([#2337]({{ site.repository }}/issues/2337)) +- Update docs to reflect new `baseurl` default ([#2341]({{ site.repository }}/issues/2341)) +- Add links to headers who have an ID. ([#2342]({{ site.repository }}/issues/2342)) +- Use symbol instead of HTML number in `upgrading.md` ([#2351]({{ site.repository }}/issues/2351)) +- Fix link to front matter defaults docs ([#2353]({{ site.repository }}/issues/2353)) +- Fix for `History.markdown` in order to fix history page in docs ([#2363]({{ site.repository }}/issues/2363)) + + +## 2.0.2 / 2014-05-07 +{: #v2-0-2} + +### Bug Fixes +{: #bug-fixes-v2-0-2} + +- Correct use of `url` and `baseurl` in the site template. ([#2317]({{ site.repository }}/issues/2317)) +- Default `baseurl` to `""` ([#2317]({{ site.repository }}/issues/2317)) + +### Site Enhancements +{: #site-enhancements-v2-0-2} + +- Correct docs for the `gist` plugin so it always includes the username. ([#2314]({{ site.repository }}/issues/2314)) +- Clarify new (defaults, `where` filter) features in docs ([#2316]({{ site.repository }}/issues/2316)) + + +## 2.0.1 / 2014-05-06 +{: #v2-0-1} + +### Bug Fixes +{: #bug-fixes-v2-0-1} + +- Require `kramdown` gem instead of `maruku` gem + + +## 2.0.0 / 2014-05-06 +{: #v2-0-0} + +### Major Enhancements +{: #major-enhancements-v2-0-0} + +- Add "Collections" feature ([#2199]({{ site.repository }}/issues/2199)) +- Add gem-based plugin whitelist to safe mode ([#1657]({{ site.repository }}/issues/1657)) +- Replace the commander command line parser with a more robust solution for our needs called `mercenary` ([#1706]({{ site.repository }}/issues/1706)) +- Remove support for Ruby 1.8.x ([#1780]({{ site.repository }}/issues/1780)) +- Move to jekyll/jekyll from mojombo/jekyll ([#1817]({{ site.repository }}/issues/1817)) +- Allow custom markdown processors ([#1872]({{ site.repository }}/issues/1872)) +- Provide support for the Rouge syntax highlighter ([#1859]({{ site.repository }}/issues/1859)) +- Provide support for Sass ([#1932]({{ site.repository }}/issues/1932)) +- Provide a 300% improvement when generating sites that use `Post#next` or `Post#previous` ([#1983]({{ site.repository }}/issues/1983)) +- Provide support for CoffeeScript ([#1991]({{ site.repository }}/issues/1991)) +- Replace Maruku with Kramdown as Default Markdown Processor ([#1988]({{ site.repository }}/issues/1988)) +- Expose `site.static_files` to Liquid ([#2075]({{ site.repository }}/issues/2075)) +- Complete redesign of the template site generated by `jekyll new` ([#2050]({{ site.repository }}/issues/2050)) +- Update Listen from 1.x to 2.x ([#2097]({{ site.repository }}/issues/2097)) +- Front matter defaults ([#2205]({{ site.repository }}/issues/2205)) +- Deprecate `relative_permalinks` configuration option (default to `false`) ([#2307]({{ site.repository }}/issues/2307)) +- Exclude files based on prefix as well as `fnmatch?` ([#2303]({{ site.repository }}/issues/2303)) + +### Minor Enhancements +{: #minor-enhancements-v2-0-0} + +- Move the EntryFilter class into the Jekyll module to avoid polluting the global namespace ([#1800]({{ site.repository }}/issues/1800)) +- Add `group_by` Liquid filter create lists of items grouped by a common property's value ([#1788]({{ site.repository }}/issues/1788)) +- Add support for Maruku's `fenced_code_blocks` option ([#1799]({{ site.repository }}/issues/1799)) +- Update Redcarpet dependency to ~> 3.0 ([#1815]({{ site.repository }}/issues/1815)) +- Automatically sort all pages by name ([#1848]({{ site.repository }}/issues/1848)) +- Better error message when time is not parseable ([#1847]({{ site.repository }}/issues/1847)) +- Allow `include` tag variable arguments to use filters ([#1841]({{ site.repository }}/issues/1841)) +- `post_url` tag should raise `ArgumentError` for invalid name ([#1825]({{ site.repository }}/issues/1825)) +- Bump dependency `mercenary` to `~> 0.2.0` ([#1879]({{ site.repository }}/issues/1879)) +- Bump dependency `safe_yaml` to `~> 1.0` ([#1886]({{ site.repository }}/issues/1886)) +- Allow sorting of content by custom properties ([#1849]({{ site.repository }}/issues/1849)) +- Add `--quiet` flag to silence output during build and serve ([#1898]({{ site.repository }}/issues/1898)) +- Add a `where` filter to filter arrays based on a key/value pair ([#1875]({{ site.repository }}/issues/1875)) +- Route 404 errors to a custom 404 page in development ([#1899]({{ site.repository }}/issues/1899)) +- Excludes are now relative to the site source ([#1916]({{ site.repository }}/issues/1916)) +- Bring MIME Types file for `jekyll serve` to complete parity with GH Pages servers ([#1993]({{ site.repository }}/issues/1993)) +- Adding Breakpoint to make new site template more responsive ([#2038]({{ site.repository }}/issues/2038)) +- Default to using the UTF-8 encoding when reading files. ([#2031]({{ site.repository }}/issues/2031)) +- Update Redcarpet dependency to ~> 3.1 ([#2044]({{ site.repository }}/issues/2044)) +- Remove support for Ruby 1.9.2 ([#2045]({{ site.repository }}/issues/2045)) +- Add `.mkdown` as valid Markdown extension ([#2048]({{ site.repository }}/issues/2048)) +- Add `index.xml` to the list of WEBrick directory index files ([#2041]({{ site.repository }}/issues/2041)) +- Make the `layouts` config key relative to CWD or to source ([#2058]({{ site.repository }}/issues/2058)) +- Update Kramdown to `~> 1.3` ([#1894]({{ site.repository }}/issues/1894)) +- Remove unnecessary references to `self` ([#2090]({{ site.repository }}/issues/2090)) +- Update to Mercenary v0.3.x ([#2085]({{ site.repository }}/issues/2085)) +- Ship Sass support as a separate gem ([#2098]({{ site.repository }}/issues/2098)) +- Extract core extensions into a Utils module ([#2112]({{ site.repository }}/issues/2112)) +- Refactor CLI & Commands For Greater Happiness ([#2143]({{ site.repository }}/issues/2143)) +- Provide useful error when Pygments returns `nil` and error out ([#2148]({{ site.repository }}/issues/2148)) +- Add support for unpublished drafts ([#2164]({{ site.repository }}/issues/2164)) +- Add `force_polling` option to the `serve` command ([#2165]({{ site.repository }}/issues/2165)) +- Clean up the `<head>` in the site template ([#2186]({{ site.repository }}/issues/2186)) +- Permit YAML blocks to end with three dots to better conform with the YAML spec ([#2110]({{ site.repository }}/issues/2110)) +- Use `File.exist?` instead of deprecated `File.exists?` ([#2214]({{ site.repository }}/issues/2214)) +- Require newline after start of front matter header ([#2211]({{ site.repository }}/issues/2211)) +- Add the ability for pages to be marked as `published: false` ([#1492]({{ site.repository }}/issues/1492)) +- Add `Jekyll::LiquidExtensions` with `.lookup_variable` method for easy looking up of variable values in a Liquid context. ([#2253]({{ site.repository }}/issues/2253)) +- Remove literal lang name from class ([#2292]({{ site.repository }}/issues/2292)) +- Return `utf-8` encoding in header for webrick error page response ([#2289]({{ site.repository }}/issues/2289)) +- Make template site easier to customize ([#2268]({{ site.repository }}/issues/2268)) +- Add two-digit year to permalink template option ([#2301]({{ site.repository }}/issues/2301)) +- Add `site.documents` to Liquid payload (list of all docs) ([#2295]({{ site.repository }}/issues/2295)) +- Take into account missing values in the Liquid sort filter ([#2299]({{ site.repository }}/issues/2299)) + +### Bug Fixes +{: #bug-fixes-v2-0-0} + +- Don't allow nil entries when loading posts ([#1796]({{ site.repository }}/issues/1796)) +- Remove the scrollbar that's always displayed in new sites generated from the site template ([#1805]({{ site.repository }}/issues/1805)) +- Add `#path` to required methods in `Jekyll::Convertible` ([#1866]({{ site.repository }}/issues/1866)) +- Default Maruku fenced code blocks to ON for 2.0.0-dev ([#1831]({{ site.repository }}/issues/1831)) +- Change short opts for host and port for `jekyll docs` to be consistent with other subcommands ([#1877]({{ site.repository }}/issues/1877)) +- Fix typos ([#1910]({{ site.repository }}/issues/1910)) +- Lock Maruku at 0.7.0 to prevent bugs caused by Maruku 0.7.1 ([#1958]({{ site.repository }}/issues/1958)) +- Fixes full path leak to source directory when using include tag ([#1951]({{ site.repository }}/issues/1951)) +- Don't generate pages that aren't being published ([#1931]({{ site.repository }}/issues/1931)) +- Use `SafeYAML.load` to avoid conflicts with other projects ([#1982]({{ site.repository }}/issues/1982)) +- Relative posts should never fail to build ([#1976]({{ site.repository }}/issues/1976)) +- Remove executable bits of non executable files ([#2056]({{ site.repository }}/issues/2056)) +- `#path` for a draft is now `_drafts` instead of `_posts` ([#2042]({{ site.repository }}/issues/2042)) +- Patch a couple show-stopping security vulnerabilities ([#1946]({{ site.repository }}/issues/1946)) +- Sanitize paths uniformly, in a Windows-friendly way ([#2065]({{ site.repository }}/issues/2065), [#2109]({{ site.repository }}/issues/2109)) +- Update gem build steps to work correctly on Windows ([#2118]({{ site.repository }}/issues/2118)) +- Remove obsolete `normalize_options` method call from `bin/jekyll` ([#2121]({{ site.repository }}/issues/2121)) +- Remove `+` characters from Pygments lexer names when adding as a CSS class ([#994]({{ site.repository }}/issues/994)) +- Remove some code that caused Ruby interpreter warnings ([#2178]({{ site.repository }}/issues/2178)) +- Only strip the drive name if it begins the string ([#2175]({{ site.repository }}/issues/2175)) +- Remove default post with invalid date from site template ([#2200]({{ site.repository }}/issues/2200)) +- Fix `Post#url` and `Page#url` escape ([#1568]({{ site.repository }}/issues/1568)) +- Strip newlines from the {% raw %}`{% highlight %}`{% endraw %} block content ([#1823]({{ site.repository }}/issues/1823)) +- Load in `rouge` only when it's been requested as the highlighter ([#2189]({{ site.repository }}/issues/2189)) +- Convert input to string before XML escaping (`xml_escape` liquid filter) ([#2244]({{ site.repository }}/issues/2244)) +- Modify configuration key for Collections and reset properly. ([#2238]({{ site.repository }}/issues/2238)) +- Avoid duplicated output using `highlight` tag ([#2264]({{ site.repository }}/issues/2264)) +- Only use Jekyll.logger for output ([#2307]({{ site.repository }}/issues/2307)) +- Close the file descriptor in `has_yaml_header?` ([#2310]({{ site.repository }}/issues/2310)) +- Add `output` to `Document` liquid output hash ([#2309]({{ site.repository }}/issues/2309)) + +### Development Fixes +{: #development-fixes-v2-0-0} + +- Add a link to the site in the README.md file ([#1795]({{ site.repository }}/issues/1795)) +- Add in History and site changes from `v1-stable` branch ([#1836]({{ site.repository }}/issues/1836)) +- Testing additions on the Excerpt class ([#1893]({{ site.repository }}/issues/1893)) +- Fix the `highlight` tag feature ([#1859]({{ site.repository }}/issues/1859)) +- Test Jekyll under Ruby 2.1.0 ([#1900]({{ site.repository }}/issues/1900)) +- Add script/cibuild for fun and profit ([#1912]({{ site.repository }}/issues/1912)) +- Use `Forwardable` for delegation between `Excerpt` and `Post` ([#1927]({{ site.repository }}/issues/1927)) +- Rename `read_things` to `read_content` ([#1928]({{ site.repository }}/issues/1928)) +- Add `script/branding` script for ASCII art lovin' ([#1936]({{ site.repository }}/issues/1936)) +- Update the README to reflect the repo move ([#1943]({{ site.repository }}/issues/1943)) +- Add the project vision to the README ([#1935]({{ site.repository }}/issues/1935)) +- Speed up Travis CI builds by using Rebund ([#1985]({{ site.repository }}/issues/1985)) +- Use Yarp as a Gem proxy for Travis CI ([#1984]({{ site.repository }}/issues/1984)) +- Remove Yarp as a Gem proxy for Travis CI ([#2004]({{ site.repository }}/issues/2004)) +- Move the reading of layouts into its own class ([#2020]({{ site.repository }}/issues/2020)) +- Test Sass import ([#2009]({{ site.repository }}/issues/2009)) +- Switch Maruku and Kramdown in lists of Runtime vs. Development dependencies ([#2049]({{ site.repository }}/issues/2049)) +- Clean up the gemspec for the project ([#2095]({{ site.repository }}/issues/2095)) +- Add Japanese translation of README and CONTRIBUTING docs. ([#2081]({{ site.repository }}/issues/2081)) +- Re-align the tables in Cucumber ([#2108]({{ site.repository }}/issues/2108)) +- Trim trailing spaces and convert tabs to spaces ([#2122]({{ site.repository }}/issues/2122)) +- Fix the failing Travis scenarios due to Cucumber issues ([#2155]({{ site.repository }}/issues/2155)) +- Wrap `bundle install` in `travis_retry` to retry when RubyGems fails ([#2160]({{ site.repository }}/issues/2160)) +- Refactor tags and categories ([#1639]({{ site.repository }}/issues/1639)) +- Extract plugin management into its own class ([#2197]({{ site.repository }}/issues/2197)) +- Add missing tests for `Command` ([#2216]({{ site.repository }}/issues/2216)) +- Update `rr` link in CONTRIBUTING doc ([#2247]({{ site.repository }}/issues/2247)) +- Streamline Cucumber execution of `jekyll` subcommands ([#2258]({{ site.repository }}/issues/2258)) +- Refactor `Commands::Serve`. ([#2269]({{ site.repository }}/issues/2269)) +- Refactor `highlight` tag ([#2154]({{ site.repository }}/issues/2154)) +- Update `Util` hash functions with latest from Rails ([#2273]({{ site.repository }}/issues/2273)) +- Workaround for Travis bug ([#2290]({{ site.repository }}/issues/2290)) + +### Site Enhancements +{: #site-enhancements-v2-0-0} + +- Document Kramdown's GFM parser option ([#1791]({{ site.repository }}/issues/1791)) +- Move CSS to includes & update normalize.css to v2.1.3 ([#1787]({{ site.repository }}/issues/1787)) +- Minify CSS only in production ([#1803]({{ site.repository }}/issues/1803)) +- Fix broken link to installation of Ruby on Mountain Lion blog post on Troubleshooting docs page ([#1797]({{ site.repository }}/issues/1797)) +- Fix issues with 1.4.1 release blog post ([#1804]({{ site.repository }}/issues/1804)) +- Add note about deploying to OpenShift ([#1812]({{ site.repository }}/issues/1812)) +- Collect all Windows-related docs onto one page ([#1818]({{ site.repository }}/issues/1818)) +- Fixed typo in datafiles doc page ([#1854]({{ site.repository }}/issues/1854)) +- Clarify how to access `site` in docs ([#1864]({{ site.repository }}/issues/1864)) +- Add closing `<code>` tag to `context.registers[:site]` note ([#1867]({{ site.repository }}/issues/1867)) +- Fix link to @mojombo's site source ([#1897]({{ site.repository }}/issues/1897)) +- Add `paginate: nil` to default configuration in docs ([#1896]({{ site.repository }}/issues/1896)) +- Add link to our License in the site footer ([#1889]({{ site.repository }}/issues/1889)) +- Add a charset note in "Writing Posts" doc page ([#1902]({{ site.repository }}/issues/1902)) +- Disallow selection of path and prompt in bash examples +- Add jekyll-compass to the plugin list ([#1923]({{ site.repository }}/issues/1923)) +- Add note in Posts docs about stripping `<p>` tags from excerpt ([#1933]({{ site.repository }}/issues/1933)) +- Add additional info about the new exclude behavior ([#1938]({{ site.repository }}/issues/1938)) +- Linkify 'awesome contributors' to point to the contributors graph on GitHub ([#1940]({{ site.repository }}/issues/1940)) +- Update `docs/sites.md` link to GitHub Training materials ([#1949]({{ site.repository }}/issues/1949)) +- Update `master` with the release info from 1.4.3 ([#1947]({{ site.repository }}/issues/1947)) +- Define docs nav in datafile ([#1953]({{ site.repository }}/issues/1953)) +- Clarify the docs around the naming convention for posts ([#1971]({{ site.repository }}/issues/1971)) +- Add missing `next` and `previous` docs for post layouts and templates ([#1970]({{ site.repository }}/issues/1970)) +- Add note to `Writing posts` page about how to strip html from excerpt ([#1962]({{ site.repository }}/issues/1962)) +- Add `jekyll-humanize` plugin to plugin list ([#1998]({{ site.repository }}/issues/1998)) +- Add `jekyll-font-awesome` plugin to plugin list ([#1999]({{ site.repository }}/issues/1999)) +- Add `sublime-jekyll` to list of Editor plugins ([#2001]({{ site.repository }}/issues/2001)) +- Add `vim-jekyll` to the list of Editor plugins ([#2005]({{ site.repository }}/issues/2005)) +- Fix non-semantic nesting of `p` tags in `news_item` layout ([#2013]({{ site.repository }}/issues/2013)) +- Document destination folder cleaning ([#2016]({{ site.repository }}/issues/2016)) +- Updated instructions for NearlyFreeSpeech.NET installation ([#2015]({{ site.repository }}/issues/2015)) +- Update link to rack-jekyll on "Deployment Methods" page ([#2047]({{ site.repository }}/issues/2047)) +- Fix typo in /docs/configuration ([#2073]({{ site.repository }}/issues/2073)) +- Fix count in docs for `site.static_files` ([#2077]({{ site.repository }}/issues/2077)) +- Update configuration docs to indicate utf-8 is the default for 2.0.0 and ASCII for 1.9.3 ([#2074]({{ site.repository }}/issues/2074)) +- Add info about unreleased feature to the site ([#2061]({{ site.repository }}/issues/2061)) +- Add whitespace to liquid example in GitHub Pages docs ([#2084]({{ site.repository }}/issues/2084)) +- Clarify the way Sass and CoffeeScript files are read in and output ([#2067]({{ site.repository }}/issues/2067)) +- Add lyche gallery tag plugin link to list of plugins ([#2094]({{ site.repository }}/issues/2094)) +- Add Jekyll Pages Directory plugin to list of plugins ([#2096]({{ site.repository }}/issues/2096)) +- Update Configuration docs page with new markdown extension ([#2102]({{ site.repository }}/issues/2102)) +- Add `jekyll-image-set` to the list of third-party plugins ([#2105]({{ site.repository }}/issues/2105)) +- Losslessly compress images ([#2128]({{ site.repository }}/issues/2128)) +- Update normalize.css to 3.0.0 ([#2126]({{ site.repository }}/issues/2126)) +- Update modernizr to v2.7.1 ([#2129]({{ site.repository }}/issues/2129)) +- Add `jekyll-ordinal` to list of third-party plugins ([#2150]({{ site.repository }}/issues/2150)) +- Add `jekyll_figure` to list of third-party plugins ([#2158]({{ site.repository }}/issues/2158)) +- Clarify the documentation for safe mode ([#2163]({{ site.repository }}/issues/2163)) +- Some HTML tidying ([#2130]({{ site.repository }}/issues/2130)) +- Remove modernizr and use html5shiv.js directly for IE less than v9 ([#2131]({{ site.repository }}/issues/2131)) +- Remove unused images ([#2187]({{ site.repository }}/issues/2187)) +- Use `array_to_sentence_string` filter when outputting news item categories ([#2191]({{ site.repository }}/issues/2191)) +- Add link to Help repo in primary navigation bar ([#2177]({{ site.repository }}/issues/2177)) +- Switch to using an ico file for the shortcut icon ([#2193]({{ site.repository }}/issues/2193)) +- Use numbers to specify font weights and only bring in font weights used ([#2185]({{ site.repository }}/issues/2185)) +- Add a link to the list of all tz database time zones ([#1824]({{ site.repository }}/issues/1824)) +- Clean-up and improve documentation `feed.xml` ([#2192]({{ site.repository }}/issues/2192)) +- Remove duplicate entry in list of third-party plugins ([#2206]({{ site.repository }}/issues/2206)) +- Reduce the whitespace in the favicon. ([#2213]({{ site.repository }}/issues/2213)) +- Add `jekyll-page-collections` to list of third-party plugins ([#2215]({{ site.repository }}/issues/2215)) +- Add a cross-reference about `post_url` ([#2243]({{ site.repository }}/issues/2243)) +- Add `jekyll-live-tiles` to list of third-party plugins ([#2250]({{ site.repository }}/issues/2250)) +- Fixed broken link to GitHub training material site source ([#2257]({{ site.repository }}/issues/2257)) +- Update link to help repo, now called `jekyll-help` ([#2277]({{ site.repository }}/issues/2277)) +- Fix capitalization of 'Jekyll' on Deployment Methods page ([#2291]({{ site.repository }}/issues/2291)) +- Include plugins by sonnym in list of third-party plugins ([#2297]({{ site.repository }}/issues/2297)) +- Add deprecated articles keeper filter to list of third-party plugins ([#2300]({{ site.repository }}/issues/2300)) +- Simplify and improve our CSS. ([#2127]({{ site.repository }}/issues/2127)) +- Use black text color for the mobile navbar ([#2306]({{ site.repository }}/issues/2306)) +- Use the built in date filter and `site.time` for the copyright year. ([#2305]({{ site.repository }}/issues/2305)) +- Update html5shiv to v3.7.2 ([#2304]({{ site.repository }}/issues/2304)) +- Add 2.0.0 release post ([#2298]({{ site.repository }}/issues/2298)) +- Add docs for custom markdown processors ([#2298]({{ site.repository }}/issues/2298)) +- Add docs for `where` and `group_by` Liquid filters ([#2298]({{ site.repository }}/issues/2298)) +- Remove notes in docs for unreleased features ([#2309]({{ site.repository }}/issues/2309)) + + +## 1.5.1 / 2014-03-27 +{: #v1-5-1} + +### Bug Fixes +{: #bug-fixes-v1-5-1} + +- Only strip the drive name if it begins the string ([#2176]({{ site.repository }}/issues/2176)) + + +## 1.5.0 / 2014-03-24 +{: #v1-5-0} + +### Minor Enhancements +{: #minor-enhancements-v1-5-0} + +- Loosen `safe_yaml` dependency to `~> 1.0` ([#2167]({{ site.repository }}/issues/2167)) +- Bump `safe_yaml` dependency to `~> 1.0.0` ([#1942]({{ site.repository }}/issues/1942)) + +### Bug Fixes +{: #bug-fixes-v1-5-0} + +- Fix issue where filesystem traversal restriction broke Windows ([#2167]({{ site.repository }}/issues/2167)) +- Lock `maruku` at `0.7.0` ([#2167]({{ site.repository }}/issues/2167)) + +### Development Fixes +{: #development-fixes-v1-5-0} + +- Lock `cucumber` at `1.3.11` ([#2167]({{ site.repository }}/issues/2167)) + + +## 1.4.3 / 2014-01-13 +{: #v1-4-3} + +### Bug Fixes +{: #bug-fixes-v1-4-3} + +- Patch show-stopping security vulnerabilities ([#1944]({{ site.repository }}/issues/1944)) + + +## 1.4.2 / 2013-12-16 +{: #v1-4-2} + +### Bug Fixes +{: #bug-fixes-v1-4-2} + +- Turn on Maruku fenced code blocks by default ([#1830]({{ site.repository }}/issues/1830)) + + +## 1.4.1 / 2013-12-09 +{: #v1-4-1} + +### Bug Fixes +{: #bug-fixes-v1-4-1} + +- Don't allow nil entries when loading posts ([#1796]({{ site.repository }}/issues/1796)) + + +## 1.4.0 / 2013-12-07 +{: #v1-4-0} + +### Major Enhancements +{: #major-enhancements-v1-4-0} + +- Add support for TOML config files ([#1765]({{ site.repository }}/issues/1765)) + +### Minor Enhancements +{: #minor-enhancements-v1-4-0} + +- Sort plugins as a way to establish a load order ([#1682]({{ site.repository }}/issues/1682)) +- Update Maruku to 0.7.0 ([#1775]({{ site.repository }}/issues/1775)) + +### Bug Fixes +{: #bug-fixes-v1-4-0} + +- Add a space between two words in a Pagination warning message ([#1769]({{ site.repository }}/issues/1769)) +- Upgrade `toml` gem to `v0.1.0` to maintain compat with Ruby 1.8.7 ([#1778]({{ site.repository }}/issues/1778)) + +### Development Fixes +{: #development-fixes-v1-4-0} + +- Remove some whitespace in the code ([#1755]({{ site.repository }}/issues/1755)) +- Remove some duplication in the reading of posts and drafts ([#1779]({{ site.repository }}/issues/1779)) + +### Site Enhancements +{: #site-enhancements-v1-4-0} + +- Fixed case of a word in the Jekyll v1.3.0 release post ([#1762]({{ site.repository }}/issues/1762)) +- Fixed the mime type for the favicon ([#1772]({{ site.repository }}/issues/1772)) + + +## 1.3.1 / 2013-11-26 +{: #v1-3-1} + +### Minor Enhancements +{: #minor-enhancements-v1-3-1} + +- Add a `--prefix` option to passthrough for the importers ([#1669]({{ site.repository }}/issues/1669)) +- Push the paginator plugin lower in the plugin priority order so other plugins run before it ([#1759]({{ site.repository }}/issues/1759)) + +### Bug Fixes +{: #bug-fixes-v1-3-1} + +- Fix the include tag when ran in a loop ([#1726]({{ site.repository }}/issues/1726)) +- Fix errors when using `--watch` on 1.8.7 ([#1730]({{ site.repository }}/issues/1730)) +- Specify where the include is called from if an included file is missing ([#1746]({{ site.repository }}/issues/1746)) + +### Development Fixes +{: #development-fixes-v1-3-1} + +- Extract `Site#filter_entries` into its own object ([#1697]({{ site.repository }}/issues/1697)) +- Enable Travis' bundle caching ([#1734]({{ site.repository }}/issues/1734)) +- Remove trailing whitespace in some files ([#1736]({{ site.repository }}/issues/1736)) +- Fix a duplicate test name ([#1754]({{ site.repository }}/issues/1754)) + +### Site Enhancements +{: #site-enhancements-v1-3-1} + +- Update link to example Rakefile to point to specific commit ([#1741]({{ site.repository }}/issues/1741)) +- Fix drafts docs to indicate that draft time is based on file modification time, not `Time.now` ([#1695]({{ site.repository }}/issues/1695)) +- Add `jekyll-monthly-archive-plugin` and `jekyll-category-archive-plugin` to list of third-party plugins ([#1693]({{ site.repository }}/issues/1693)) +- Add `jekyll-asset-path-plugin` to list of third-party plugins ([#1670]({{ site.repository }}/issues/1670)) +- Add `emoji-for-jekyll` to list of third-part plugins ([#1708]({{ site.repository }}/issues/1708)) +- Fix previous section link on plugins page to point to pagination page ([#1707]({{ site.repository }}/issues/1707)) +- Add `org-mode` converter plugin to third-party plugins ([#1711]({{ site.repository }}/issues/1711)) +- Point "Blog migrations" page to http://import.jekyllrb.com ([#1732]({{ site.repository }}/issues/1732)) +- Add docs for `post_url` when posts are in subdirectories ([#1718]({{ site.repository }}/issues/1718)) +- Update the docs to point to `example.com` ([#1448]({{ site.repository }}/issues/1448)) + + +## 1.3.0 / 2013-11-04 +{: #v1-3-0} + +### Major Enhancements +{: #major-enhancements-v1-3-0} + +- Add support for adding data as YAML files under a site's `_data` directory ([#1003]({{ site.repository }}/issues/1003)) +- Allow variables to be used with `include` tags ([#1495]({{ site.repository }}/issues/1495)) +- Allow using gems for plugin management ([#1557]({{ site.repository }}/issues/1557)) + +### Minor Enhancements +{: #minor-enhancements-v1-3-0} + +- Decrease the specificity in the site template CSS ([#1574]({{ site.repository }}/issues/1574)) +- Add `encoding` configuration option ([#1449]({{ site.repository }}/issues/1449)) +- Provide better error handling for Jekyll's custom Liquid tags ([#1514]({{ site.repository }}/issues/1514)) +- If an included file causes a Liquid error, add the path to the include file that caused the error to the error message ([#1596]({{ site.repository }}/issues/1596)) +- If a layout causes a Liquid error, change the error message so that we know it comes from the layout ([#1601]({{ site.repository }}/issues/1601)) +- Update Kramdown dependency to `~> 1.2` ([#1610]({{ site.repository }}/issues/1610)) +- Update `safe_yaml` dependency to `~> 0.9.7` ([#1602]({{ site.repository }}/issues/1602)) +- Allow layouts to be in subfolders like includes ([#1622]({{ site.repository }}/issues/1622)) +- Switch to listen for site watching while serving ([#1589]({{ site.repository }}/issues/1589)) +- Add a `json` liquid filter to be used in sites ([#1651]({{ site.repository }}/issues/1651)) +- Point people to the migration docs when the `jekyll-import` gem is missing ([#1662]({{ site.repository }}/issues/1662)) + +### Bug Fixes +{: #bug-fixes-v1-3-0} + +- Fix up matching against source and destination when the two locations are similar ([#1556]({{ site.repository }}/issues/1556)) +- Fix the missing `pathname` require in certain cases ([#1255]({{ site.repository }}/issues/1255)) +- Use `+` instead of `Array#concat` when building `Post` attribute list ([#1571]({{ site.repository }}/issues/1571)) +- Print server address when launching a server ([#1586]({{ site.repository }}/issues/1586)) +- Downgrade to Maruku `~> 0.6.0` in order to avoid changes in rendering ([#1598]({{ site.repository }}/issues/1598)) +- Fix error with failing include tag when variable was file name ([#1613]({{ site.repository }}/issues/1613)) +- Downcase lexers before passing them to pygments ([#1615]({{ site.repository }}/issues/1615)) +- Capitalize the short verbose switch because it conflicts with the built-in Commander switch ([#1660]({{ site.repository }}/issues/1660)) +- Fix compatibility with 1.8.x ([#1665]({{ site.repository }}/issues/1665)) +- Fix an error with the new file watching code due to library version incompatibilities ([#1687]({{ site.repository }}/issues/1687)) + +### Development Fixes +{: #development-fixes-v1-3-0} + +- Add coverage reporting with Coveralls ([#1539]({{ site.repository }}/issues/1539)) +- Refactor the Liquid `include` tag ([#1490]({{ site.repository }}/issues/1490)) +- Update launchy dependency to `~> 2.3` ([#1608]({{ site.repository }}/issues/1608)) +- Update rr dependency to `~> 1.1` ([#1604]({{ site.repository }}/issues/1604)) +- Update cucumber dependency to `~> 1.3` ([#1607]({{ site.repository }}/issues/1607)) +- Update coveralls dependency to `~> 0.7.0` ([#1606]({{ site.repository }}/issues/1606)) +- Update rake dependency to `~> 10.1` ([#1603]({{ site.repository }}/issues/1603)) +- Clean up `site.rb` comments to be more concise/uniform ([#1616]({{ site.repository }}/issues/1616)) +- Use the master branch for the build badge in the readme ([#1636]({{ site.repository }}/issues/1636)) +- Refactor Site#render ([#1638]({{ site.repository }}/issues/1638)) +- Remove duplication in command line options ([#1637]({{ site.repository }}/issues/1637)) +- Add tests for all the coderay options ([#1543]({{ site.repository }}/issues/1543)) +- Improve some of the Cucumber test code ([#1493]({{ site.repository }}/issues/1493)) +- Improve comparisons of timestamps by ignoring the seconds ([#1582]({{ site.repository }}/issues/1582)) + +### Site Enhancements +{: #site-enhancements-v1-3-0} + +- Fix params for `JekyllImport::WordPress.process` arguments ([#1554]({{ site.repository }}/issues/1554)) +- Add `jekyll-suggested-tweet` to list of third-party plugins ([#1555]({{ site.repository }}/issues/1555)) +- Link to Liquid's docs for tags and filters ([#1553]({{ site.repository }}/issues/1553)) +- Add note about installing Xcode on the Mac in the Installation docs ([#1561]({{ site.repository }}/issues/1561)) +- Simplify/generalize pagination docs ([#1577]({{ site.repository }}/issues/1577)) +- Add documentation for the new data sources feature ([#1503]({{ site.repository }}/issues/1503)) +- Add more information on how to create generators ([#1590]({{ site.repository }}/issues/1590), [#1592]({{ site.repository }}/issues/1592)) +- Improve the instructions for mimicking GitHub Flavored Markdown ([#1614]({{ site.repository }}/issues/1614)) +- Add `jekyll-import` warning note of missing dependencies ([#1626]({{ site.repository }}/issues/1626)) +- Fix grammar in the Usage section ([#1635]({{ site.repository }}/issues/1635)) +- Add documentation for the use of gems as plugins ([#1656]({{ site.repository }}/issues/1656)) +- Document the existence of a few additional plugins ([#1405]({{ site.repository }}/issues/1405)) +- Document that the `date_to_string` always returns a two digit day ([#1663]({{ site.repository }}/issues/1663)) +- Fix navigation in the "Working with Drafts" page ([#1667]({{ site.repository }}/issues/1667)) +- Fix an error with the data documentation ([#1691]({{ site.repository }}/issues/1691)) + + +## 1.2.1 / 2013-09-14 +{: #v1-2-1} + +### Minor Enhancements +{: #minor-enhancements-v1-2-1} + +- Print better messages for detached server. Mute output on detach. ([#1518]({{ site.repository }}/issues/1518)) +- Disable reverse lookup when running `jekyll serve` ([#1363]({{ site.repository }}/issues/1363)) +- Upgrade RedCarpet dependency to `~> 2.3.0` ([#1515]({{ site.repository }}/issues/1515)) +- Upgrade to Liquid `>= 2.5.2, < 2.6` ([#1536]({{ site.repository }}/issues/1536)) + +### Bug Fixes +{: #bug-fixes-v1-2-1} + +- Fix file discrepancy in gemspec ([#1522]({{ site.repository }}/issues/1522)) +- Force rendering of Include tag ([#1525]({{ site.repository }}/issues/1525)) + +### Development Fixes +{: #development-fixes-v1-2-1} + +- Add a rake task to generate a new release post ([#1404]({{ site.repository }}/issues/1404)) +- Mute LSI output in tests ([#1531]({{ site.repository }}/issues/1531)) +- Update contributor documentation ([#1537]({{ site.repository }}/issues/1537)) + +### Site Enhancements +{: #site-enhancements-v1-2-1} + +- Fix a couple of validation errors on the site ([#1511]({{ site.repository }}/issues/1511)) +- Make navigation menus reusable ([#1507]({{ site.repository }}/issues/1507)) +- Fix link to History page from Release v1.2.0 notes post. +- Fix markup in History file for command line options ([#1512]({{ site.repository }}/issues/1512)) +- Expand 1.2 release post title to 1.2.0 ([#1516]({{ site.repository }}/issues/1516)) + + +## 1.2.0 / 2013-09-06 +{: #v1-2-0} + +### Major Enhancements +{: #major-enhancements-v1-2-0} + +- Disable automatically-generated excerpts when `excerpt_separator` is `""`. ([#1386]({{ site.repository }}/issues/1386)) +- Add checking for URL conflicts when running `jekyll doctor` ([#1389]({{ site.repository }}/issues/1389)) + +### Minor Enhancements +{: #minor-enhancements-v1-2-0} + +- Catch and fix invalid `paginate` values ([#1390]({{ site.repository }}/issues/1390)) +- Remove superfluous `div.container` from the default html template for `jekyll new` ([#1315]({{ site.repository }}/issues/1315)) +- Add `-D` short-form switch for the drafts option ([#1394]({{ site.repository }}/issues/1394)) +- Update the links in the site template for Twitter and GitHub ([#1400]({{ site.repository }}/issues/1400)) +- Update dummy email address to example.com domain ([#1408]({{ site.repository }}/issues/1408)) +- Update normalize.css to v2.1.2 and minify; add rake task to update normalize.css with greater ease. ([#1430]({{ site.repository }}/issues/1430)) +- Add the ability to detach the server ran by `jekyll serve` from it's controlling terminal ([#1443]({{ site.repository }}/issues/1443)) +- Improve permalink generation for URLs with special characters ([#944]({{ site.repository }}/issues/944)) +- Expose the current Jekyll version to posts and pages via a new `jekyll.version` variable ([#1481]({{ site.repository }}/issues/1481)) + +### Bug Fixes +{: #bug-fixes-v1-2-0} + +- Markdown extension matching matches only exact matches ([#1382]({{ site.repository }}/issues/1382)) +- Fixed NoMethodError when message passed to `Stevenson#message` is nil ([#1388]({{ site.repository }}/issues/1388)) +- Use binary mode when writing file ([#1364]({{ site.repository }}/issues/1364)) +- Fix 'undefined method `encoding` for "mailto"' errors w/ Ruby 1.8 and Kramdown > 0.14.0 ([#1397]({{ site.repository }}/issues/1397)) +- Do not force the permalink to be a dir if it ends on .html ([#963]({{ site.repository }}/issues/963)) +- When a Liquid Exception is caught, show the full path rel. to site source ([#1415]({{ site.repository }}/issues/1415)) +- Properly read in the config options when serving the docs locally ([#1444]({{ site.repository }}/issues/1444)) +- Fixed `--layouts` option for `build` and `serve` commands ([#1458]({{ site.repository }}/issues/1458)) +- Remove kramdown as a runtime dependency since it's optional ([#1498]({{ site.repository }}/issues/1498)) +- Provide proper error handling for invalid file names in the include tag ([#1494]({{ site.repository }}/issues/1494)) + +### Development Fixes +{: #development-fixes-v1-2-0} + +- Remove redundant argument to Jekyll::Commands::New#scaffold_post_content ([#1356]({{ site.repository }}/issues/1356)) +- Add new dependencies to the README ([#1360]({{ site.repository }}/issues/1360)) +- Fix link to contributing page in README ([#1424]({{ site.repository }}/issues/1424)) +- Update TomDoc in Pager#initialize to match params ([#1441]({{ site.repository }}/issues/1441)) +- Refactor `Site#cleanup` into `Jekyll::Site::Cleaner` class ([#1429]({{ site.repository }}/issues/1429)) +- Several other small minor refactorings ([#1341]({{ site.repository }}/issues/1341)) +- Ignore `_site` in jekyllrb.com deploy ([#1480]({{ site.repository }}/issues/1480)) +- Add Gem version and dependency badge to README ([#1497]({{ site.repository }}/issues/1497)) + +### Site Enhancements +{: #site-enhancements-v1-2-0} + +- Add info about new releases ([#1353]({{ site.repository }}/issues/1353)) +- Update plugin list with jekyll-rss plugin ([#1354]({{ site.repository }}/issues/1354)) +- Update the site list page with Ruby's official site ([#1358]({{ site.repository }}/issues/1358)) +- Add `jekyll-ditaa` to list of third-party plugins ([#1370]({{ site.repository }}/issues/1370)) +- Add `postfiles` to list of third-party plugins ([#1373]({{ site.repository }}/issues/1373)) +- For internal links, use full path including trailing `/` ([#1411]({{ site.repository }}/issues/1411)) +- Use curly apostrophes in the docs ([#1419]({{ site.repository }}/issues/1419)) +- Update the docs for Redcarpet in Jekyll ([#1418]({{ site.repository }}/issues/1418)) +- Add `pluralize` and `reading_time` filters to docs ([#1439]({{ site.repository }}/issues/1439)) +- Fix markup for the Kramdown options ([#1445]({{ site.repository }}/issues/1445)) +- Fix typos in the History file ([#1454]({{ site.repository }}/issues/1454)) +- Add trailing slash to site's post URL ([#1462]({{ site.repository }}/issues/1462)) +- Clarify that `--config` will take multiple files ([#1474]({{ site.repository }}/issues/1474)) +- Fix docs/templates.md private gist example ([#1477]({{ site.repository }}/issues/1477)) +- Use `site.repository` for Jekyll's GitHub URL ([#1463]({{ site.repository }}/issues/1463)) +- Add `jekyll-pageless-redirects` to list of third-party plugins ([#1486]({{ site.repository }}/issues/1486)) +- Clarify that `date_to_xmlschema` returns an ISO 8601 string ([#1488]({{ site.repository }}/issues/1488)) +- Add `jekyll-good-include` to list of third-party plugins ([#1491]({{ site.repository }}/issues/1491)) +- XML escape the blog post title in our feed ([#1501]({{ site.repository }}/issues/1501)) +- Add `jekyll-toc-generator` to list of third-party plugins ([#1506]({{ site.repository }}/issues/1506)) + + +## 1.1.2 / 2013-07-25 +{: #v1-1-2} + +### Bug Fixes +{: #bug-fixes-v1-1-2} + +- Require Liquid 2.5.1 ([#1349]({{ site.repository }}/issues/1349)) + + +## 1.1.1 / 2013-07-24 +{: #v1-1-1} + +### Minor Enhancements +{: #minor-enhancements-v1-1-1} + +- Remove superfluous `table` selector from main.css in `jekyll new` template ([#1328]({{ site.repository }}/issues/1328)) +- Abort with non-zero exit codes ([#1338]({{ site.repository }}/issues/1338)) + +### Bug Fixes +{: #bug-fixes-v1-1-1} + +- Fix up the rendering of excerpts ([#1339]({{ site.repository }}/issues/1339)) + +### Site Enhancements +{: #site-enhancements-v1-1-1} + +- Add Jekyll Image Tag to the plugins list ([#1306]({{ site.repository }}/issues/1306)) +- Remove erroneous statement that `site.pages` are sorted alphabetically. +- Add info about the `_drafts` directory to the directory structure docs ([#1320]({{ site.repository }}/issues/1320)) +- Improve the layout of the plugin listing by organizing it into categories ([#1310]({{ site.repository }}/issues/1310)) +- Add generator-jekyllrb and grunt-jekyll to plugins page ([#1330]({{ site.repository }}/issues/1330)) +- Mention Kramdown as option for markdown parser on Extras page ([#1318]({{ site.repository }}/issues/1318)) +- Update Quick-Start page to include reminder that all requirements must be installed ([#1327]({{ site.repository }}/issues/1327)) +- Change filename in `include` example to an HTML file so as not to indicate that Jekyll will automatically convert them. ([#1303]({{ site.repository }}/issues/1303)) +- Add an RSS feed for commits to Jekyll ([#1343]({{ site.repository }}/issues/1343)) + + +## 1.1.0 / 2013-07-14 +{: #v1-1-0} + +### Major Enhancements +{: #major-enhancements-v1-1-0} + +- Add `docs` subcommand to read Jekyll's docs when offline. ([#1046]({{ site.repository }}/issues/1046)) +- Support passing parameters to templates in `include` tag ([#1204]({{ site.repository }}/issues/1204)) +- Add support for Liquid tags to post excerpts ([#1302]({{ site.repository }}/issues/1302)) + +### Minor Enhancements +{: #minor-enhancements-v1-1-0} + +- Search the hierarchy of pagination path up to site root to determine template page for pagination. ([#1198]({{ site.repository }}/issues/1198)) +- Add the ability to generate a new Jekyll site without a template ([#1171]({{ site.repository }}/issues/1171)) +- Use redcarpet as the default markdown engine in newly generated sites ([#1245]({{ site.repository }}/issues/1245), [#1247]({{ site.repository }}/issues/1247)) +- Add `redcarpet` as a runtime dependency so `jekyll build` works out-of-the-box for new sites. ([#1247]({{ site.repository }}/issues/1247)) +- In the generated site, remove files that will be replaced by a directory ([#1118]({{ site.repository }}/issues/1118)) +- Fail loudly if a user-specified configuration file doesn't exist ([#1098]({{ site.repository }}/issues/1098)) +- Allow for all options for Kramdown HTML Converter ([#1201]({{ site.repository }}/issues/1201)) + +### Bug Fixes +{: #bug-fixes-v1-1-0} + +- Fix pagination in subdirectories. ([#1198]({{ site.repository }}/issues/1198)) +- Fix an issue with directories and permalinks that have a plus sign (+) in them ([#1215]({{ site.repository }}/issues/1215)) +- Provide better error reporting when generating sites ([#1253]({{ site.repository }}/issues/1253)) +- Latest posts first in non-LSI `related_posts` ([#1271]({{ site.repository }}/issues/1271)) + +### Development Fixes +{: #development-fixes-v1-1-0} + +- Merge the theme and layout Cucumber steps into one step ([#1151]({{ site.repository }}/issues/1151)) +- Restrict activesupport dependency to pre-4.0.0 to maintain compatibility with `<= 1.9.2` +- Include/exclude deprecation handling simplification ([#1284]({{ site.repository }}/issues/1284)) +- Convert README to Markdown. ([#1267]({{ site.repository }}/issues/1267)) +- Refactor Jekyll::Site ([#1144]({{ site.repository }}/issues/1144)) + +### Site Enhancements +{: #site-enhancements-v1-1-0} + +- Add "News" section for release notes, along with an RSS feed ([#1093]({{ site.repository }}/issues/1093), [#1285]({{ site.repository }}/issues/1285), [#1286]({{ site.repository }}/issues/1286)) +- Add "History" page. +- Restructured docs sections to include "Meta" section. +- Add message to "Templates" page that specifies that Python must be installed in order to use Pygments. ([#1182]({{ site.repository }}/issues/1182)) +- Update link to the official Maruku repo ([#1175]({{ site.repository }}/issues/1175)) +- Add documentation about `paginate_path` to "Templates" page in docs ([#1129]({{ site.repository }}/issues/1129)) +- Give the quick-start guide its own page ([#1191]({{ site.repository }}/issues/1191)) +- Update ProTip on Installation page in docs to point to all the info about Pygments and the 'highlight' tag. ([#1196]({{ site.repository }}/issues/1196)) +- Run `site/img` through ImageOptim (thanks @qrush!) ([#1208]({{ site.repository }}/issues/1208)) +- Added Jade Converter to `site/docs/plugins` ([#1210]({{ site.repository }}/issues/1210)) +- Fix location of docs pages in Contributing pages ([#1214]({{ site.repository }}/issues/1214)) +- Add ReadInXMinutes plugin to the plugin list ([#1222]({{ site.repository }}/issues/1222)) +- Remove plugins from the plugin list that have equivalents in Jekyll proper ([#1223]({{ site.repository }}/issues/1223)) +- Add jekyll-assets to the plugin list ([#1225]({{ site.repository }}/issues/1225)) +- Add jekyll-pandoc-multiple-formats to the plugin list ([#1229]({{ site.repository }}/issues/1229)) +- Remove dead link to "Using Git to maintain your blog" ([#1227]({{ site.repository }}/issues/1227)) +- Tidy up the third-party plugins listing ([#1228]({{ site.repository }}/issues/1228)) +- Update contributor information ([#1192]({{ site.repository }}/issues/1192)) +- Update URL of article about Blogger migration ([#1242]({{ site.repository }}/issues/1242)) +- Specify that RedCarpet is the default for new Jekyll sites on Quickstart page ([#1247]({{ site.repository }}/issues/1247)) +- Added `site.pages` to Variables page in docs ([#1251]({{ site.repository }}/issues/1251)) +- Add Youku and Tudou Embed link on Plugins page. ([#1250]({{ site.repository }}/issues/1250)) +- Add note that `gist` tag supports private gists. ([#1248]({{ site.repository }}/issues/1248)) +- Add `jekyll-timeago` to list of third-party plugins. ([#1260]({{ site.repository }}/issues/1260)) +- Add `jekyll-swfobject` to list of third-party plugins. ([#1263]({{ site.repository }}/issues/1263)) +- Add `jekyll-picture-tag` to list of third-party plugins. ([#1280]({{ site.repository }}/issues/1280)) +- Update the GitHub Pages documentation regarding relative URLs ([#1291]({{ site.repository }}/issues/1291)) +- Update the S3 deployment documentation ([#1294]({{ site.repository }}/issues/1294)) +- Add suggestion for Xcode CLT install to troubleshooting page in docs ([#1296]({{ site.repository }}/issues/1296)) +- Add 'Working with drafts' page to docs ([#1289]({{ site.repository }}/issues/1289)) +- Add information about time zones to the documentation for a page's date ([#1304]({{ site.repository }}/issues/1304)) + + +## 1.0.3 / 2013-06-07 +{: #v1-0-3} + +### Minor Enhancements +{: #minor-enhancements-v1-0-3} + +- Add support to gist tag for private gists. ([#1189]({{ site.repository }}/issues/1189)) +- Fail loudly when Maruku errors out ([#1190]({{ site.repository }}/issues/1190)) +- Move the building of related posts into their own class ([#1057]({{ site.repository }}/issues/1057)) +- Removed trailing spaces in several places throughout the code ([#1116]({{ site.repository }}/issues/1116)) +- Add a `--force` option to `jekyll new` ([#1115]({{ site.repository }}/issues/1115)) +- Convert IDs in the site template to classes ([#1170]({{ site.repository }}/issues/1170)) + +### Bug Fixes +{: #bug-fixes-v1-0-3} + +- Fix typo in Stevenson constant "ERROR". ([#1166]({{ site.repository }}/issues/1166)) +- Rename Jekyll::Logger to Jekyll::Stevenson to fix inheritance issue ([#1106]({{ site.repository }}/issues/1106)) +- Exit with a non-zero exit code when dealing with a Liquid error ([#1121]({{ site.repository }}/issues/1121)) +- Make the `exclude` and `include` options backwards compatible with versions of Jekyll prior to 1.0 ([#1114]({{ site.repository }}/issues/1114)) +- Fix pagination on Windows ([#1063]({{ site.repository }}/issues/1063)) +- Fix the application of Pygments' Generic Output style to Go code ([#1156]({{ site.repository }}/issues/1156)) + +### Site Enhancements +{: #site-enhancements-v1-0-3} + +- Add a Pro Tip to docs about front matter variables being optional ([#1147]({{ site.repository }}/issues/1147)) +- Add changelog to site as History page in /docs/ ([#1065]({{ site.repository }}/issues/1065)) +- Add note to Upgrading page about new config options in 1.0.x ([#1146]({{ site.repository }}/issues/1146)) +- Documentation for `date_to_rfc822` and `uri_escape` ([#1142]({{ site.repository }}/issues/1142)) +- Documentation highlight boxes shouldn't show scrollbars if not necessary ([#1123]({{ site.repository }}/issues/1123)) +- Add link to jekyll-minibundle in the doc's plugins list ([#1035]({{ site.repository }}/issues/1035)) +- Quick patch for importers documentation +- Fix prefix for WordpressDotCom importer in docs ([#1107]({{ site.repository }}/issues/1107)) +- Add jekyll-contentblocks plugin to docs ([#1068]({{ site.repository }}/issues/1068)) +- Make code bits in notes look more natural, more readable ([#1089]({{ site.repository }}/issues/1089)) +- Fix logic for `relative_permalinks` instructions on Upgrading page ([#1101]({{ site.repository }}/issues/1101)) +- Add docs for post excerpt ([#1072]({{ site.repository }}/issues/1072)) +- Add docs for gist tag ([#1072]({{ site.repository }}/issues/1072)) +- Add docs indicating that Pygments does not need to be installed separately ([#1099]({{ site.repository }}/issues/1099), [#1119]({{ site.repository }}/issues/1119)) +- Update the migrator docs to be current ([#1136]({{ site.repository }}/issues/1136)) +- Add the Jekyll Gallery Plugin to the plugin list ([#1143]({{ site.repository }}/issues/1143)) + +### Development Fixes +{: #development-fixes-v1-0-3} + +- Use Jekyll.logger instead of Jekyll::Stevenson to log things ([#1149]({{ site.repository }}/issues/1149)) +- Fix pesky Cucumber infinite loop ([#1139]({{ site.repository }}/issues/1139)) +- Do not write posts with timezones in Cucumber tests ([#1124]({{ site.repository }}/issues/1124)) +- Use ISO formatted dates in Cucumber features ([#1150]({{ site.repository }}/issues/1150)) + + +## 1.0.2 / 2013-05-12 +{: #v1-0-2} + +### Major Enhancements +{: #major-enhancements-v1-0-2} + +- Add `jekyll doctor` command to check site for any known compatibility problems ([#1081]({{ site.repository }}/issues/1081)) +- Backwards-compatibilize relative permalinks ([#1081]({{ site.repository }}/issues/1081)) + +### Minor Enhancements +{: #minor-enhancements-v1-0-2} + +- Add a `data-lang="<lang>"` attribute to Redcarpet code blocks ([#1066]({{ site.repository }}/issues/1066)) +- Deprecate old config `server_port`, match to `port` if `port` isn't set ([#1084]({{ site.repository }}/issues/1084)) +- Update pygments.rb version to 0.5.0 ([#1061]({{ site.repository }}/issues/1061)) +- Update Kramdown version to 1.0.2 ([#1067]({{ site.repository }}/issues/1067)) + +### Bug Fixes +{: #bug-fixes-v1-0-2} + +- Fix issue when categories are numbers ([#1078]({{ site.repository }}/issues/1078)) +- Catching that Redcarpet gem isn't installed ([#1059]({{ site.repository }}/issues/1059)) + +### Site Enhancements +{: #site-enhancements-v1-0-2} + +- Add documentation about `relative_permalinks` ([#1081]({{ site.repository }}/issues/1081)) +- Remove pygments-installation instructions, as pygments.rb is bundled with it ([#1079]({{ site.repository }}/issues/1079)) +- Move pages to be Pages for realz ([#985]({{ site.repository }}/issues/985)) +- Updated links to Liquid documentation ([#1073]({{ site.repository }}/issues/1073)) + + +## 1.0.1 / 2013-05-08 +{: #v1-0-1} + +### Minor Enhancements +{: #minor-enhancements-v1-0-1} + +- Do not force use of `toc_token` when using `generate_tok` in RDiscount ([#1048]({{ site.repository }}/issues/1048)) +- Add newer `language-` class name prefix to code blocks ([#1037]({{ site.repository }}/issues/1037)) +- Commander error message now preferred over process abort with incorrect args ([#1040]({{ site.repository }}/issues/1040)) + +### Bug Fixes +{: #bug-fixes-v1-0-1} + +- Make Redcarpet respect the pygments configuration option ([#1053]({{ site.repository }}/issues/1053)) +- Fix the index build with LSI ([#1045]({{ site.repository }}/issues/1045)) +- Don't print deprecation warning when no arguments are specified. ([#1041]({{ site.repository }}/issues/1041)) +- Add missing `</div>` to site template used by `new` subcommand, fixed typos in code ([#1032]({{ site.repository }}/issues/1032)) + +### Site Enhancements +{: #site-enhancements-v1-0-1} + +- Changed https to http in the GitHub Pages link ([#1051]({{ site.repository }}/issues/1051)) +- Remove CSS cruft, fix typos, fix HTML errors ([#1028]({{ site.repository }}/issues/1028)) +- Removing manual install of Pip and Distribute ([#1025]({{ site.repository }}/issues/1025)) +- Updated URL for Markdown references plugin ([#1022]({{ site.repository }}/issues/1022)) + +### Development Fixes +{: #development-fixes-v1-0-1} + +- Markdownify history file ([#1027]({{ site.repository }}/issues/1027)) +- Update links on README to point to new jekyllrb.com ([#1018]({{ site.repository }}/issues/1018)) + + +## 1.0.0 / 2013-05-06 +{: #v1-0-0} + +### Major Enhancements +{: #major-enhancements-v1-0-0} + +- Add `jekyll new` subcommand: generate a Jekyll scaffold ([#764]({{ site.repository }}/issues/764)) +- Refactored Jekyll commands into subcommands: build, serve, and migrate. ([#690]({{ site.repository }}/issues/690)) +- Removed importers/migrators from main project, migrated to jekyll-import sub-gem ([#793]({{ site.repository }}/issues/793)) +- Added ability to render drafts in `_drafts` folder via command line ([#833]({{ site.repository }}/issues/833)) +- Add ordinal date permalink style (/:categories/:year/:y_day/:title.html) ([#928]({{ site.repository }}/issues/928)) + +### Minor Enhancements +{: #minor-enhancements-v1-0-0} + +- Site template HTML5-ified ([#964]({{ site.repository }}/issues/964)) +- Use post's directory path when matching for the `post_url` tag ([#998]({{ site.repository }}/issues/998)) +- Loosen dependency on Pygments so it's only required when it's needed ([#1015]({{ site.repository }}/issues/1015)) +- Parse strings into Time objects for date-related Liquid filters ([#1014]({{ site.repository }}/issues/1014)) +- Tell the user if there is no subcommand specified ([#1008]({{ site.repository }}/issues/1008)) +- Freak out if the destination of `jekyll new` exists and is non-empty ([#981]({{ site.repository }}/issues/981)) +- Add `timezone` configuration option for compilation ([#957]({{ site.repository }}/issues/957)) +- Add deprecation messages for pre-1.0 CLI options ([#959]({{ site.repository }}/issues/959)) +- Refactor and colorize logging ([#959]({{ site.repository }}/issues/959)) +- Refactor Markdown parsing ([#955]({{ site.repository }}/issues/955)) +- Added application/vnd.apple.pkpass to mime.types served by WEBrick ([#907]({{ site.repository }}/issues/907)) +- Move template site to default markdown renderer ([#961]({{ site.repository }}/issues/961)) +- Expose new attribute to Liquid via `page`: `page.path` ([#951]({{ site.repository }}/issues/951)) +- Accept multiple config files from command line ([#945]({{ site.repository }}/issues/945)) +- Add page variable to liquid custom tags and blocks ([#413]({{ site.repository }}/issues/413)) +- Add `paginator.previous_page_path` and `paginator.next_page_path` ([#942]({{ site.repository }}/issues/942)) +- Backwards compatibility for 'auto' ([#821]({{ site.repository }}/issues/821), [#934]({{ site.repository }}/issues/934)) +- Added date_to_rfc822 used on RSS feeds ([#892]({{ site.repository }}/issues/892)) +- Upgrade version of pygments.rb to 0.4.2 ([#927]({{ site.repository }}/issues/927)) +- Added short month (e.g. "Sep") to permalink style options for posts ([#890]({{ site.repository }}/issues/890)) +- Expose site.baseurl to Liquid templates ([#869]({{ site.repository }}/issues/869)) +- Adds excerpt attribute to posts which contains first paragraph of content ([#837]({{ site.repository }}/issues/837)) +- Accept custom configuration file via CLI ([#863]({{ site.repository }}/issues/863)) +- Load in GitHub Pages MIME Types on `jekyll serve` ([#847]({{ site.repository }}/issues/847), [#871]({{ site.repository }}/issues/871)) +- Improve debuggability of error message for a malformed highlight tag ([#785]({{ site.repository }}/issues/785)) +- Allow symlinked files in unsafe mode ([#824]({{ site.repository }}/issues/824)) +- Add 'gist' Liquid tag to core ([#822]({{ site.repository }}/issues/822), [#861]({{ site.repository }}/issues/861)) +- New format of Jekyll output ([#795]({{ site.repository }}/issues/795)) +- Reinstate `--limit_posts` and `--future` switches ([#788]({{ site.repository }}/issues/788)) +- Remove ambiguity from command descriptions ([#815]({{ site.repository }}/issues/815)) +- Fix SafeYAML Warnings ([#807]({{ site.repository }}/issues/807)) +- Relaxed Kramdown version to 0.14 ([#808]({{ site.repository }}/issues/808)) +- Aliased `jekyll server` to `jekyll serve`. ([#792]({{ site.repository }}/issues/792)) +- Updated gem versions for Kramdown, Rake, Shoulda, Cucumber, and RedCarpet. ([#744]({{ site.repository }}/issues/744)) +- Refactored Jekyll subcommands into Jekyll::Commands submodule, which now contains them ([#768]({{ site.repository }}/issues/768)) +- Rescue from import errors in Wordpress.com migrator ([#671]({{ site.repository }}/issues/671)) +- Massively accelerate LSI performance ([#664]({{ site.repository }}/issues/664)) +- Truncate post slugs when importing from Tumblr ([#496]({{ site.repository }}/issues/496)) +- Add glob support to include, exclude option ([#743]({{ site.repository }}/issues/743)) +- Layout of Page or Post defaults to 'page' or 'post', respectively ([#580]({{ site.repository }}/issues/580)) REPEALED by ([#977]({{ site.repository }}/issues/977)) +- "Keep files" feature ([#685]({{ site.repository }}/issues/685)) +- Output full path & name for files that don't parse ([#745]({{ site.repository }}/issues/745)) +- Add source and destination directory protection ([#535]({{ site.repository }}/issues/535)) +- Better YAML error message ([#718]({{ site.repository }}/issues/718)) +- Bug Fixes +- Paginate in subdirectories properly ([#1016]({{ site.repository }}/issues/1016)) +- Ensure post and page URLs have a leading slash ([#992]({{ site.repository }}/issues/992)) +- Catch all exceptions, not just StandardError descendents ([#1007]({{ site.repository }}/issues/1007)) +- Bullet-proof `limit_posts` option ([#1004]({{ site.repository }}/issues/1004)) +- Read in YAML as UTF-8 to accept non-ASCII chars ([#836]({{ site.repository }}/issues/836)) +- Fix the CLI option `--plugins` to actually accept dirs and files ([#993]({{ site.repository }}/issues/993)) +- Allow 'excerpt' in front matter to override the extracted excerpt ([#946]({{ site.repository }}/issues/946)) +- Fix cascade problem with site.baseurl, site.port and site.host. ([#935]({{ site.repository }}/issues/935)) +- Filter out directories with valid post names ([#875]({{ site.repository }}/issues/875)) +- Fix symlinked static files not being correctly built in unsafe mode ([#909]({{ site.repository }}/issues/909)) +- Fix integration with directory_watcher 1.4.x ([#916]({{ site.repository }}/issues/916)) +- Accepting strings as arguments to jekyll-import command ([#910]({{ site.repository }}/issues/910)) +- Force usage of older directory_watcher gem as 1.5 is broken ([#883]({{ site.repository }}/issues/883)) +- Ensure all Post categories are downcase ([#842]({{ site.repository }}/issues/842), [#872]({{ site.repository }}/issues/872)) +- Force encoding of the rdiscount TOC to UTF8 to avoid conversion errors ([#555]({{ site.repository }}/issues/555)) +- Patch for multibyte URI problem with `jekyll serve` ([#723]({{ site.repository }}/issues/723)) +- Order plugin execution by priority ([#864]({{ site.repository }}/issues/864)) +- Fixed Page#dir and Page#url for edge cases ([#536]({{ site.repository }}/issues/536)) +- Fix broken `post_url` with posts with a time in their front matter ([#831]({{ site.repository }}/issues/831)) +- Look for plugins under the source directory ([#654]({{ site.repository }}/issues/654)) +- Tumblr Migrator: finds `_posts` dir correctly, fixes truncation of long post names ([#775]({{ site.repository }}/issues/775)) +- Force Categories to be Strings ([#767]({{ site.repository }}/issues/767)) +- Safe YAML plugin to prevent vulnerability ([#777]({{ site.repository }}/issues/777)) +- Add SVG support to Jekyll/WEBrick. ([#407]({{ site.repository }}/issues/407), [#406]({{ site.repository }}/issues/406)) +- Prevent custom destination from causing continuous regen on watch ([#528]({{ site.repository }}/issues/528), [#820]({{ site.repository }}/issues/820), [#862]({{ site.repository }}/issues/862)) + +### Site Enhancements +{: #site-enhancements-v1-0-0} + +- Responsify ([#860]({{ site.repository }}/issues/860)) +- Fix spelling, punctuation and phrasal errors ([#989]({{ site.repository }}/issues/989)) +- Update quickstart instructions with `new` command ([#966]({{ site.repository }}/issues/966)) +- Add docs for page.excerpt ([#956]({{ site.repository }}/issues/956)) +- Add docs for page.path ([#951]({{ site.repository }}/issues/951)) +- Clean up site docs to prepare for 1.0 release ([#918]({{ site.repository }}/issues/918)) +- Bring site into master branch with better preview/deploy ([#709]({{ site.repository }}/issues/709)) +- Redesigned site ([#583]({{ site.repository }}/issues/583)) + +### Development Fixes +{: #development-fixes-v1-0-0} + +- Exclude Cucumber 1.2.4, which causes tests to fail in 1.9.2 ([#938]({{ site.repository }}/issues/938)) +- Added "features:html" rake task for debugging purposes, cleaned up Cucumber profiles ([#832]({{ site.repository }}/issues/832)) +- Explicitly require HTTPS rubygems source in Gemfile ([#826]({{ site.repository }}/issues/826)) +- Changed Ruby version for development to 1.9.3-p374 from p362 ([#801]({{ site.repository }}/issues/801)) +- Including a link to the GitHub Ruby style guide in CONTRIBUTING.md ([#806]({{ site.repository }}/issues/806)) +- Added script/bootstrap ([#776]({{ site.repository }}/issues/776)) +- Running Simplecov under 2 conditions: ENV(COVERAGE)=true and with Ruby version of greater than 1.9 ([#771]({{ site.repository }}/issues/771)) +- Switch to Simplecov for coverage report ([#765]({{ site.repository }}/issues/765)) + + +## 0.12.1 / 2013-02-19 +{: #v0-12-1} + +### Minor Enhancements +{: #minor-enhancements-v0-12-1} + +- Update Kramdown version to 0.14.1 ([#744]({{ site.repository }}/issues/744)) +- Test Enhancements +- Update Rake version to 10.0.3 ([#744]({{ site.repository }}/issues/744)) +- Update Shoulda version to 3.3.2 ([#744]({{ site.repository }}/issues/744)) +- Update Redcarpet version to 2.2.2 ([#744]({{ site.repository }}/issues/744)) + + +## 0.12.0 / 2012-12-22 +{: #v0-12-0} + +### Minor Enhancements +{: #minor-enhancements-v0-12-0} + +- Add ability to explicitly specify included files ([#261]({{ site.repository }}/issues/261)) +- Add `--default-mimetype` option ([#279]({{ site.repository }}/issues/279)) +- Allow setting of RedCloth options ([#284]({{ site.repository }}/issues/284)) +- Add `post_url` Liquid tag for internal post linking ([#369]({{ site.repository }}/issues/369)) +- Allow multiple plugin dirs to be specified ([#438]({{ site.repository }}/issues/438)) +- Inline TOC token support for RDiscount ([#333]({{ site.repository }}/issues/333)) +- Add the option to specify the paginated url format ([#342]({{ site.repository }}/issues/342)) +- Swap out albino for pygments.rb ([#569]({{ site.repository }}/issues/569)) +- Support Redcarpet 2 and fenced code blocks ([#619]({{ site.repository }}/issues/619)) +- Better reporting of Liquid errors ([#624]({{ site.repository }}/issues/624)) +- Bug Fixes +- Allow some special characters in highlight names +- URL escape category names in URL generation ([#360]({{ site.repository }}/issues/360)) +- Fix error with `limit_posts` ([#442]({{ site.repository }}/issues/442)) +- Properly select dotfile during directory scan ([#363]({{ site.repository }}/issues/363), [#431]({{ site.repository }}/issues/431), [#377]({{ site.repository }}/issues/377)) +- Allow setting of Kramdown `smart_quotes` ([#482]({{ site.repository }}/issues/482)) +- Ensure front matter is at start of file ([#562]({{ site.repository }}/issues/562)) + + +## 0.11.2 / 2011-12-27 +{: #v0-11-2} + +- Bug Fixes +- Fix gemspec + + +## 0.11.1 / 2011-12-27 +{: #v0-11-1} + +- Bug Fixes +- Fix extra blank line in highlight blocks ([#409]({{ site.repository }}/issues/409)) +- Update dependencies + + +## 0.11.0 / 2011-07-10 +{: #v0-11-0} + +### Major Enhancements +{: #major-enhancements-v0-11-0} + +- Add command line importer functionality ([#253]({{ site.repository }}/issues/253)) +- Add Redcarpet Markdown support ([#318]({{ site.repository }}/issues/318)) +- Make markdown/textile extensions configurable ([#312]({{ site.repository }}/issues/312)) +- Add `markdownify` filter + +### Minor Enhancements +{: #minor-enhancements-v0-11-0} + +- Switch to Albino gem +- Bundler support +- Use English library to avoid hoops ([#292]({{ site.repository }}/issues/292)) +- Add Posterous importer ([#254]({{ site.repository }}/issues/254)) +- Fixes for Wordpress importer ([#274]({{ site.repository }}/issues/274), [#252]({{ site.repository }}/issues/252), [#271]({{ site.repository }}/issues/271)) +- Better error message for invalid post date ([#291]({{ site.repository }}/issues/291)) +- Print formatted fatal exceptions to stdout on build failure +- Add Tumblr importer ([#323]({{ site.repository }}/issues/323)) +- Add Enki importer ([#320]({{ site.repository }}/issues/320)) +- Bug Fixes +- Secure additional path exploits + + +## 0.10.0 / 2010-12-16 +{: #v0-10-0} + +- Bug Fixes +- Add `--no-server` option. + + +## 0.9.0 / 2010-12-15 +{: #v0-9-0} + +### Minor Enhancements +{: #minor-enhancements-v0-9-0} + +- Use OptionParser's `[no-]` functionality for better boolean parsing. +- Add Drupal migrator ([#245]({{ site.repository }}/issues/245)) +- Complain about YAML and Liquid errors ([#249]({{ site.repository }}/issues/249)) +- Remove orphaned files during regeneration ([#247]({{ site.repository }}/issues/247)) +- Add Marley migrator ([#28]({{ site.repository }}/issues/28)) + + +## 0.8.0 / 2010-11-22 +{: #v0-8-0} + +### Minor Enhancements +{: #minor-enhancements-v0-8-0} + +- Add wordpress.com importer ([#207]({{ site.repository }}/issues/207)) +- Add `--limit-posts` cli option ([#212]({{ site.repository }}/issues/212)) +- Add `uri_escape` filter ([#234]({{ site.repository }}/issues/234)) +- Add `--base-url` cli option ([#235]({{ site.repository }}/issues/235)) +- Improve MT migrator ([#238]({{ site.repository }}/issues/238)) +- Add kramdown support ([#239]({{ site.repository }}/issues/239)) +- Bug Fixes +- Fixed filename basename generation ([#208]({{ site.repository }}/issues/208)) +- Set mode to UTF8 on Sequel connections ([#237]({{ site.repository }}/issues/237)) +- Prevent `_includes` dir from being a symlink + + +## 0.7.0 / 2010-08-24 +{: #v0-7-0} + +### Minor Enhancements +{: #minor-enhancements-v0-7-0} + +- Add support for rdiscount extensions ([#173]({{ site.repository }}/issues/173)) +- Bug Fixes +- Highlight should not be able to render local files +- The site configuration may not always provide a 'time' setting ([#184]({{ site.repository }}/issues/184)) + + +## 0.6.2 / 2010-06-25 +{: #v0-6-2} + +- Bug Fixes +- Fix Rakefile 'release' task (tag pushing was missing origin) +- Ensure that RedCloth is loaded when textilize filter is used ([#183]({{ site.repository }}/issues/183)) +- Expand source, destination, and plugin paths ([#180]({{ site.repository }}/issues/180)) +- Fix `page.url` to include full relative path ([#181]({{ site.repository }}/issues/181)) + + +## 0.6.1 / 2010-06-24 +{: #v0-6-1} + +- Bug Fixes +- Fix Markdown Pygments prefix and suffix ([#178]({{ site.repository }}/issues/178)) + + +## 0.6.0 / 2010-06-23 +{: #v0-6-0} + +### Major Enhancements +{: #major-enhancements-v0-6-0} + +- Proper plugin system ([#19]({{ site.repository }}/issues/19), [#100]({{ site.repository }}/issues/100)) +- Add safe mode so unsafe converters/generators can be added +- Maruku is now the only processor dependency installed by default. Other processors will be lazy-loaded when necessary (and prompt the user to install them when necessary) ([#57]({{ site.repository }}/issues/57)) + +### Minor Enhancements +{: #minor-enhancements-v0-6-0} + +- Inclusion/exclusion of future dated posts ([#59]({{ site.repository }}/issues/59)) +- Generation for a specific time ([#59]({{ site.repository }}/issues/59)) +- Allocate `site.time` on render not per site_payload invocation ([#59]({{ site.repository }}/issues/59)) +- Pages now present in the site payload and can be used through the `site.pages` and `site.html_pages` variables +- Generate phase added to site#process and pagination is now a generator +- Switch to RakeGem for build/test process +- Only regenerate static files when they have changed ([#142]({{ site.repository }}/issues/142)) +- Allow arbitrary options to Pygments ([#31]({{ site.repository }}/issues/31)) +- Allow URL to be set via command line option ([#147]({{ site.repository }}/issues/147)) +- Bug Fixes +- Render highlighted code for non markdown/textile pages ([#116]({{ site.repository }}/issues/116)) +- Fix highlighting on Ruby 1.9 ([#65]({{ site.repository }}/issues/65)) +- Fix extension munging when pretty permalinks are enabled ([#64]({{ site.repository }}/issues/64)) +- Stop sorting categories ([#33]({{ site.repository }}/issues/33)) +- Preserve generated attributes over front matter ([#119]({{ site.repository }}/issues/119)) +- Fix source directory binding using `Dir.pwd` ([#75]({{ site.repository }}/issues/75)) + + +## 0.5.7 / 2010-01-12 +{: #v0-5-7} + +### Minor Enhancements +{: #minor-enhancements-v0-5-7} + +- Allow overriding of post date in the front matter ([#62]({{ site.repository }}/issues/62), [#38]({{ site.repository }}/issues/38)) +- Bug Fixes +- Categories isn't always an array ([#73]({{ site.repository }}/issues/73)) +- Empty tags causes error in read_posts ([#84]({{ site.repository }}/issues/84)) +- Fix pagination to adhere to read/render/write paradigm +- Test Enhancement +- Cucumber features no longer use site.posts.first where a better alternative is available + + +## 0.5.6 / 2010-01-08 +{: #v0-5-6} + +- Bug Fixes +- Require redcloth >= 4.2.1 in tests ([#92]({{ site.repository }}/issues/92)) +- Don't break on triple dashes in front matter ([#93]({{ site.repository }}/issues/93)) + +### Minor Enhancements +{: #minor-enhancements-v0-5-6} + +- Allow .mkd as markdown extension +- Use $stdout/err instead of constants ([#99]({{ site.repository }}/issues/99)) +- Properly wrap code blocks ([#91]({{ site.repository }}/issues/91)) +- Add javascript mime type for webrick ([#98]({{ site.repository }}/issues/98)) + + +## 0.5.5 / 2010-01-08 +{: #v0-5-5} + +- Bug Fixes +- Fix pagination % 0 bug ([#78]({{ site.repository }}/issues/78)) +- Ensure all posts are processed first ([#71]({{ site.repository }}/issues/71)) +- After this point I will no longer be giving credit in the history; that is what the commit log is for. + + +## 0.5.4 / 2009-08-23 +{: #v0-5-4} + +- Bug Fixes +- Do not allow symlinks (security vulnerability) + + +## 0.5.3 / 2009-07-14 +{: #v0-5-3} + +- Bug Fixes +- Solving the permalink bug where non-html files wouldn't work (@jeffrydegrande) + + +## 0.5.2 / 2009-06-24 +{: #v0-5-2} + +- Enhancements +- Added --paginate option to the executable along with a paginator object for the payload (@calavera) +- Upgraded RedCloth to 4.2.1, which makes `<notextile>` tags work once again. +- Configuration options set in config.yml are now available through the site payload (@vilcans) +- Posts can now have an empty front matter or none at all (@ bahuvrihi) +- Bug Fixes +- Fixing Ruby 1.9 issue that requires `#to_s` on the err object (@Chrononaut) +- Fixes for pagination and ordering posts on the same day (@ujh) +- Made pages respect permalinks style and permalinks in yml front matter (@eugenebolshakov) +- Index.html file should always have index.html permalink (@eugenebolshakov) +- Added trailing slash to pretty permalink style so Apache is happy (@eugenebolshakov) +- Bad markdown processor in config fails sooner and with better message (@ gcnovus) +- Allow CRLFs in front matter (@juretta) +- Added Date#xmlschema for Ruby versions < 1.9 + + +## 0.5.1 / 2009-05-06 +{: #v0-5-1} + +### Major Enhancements +{: #major-enhancements-v0-5-1} + +- Next/previous posts in site payload (@pantulis, @tomo) +- Permalink templating system +- Moved most of the README out to the GitHub wiki +- Exclude option in configuration so specified files won't be brought over with generated site (@duritong) +- Bug Fixes +- Making sure config.yaml references are all gone, using only config.yml +- Fixed syntax highlighting breaking for UTF-8 code (@henrik) +- Worked around RDiscount bug that prevents Markdown from getting parsed after highlight (@henrik) +- CGI escaped post titles (@Chrononaut) + + +## 0.5.0 / 2009-04-07 +{: #v0-5-0} + +### Minor Enhancements +{: #minor-enhancements-v0-5-0} + +- Ability to set post categories via YAML (@qrush) +- Ability to set prevent a post from publishing via YAML (@qrush) +- Add textilize filter (@willcodeforfoo) +- Add 'pretty' permalink style for wordpress-like urls (@dysinger) +- Made it possible to enter categories from YAML as an array (@Chrononaut) +- Ignore Emacs autosave files (@Chrononaut) +- Bug Fixes +- Use block syntax of popen4 to ensure that subprocesses are properly disposed (@jqr) +- Close open4 streams to prevent zombies (@rtomayko) +- Only query required fields from the WP Database (@ariejan) +- Prevent `_posts` from being copied to the destination directory (@bdimcheff) +- Refactors +- Factored the filtering code into a method (@Chrononaut) +- Fix tests and convert to Shoulda (@qrush, @technicalpickles) +- Add Cucumber acceptance test suite (@qrush, @technicalpickles) + + +## 0.4.1 + +### Minor Enhancements +{: #minor-enhancements-v--} + +- Changed date format on wordpress converter (zeropadding) (@dysinger) +- Bug Fixes +- Add Jekyll binary as executable to gemspec (@dysinger) + + +## 0.4.0 / 2009-02-03 +{: #v0-4-0} + +### Major Enhancements +{: #major-enhancements-v0-4-0} + +- Switch to Jeweler for packaging tasks + +### Minor Enhancements +{: #minor-enhancements-v0-4-0} + +- Type importer (@codeslinger) +- `site.topics` accessor (@baz) +- Add `array_to_sentence_string` filter (@mchung) +- Add a converter for textpattern (@PerfectlyNormal) +- Add a working Mephisto / MySQL converter (@ivey) +- Allowing .htaccess files to be copied over into the generated site (@briandoll) +- Add option to not put file date in permalink URL (@mreid) +- Add line number capabilities to highlight blocks (@jcon) +- Bug Fixes +- Fix permalink behavior (@cavalle) +- Fixed an issue with pygments, markdown, and newlines (@zpinter) +- Ampersands need to be escaped (@pufuwozu, @ap) +- Test and fix the site.categories hash (@zzot) +- Fix site payload available to files (@matrix9180) + + +## 0.3.0 / 2008-12-24 +{: #v0-3-0} + +### Major Enhancements +{: #major-enhancements-v0-3-0} + +- Added `--server` option to start a simple WEBrick server on destination directory (@johnreilly and @mchung) + +### Minor Enhancements +{: #minor-enhancements-v0-3-0} + +- Added post categories based on directories containing `_posts` (@mreid) +- Added post topics based on directories underneath `_posts` +- Added new date filter that shows the full month name (@mreid) +- Merge Post's front matter into its to_liquid payload (@remi) +- Restrict includes to regular files underneath `_includes` +- Bug Fixes +- Change YAML delimiter matcher so as to not chew up 2nd level markdown headers (@mreid) +- Fix bug that meant page data (such as the date) was not available in templates (@mreid) +- Properly reject directories in `_layouts` + + +## 0.2.1 / 2008-12-15 +{: #v0-2-1} + +- Major Changes +- Use Maruku (pure Ruby) for Markdown by default (@mreid) +- Allow use of RDiscount with `--rdiscount` flag + +### Minor Enhancements +{: #minor-enhancements-v0-2-1} + +- Don't load directory_watcher unless it's needed (@pjhyett) + + +## 0.2.0 / 2008-12-14 +{: #v0-2-0} + +- Major Changes +- related_posts is now found in `site.related_posts` + + +## 0.1.6 / 2008-12-13 +{: #v0-1-6} + +- Major Features +- Include files in `_includes` with {% raw %}`{% include x.textile %}`{% endraw %} + + +## 0.1.5 / 2008-12-12 +{: #v0-1-5} + +### Major Enhancements +{: #major-enhancements-v0-1-5} + +- Code highlighting with Pygments if `--pygments` is specified +- Disable true LSI by default, enable with `--lsi` + +### Minor Enhancements +{: #minor-enhancements-v0-1-5} + +- Output informative message if RDiscount is not available (@JackDanger) +- Bug Fixes +- Prevent Jekyll from picking up the output directory as a source (@JackDanger) +- Skip `related_posts` when there is only one post (@JackDanger) + + +## 0.1.4 / 2008-12-08 +{: #v0-1-4} + +- Bug Fixes +- DATA does not work properly with rubygems + + +## 0.1.3 / 2008-12-06 +{: #v0-1-3} + +- Major Features +- Markdown support (@vanpelt) +- Mephisto and CSV converters (@vanpelt) +- Code hilighting (@vanpelt) +- Autobuild +- Bug Fixes +- Accept both `\r\n` and `\n` in YAML header (@vanpelt) + + +## 0.1.2 / 2008-11-22 +{: #v0-1-2} + +- Major Features +- Add a real "related posts" implementation using Classifier +- Command Line Changes +- Allow cli to be called with 0, 1, or 2 args intuiting dir paths if they are omitted + + +## 0.1.1 / 2008-11-22 +{: #v0-1-1} + +- Minor Additions +- Posts now support introspectional data e.g. {% raw %}`{{ page.url }}`{% endraw %} + + +## 0.1.0 / 2008-11-05 +{: #v0-1-0} + +- First release +- Converts posts written in Textile +- Converts regular site pages +- Simple copy of binary files + + +## 0.0.0 / 2008-10-19 +{: #v0-0-0} + +- Birthday! diff --git a/docs/_docs/includes.md b/docs/_docs/includes.md new file mode 100644 index 0000000..8409942 --- /dev/null +++ b/docs/_docs/includes.md @@ -0,0 +1,156 @@ +--- +title: Includes +permalink: /docs/includes/ +--- + +The `include` tag allows you to include the content from another file stored in the `_includes` folder: + +{% raw %} +```liquid +{% include footer.html %} +``` +{% endraw %} + +Jekyll will look for the referenced file (in this case, `footer.html`) in the `_includes` directory at the root of your source directory and insert its contents. + +### Including files relative to another file + +You can choose to include file fragments relative to the current file by using the `include_relative` tag: + +{% raw %} +```liquid +{% include_relative somedir/footer.html %} +``` +{% endraw %} + +You won't need to place your included content within the `_includes` directory. Instead, +the inclusion is specifically relative to the file where the tag is being used. For example, +if `_posts/2014-09-03-my-file.markdown` uses the `include_relative` tag, the included file +must be within the `_posts` directory or one of its subdirectories. + +Note that you cannot use the `../` syntax to specify an include location that refers to a higher-level directory. + +All the other capabilities of the `include` tag are available to the `include_relative` tag, +such as variables. + +### Using variables names for the include file + +The name of the file you want to embed can be specified as a variable instead of an actual file name. For example, suppose you defined a variable in your page's front matter like this: + +```yaml +--- +title: My page +my_variable: footer_company_a.html +--- +``` + +You could then reference that variable in your include: + +{% raw %} +```liquid +{% if page.my_variable %} + {% include {{ page.my_variable }} %} +{% endif %} +``` +{% endraw %} + +In this example, the include would insert the file `footer_company_a.html` from the `_includes/footer_company_a.html` directory. + +### Passing parameters to includes + +You can also pass parameters to an include. For example, suppose you have a file called `note.html` in your `_includes` folder that contains this formatting: + +{% raw %} +```liquid +<div markdown="span" class="alert alert-info" role="alert"> +<i class="fa fa-info-circle"></i> <b>Note:</b> +{{ include.content }} +</div> +``` +{% endraw %} + +The {% raw %}`{{ include.content }}`{% endraw %} is a parameter that gets populated when you call the include and specify a value for that parameter, like this: + +{% raw %} +```liquid +{% include note.html content="This is my sample note." %} +``` +{% endraw %} + +The value of `content` (which is `This is my sample note`) will be inserted into the {% raw %}`{{ include.content }}`{% endraw %} parameter. + +Passing parameters to includes is especially helpful when you want to hide away complex formatting from your Markdown content. + +For example, suppose you have a special image syntax with complex formatting, and you don't want your authors to remember the complex formatting. As a result, you decide to simplify the formatting by using an include with parameters. Here's an example of the special image syntax you might want to populate with an include: + +```html +<figure> + <a href="https://jekyllrb.com"> + <img src="logo.png" style="max-width: 200px;" + alt="Jekyll logo" /> + </a> + <figcaption>This is the Jekyll logo</figcaption> +</figure> +``` + +You could templatize this content in your include and make each value available as a parameter, like this: + +{% raw %} +```liquid +<figure> + <a href="{{ include.url }}"> + <img src="{{ include.file }}" style="max-width: {{ include.max-width }};" + alt="{{ include.alt }}"/> + </a> + <figcaption>{{ include.caption }}</figcaption> +</figure> +``` +{% endraw %} + +This include contains 5 parameters: + +* `url` +* `max-width` +* `file` +* `alt` +* `caption` + +Here's an example that passes all the parameters to this include (the include file is named `image.html`): + +{% raw %} +```liquid +{% include image.html url="http://jekyllrb.com" +max-width="200px" file="logo.png" alt="Jekyll logo" +caption="This is the Jekyll logo." %} +``` +{% endraw %} + +The result is the original HTML code shown earlier. + +To safeguard situations where users don't supply a value for the parameter, you can use [Liquid's default filter](https://shopify.github.io/liquid/filters/default/). + +Overall, you can create includes that act as templates for a variety of uses — inserting audio or video clips, alerts, special formatting, and more. Note that you should avoid using too many includes, as this will slow down the build time of your site. For example, don't use includes every time you insert an image. (The above technique shows a use case for special images.) + +### Passing parameter variables to includes + +Suppose the parameter you want to pass to the include is a variable rather than a string. For example, you might be using {% raw %}`{{ site.product_name }}`{% endraw %} to refer to every instance of your product rather than the actual hard-coded name. (In this case, your `_config.yml` file would have a key called `product_name` with a value of your product's name.) + +The string you pass to your include parameter can't contain curly braces. For example, you can't pass a parameter that contains this: {% raw %}`"The latest version of {{ site.product_name }} is now available."`{% endraw %} + +If you want to include this variable in your parameter that you pass to an include, you need to store the entire parameter as a variable before passing it to the include. You can use `capture` tags to create the variable: + +{% raw %} +```liquid +{% capture download_note %} +The latest version of {{ site.product_name }} is now available. +{% endcapture %} +``` +{% endraw %} + +Then pass this captured variable into the parameter for the include. Omit the quotation marks around the parameter content because it's no longer a string (it's a variable): + +{% raw %} +```liquid +{% include note.html content=download_note %} +``` +{% endraw %} diff --git a/docs/_docs/index.md b/docs/_docs/index.md new file mode 100644 index 0000000..83c3e57 --- /dev/null +++ b/docs/_docs/index.md @@ -0,0 +1,55 @@ +--- +title: Quickstart +permalink: /docs/ +redirect_from: + - /docs/home/ + - /docs/quickstart/ + - /docs/extras/ +--- +Jekyll is a static site generator. It takes text written in your +favorite markup language and uses layouts to create a static website. You can +tweak the site's look and feel, URLs, the data displayed on the page, and more. + +## Prerequisites + +Jekyll requires the following: + +* Ruby version **{{ site.data.ruby.min_version }}** or higher +* RubyGems +* GCC and Make + +See [Requirements]({{ '/docs/installation/#requirements' | relative_url }}) for guides and details. + +## Instructions + +1. Install all [prerequisites]({{ '/docs/installation/' | relative_url }}). +2. Install the jekyll and bundler [gems]({{ '/docs/ruby-101/#gems' | relative_url }}). +```sh +gem install jekyll bundler +``` +3. Create a new Jekyll site at `./myblog`. +```sh +jekyll new myblog +``` +4. Change into your new directory. +```sh +cd myblog +``` +5. Build the site and make it available on a local server. +```sh +bundle exec jekyll serve +``` +6. Browse to [http://localhost:4000](http://localhost:4000){:target="_blank"} + +{: .note .warning} +If you are using Ruby version 3.0.0 or higher, step 5 [may fail](https://github.com/github/pages-gem/issues/752). You may fix it by adding `webrick` to your dependencies: `bundle add webrick` + +{: .note .info} +Pass the `--livereload` option to `serve` to automatically refresh the page with each change you make to the source files: `bundle exec jekyll serve --livereload` + + +If you encounter any errors during this process, check that you have installed all the prerequisites in [Requirements]({{ '/docs/installation/#requirements' | relative_url }}). +If you still have issues, see [Troubleshooting]({{ '/docs/troubleshooting/#configuration-problems' | relative_url }}). + +{: .note .info} +Installation varies based on your operating system. See our [guides]({{ '/docs/installation/#guides' | relative_url }}) for OS-specific instructions. diff --git a/docs/_docs/installation.md b/docs/_docs/installation.md new file mode 100644 index 0000000..5009857 --- /dev/null +++ b/docs/_docs/installation.md @@ -0,0 +1,22 @@ +--- +title: Installation +description: Official guide to install Jekyll on macOS, GNU/Linux or Windows. +permalink: /docs/installation/ +--- + +Jekyll is a [Ruby Gem]({{ '/docs/ruby-101/#gems' | relative_url }}) that can be installed on most systems. + +## Requirements + +* [Ruby](https://www.ruby-lang.org/en/downloads/) version **{{ site.data.ruby.min_version }}** or higher, including all development headers (check your Ruby version using `ruby -v`) +* [RubyGems](https://rubygems.org/pages/download) (check your Gems version using `gem -v`) +* [GCC](https://gcc.gnu.org/install/) and [Make](https://www.gnu.org/software/make/) (check versions using `gcc -v`,`g++ -v`, and `make -v`) + +## Guides + +For detailed install instructions, follow the guide for your operating system. + +* [macOS]({{ '/docs/installation/macos/' | relative_url }}) +* [Ubuntu]({{ '/docs/installation/ubuntu/' | relative_url }}) +* [Other Linux]({{ '/docs/installation/other-linux/' | relative_url }}) +* [Windows]({{ '/docs/installation/windows/' | relative_url }}) \ No newline at end of file diff --git a/docs/_docs/installation/macos.md b/docs/_docs/installation/macos.md new file mode 100644 index 0000000..d445277 --- /dev/null +++ b/docs/_docs/installation/macos.md @@ -0,0 +1,94 @@ +--- +title: Jekyll on macOS +permalink: /docs/installation/macos/ +--- + +## Supported macOS versions + +- Monterey (macOS 12) +- Big Sur (macOS 11) +- Catalina (macOS 10.15) + +Older macOS versions might work, but we don't officially support them. + +## Install Ruby + +To install Jekyll on macOS, you need a proper Ruby development environment. +While macOS comes preinstalled with Ruby, we don't recommend using that version +to install Jekyll. This external article goes over the various reasons +[why you shouldn't use the system Ruby](https://www.moncefbelyamani.com/why-you-shouldn-t-use-the-system-ruby-to-install-gems-on-a-mac/). + +Instead, you'll need to install a separate and newer version of Ruby using a +version manager such as [asdf], [chruby], [rbenv], or [rvm]. Version managers +allow you to easily install multiple versions of Ruby, and switch between them. + +We recommend `chruby` because it's the simplest and least likely to cause issues. + +The instructions below are an excerpt from this detailed external guide to +[install Ruby on Mac]. They work best if you're setting up development tools +for the first time on your Mac. If you've already tried to install Ruby or +Jekyll on your Mac, or if you run into any issues, read that guide. + +[asdf]: https://asdf-vm.com/ +[chruby]: https://github.com/postmodern/chruby +[rbenv]: https://github.com/rbenv/rbenv +[rvm]: https://rvm.io/ +[install Ruby on Mac]: https://www.moncefbelyamani.com/how-to-install-xcode-homebrew-git-rvm-ruby-on-mac/ + +### Step 1: Install Homebrew + +[Homebrew](https://brew.sh/) makes it easy to install development tools on a Mac. + +```sh +/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" +``` + +### Step 2: Install chruby and the latest Ruby with ruby-install + +Install `chruby` and `ruby-install` with Homebrew: + +```sh +brew install chruby ruby-install xz +``` + +Install the latest stable version of Ruby: + +```sh +ruby-install ruby +``` + +This will take a few minutes, and once it's done, configure your shell to +automatically use `chruby`: + +```sh +echo "source $(brew --prefix)/opt/chruby/share/chruby/chruby.sh" >> ~/.zshrc +echo "source $(brew --prefix)/opt/chruby/share/chruby/auto.sh" >> ~/.zshrc +echo "chruby ruby-{{ site.data.ruby.current_version }}" >> ~/.zshrc # run 'chruby' to see actual version +``` + +If you're using Bash, replace `.zshrc` with `.bash_profile`. If you're not sure, +read this external guide to +[find out which shell you're using](https://www.moncefbelyamani.com/which-shell-am-i-using-how-can-i-switch/). + +Quit and relaunch Terminal, then check that everything is working: + +```sh +ruby -v +``` + +It should show {{ site.data.ruby.current_version_output }} or a newer version. + +Next, read that same external guide for important notes about +[setting and switching between Ruby versions with chruby](https://www.moncefbelyamani.com/how-to-install-xcode-homebrew-git-rvm-ruby-on-mac/#how-to-install-different-versions-of-ruby-and-switch-between-them). + +## Install Jekyll + +After installing Ruby with chruby, install the latest Jekyll gem: + +```sh +gem install jekyll +``` + +## Troubleshooting + +See [Troubleshooting]({{ '/docs/troubleshooting/' | relative_url }}) or [ask for help on our forum](https://talk.jekyllrb.com). diff --git a/docs/_docs/installation/other-linux.md b/docs/_docs/installation/other-linux.md new file mode 100644 index 0000000..b6420af --- /dev/null +++ b/docs/_docs/installation/other-linux.md @@ -0,0 +1,60 @@ +--- +title: Jekyll on Linux +permalink: /docs/installation/other-linux/ +--- + +Installation on other Linux distributions works similarly to installing on [Ubuntu](../ubuntu/). + +## Install prerequisites + +### Fedora + +```sh +sudo dnf install ruby ruby-devel openssl-devel redhat-rpm-config @development-tools +``` +### RHEL8/CentOS8 + +```sh +sudo dnf install ruby ruby-devel +sudo dnf group install "Development Tools" +``` + +### Debian + +```sh +sudo apt-get install ruby-full build-essential +``` + +### Gentoo + +```sh +sudo emerge -av jekyll +``` + +or + +```sh +sudo emerge --ask --verbose jekyll +``` + +### ArchLinux + +```sh +sudo pacman -S ruby base-devel +``` + +### OpenSUSE + +```sh +sudo zypper install -t pattern devel_ruby devel_C_C++ +sudo zypper install ruby-devel +``` + +### Clear Linux + +```sh +sudo swupd bundle-add ruby-basic +``` +## Install Jekyll + +Follow the instructions for [Ubuntu](../ubuntu/). diff --git a/docs/_docs/installation/ubuntu.md b/docs/_docs/installation/ubuntu.md new file mode 100644 index 0000000..a706a6c --- /dev/null +++ b/docs/_docs/installation/ubuntu.md @@ -0,0 +1,32 @@ +--- +title: Jekyll on Ubuntu +permalink: /docs/installation/ubuntu/ +--- + +## Install dependencies + +Install Ruby and other [prerequisites]({{ '/docs/installation/#requirements' | relative_url }}): + +```sh +sudo apt-get install ruby-full build-essential zlib1g-dev +``` + +Avoid installing RubyGems packages (called gems) as the root user. Instead, +set up a gem installation directory for your user account. The following +commands will add environment variables to your `~/.bashrc` file to configure +the gem installation path: + +```sh +echo '# Install Ruby Gems to ~/gems' >> ~/.bashrc +echo 'export GEM_HOME="$HOME/gems"' >> ~/.bashrc +echo 'export PATH="$HOME/gems/bin:$PATH"' >> ~/.bashrc +source ~/.bashrc +``` + +Finally, install Jekyll and Bundler: + +```sh +gem install jekyll bundler +``` + +That's it! You're ready to start using Jekyll. diff --git a/docs/_docs/installation/windows.md b/docs/_docs/installation/windows.md new file mode 100644 index 0000000..6e3e5b1 --- /dev/null +++ b/docs/_docs/installation/windows.md @@ -0,0 +1,145 @@ +--- +title: Jekyll on Windows +permalink: /docs/installation/windows/ +redirect_from: + - /docs/windows/ +--- + +While Windows is not an officially-supported platform, it can be used to run Jekyll with the proper tweaks. + +## Installing Ruby and Jekyll + +### Installation via RubyInstaller + +The easiest way to install Ruby and Jekyll is by using the [RubyInstaller](https://rubyinstaller.org/) for Windows. + +RubyInstaller is a self-contained Windows-based installer that includes the Ruby language, an execution environment, +important documentation, and more. + +We only cover RubyInstaller-2.4 and newer here. Older versions need to +[install the Devkit](https://github.com/oneclick/rubyinstaller/wiki/Development-Kit) manually. + +1. Download and install a **Ruby+Devkit** version from [RubyInstaller Downloads](https://rubyinstaller.org/downloads/). + Use default options for installation. +2. Run the `ridk install` step on the last stage of the installation wizard. This is needed for installing gems with native + extensions. You can find additional information regarding this in the + [RubyInstaller Documentation](https://github.com/oneclick/rubyinstaller2#using-the-installer-on-a-target-system). + From the options choose `MSYS2 and MINGW development tool chain`. +3. Open a new command prompt window from the start menu, so that changes to the `PATH` environment variable becomes effective. + Install Jekyll and Bundler using `gem install jekyll bundler` +4. Check if Jekyll has been installed properly: `jekyll -v` + +{: .note .info} +You may receive an error when checking if Jekyll has not been installed properly. Reboot your system and run `jekyll -v` again. +If the error persists, please open a [RubyInstaller issue](https://github.com/oneclick/rubyinstaller2/issues/new). + +That's it, you're ready to use Jekyll! + +### Installation via Bash on Windows 10 + +If you are using Windows 10 version 1607 or later, another option to run Jekyll is by +[installing](https://msdn.microsoft.com/en-us/commandline/wsl/install_guide) the Windows Subsystem for Linux. + +{: .note .info} +You must have [Windows Subsystem for Linux](https://msdn.microsoft.com/en-us/commandline/wsl/about) enabled. + +Make sure all your packages and repositories are up to date. Open a new Command Prompt or PowerShell window and type `bash`. + +Your terminal should now be a Bash instance. Next, update your repository lists and packages: + +```sh +sudo apt-get update -y && sudo apt-get upgrade -y +``` + +Next, install Ruby. To do this, let's use a repository from [BrightBox](https://www.brightbox.com/docs/ruby/ubuntu/), +which hosts optimized versions of Ruby for Ubuntu. + +```sh +sudo apt-add-repository ppa:brightbox/ruby-ng +sudo apt-get update +sudo apt-get install ruby2.5 ruby2.5-dev build-essential dh-autoreconf +``` + +Next, update your Ruby gems: + +```sh +gem update +``` + +Install Jekyll: + +```sh +gem install jekyll bundler +``` + +{: .note .info} + No `sudo` here. + +Check your Jekyll version: + +```sh +jekyll -v +``` + +That's it! You're ready to start using Jekyll. + +You can make sure time management is working properly by inspecting your `_posts` folder. You should see a markdown file +with the current date in the filename. + +<div class="note info"> + <h5>Non-superuser account issues</h5> + <p>If the `jekyll new` command prints the error "Your user account isn't allowed to install to the system RubyGems", see + the "Running Jekyll as Non-Superuser" instructions in + <a href="{{ '/docs/troubleshooting/#no-sudo' | relative_url }}">Troubleshooting</a>.</p> +</div> + +{: .note .info} +Bash on Ubuntu on Windows is still under development, so you may run into issues. + +## Encoding + +If you use UTF-8 encoding, Jekyll will break if a file starts with characters representing a [BOM](https://en.wikipedia.org/wiki/Byte_order_mark#UTF-8). Therefore, remove this sequence of bytes if it appears at the beginning of your file. + +Additionally, you might need to change the code page of the console window to UTF-8 in case you get a +`Liquid Exception: Incompatible character encoding` error during the site generation process. Run the following: + +```sh +chcp 65001 +``` + +## Time Zone Management + +Since Windows doesn't have a native source of zoneinfo data, the Ruby Interpreter doesn't understand IANA Timezones. +Using them had the `TZ` environment variable default to UTC/GMT 00:00. + +Though Windows users could alternatively define their blog's timezone by setting the key to use the POSIX format of defining +timezones, it wasn't as user-friendly when it came to having the clock altered to changing DST-rules. + +Jekyll now uses a rubygem to internally configure Timezone based on established +[IANA Timezone Database](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones). + +While 'new' blogs created with Jekyll v3.4 and greater, will have the following added to their `Gemfile` by default, existing +sites *will* have to update their `Gemfile` (and installed gems) to enable development on Windows: + +```ruby +# Windows and JRuby does not include zoneinfo files, so bundle the tzinfo-data gem +# and associated library. +platforms :mingw, :x64_mingw, :mswin, :jruby do + gem "tzinfo", ">= 1", "< 3" + gem "tzinfo-data" +end +``` + +## Auto Regeneration + +Jekyll uses the `listen` gem to watch for changes when the `--watch` switch is specified during a build or serve. +While `listen` has built-in support for UNIX systems, it may require an extra gem for compatibility with Windows. + +Add the following to the `Gemfile` for your site if you have issues with auto-regeneration on Windows alone: + +```ruby +gem 'wdm', '~> 0.1.1', :install_if => Gem.win_platform? +``` + +You have to use a [Ruby+Devkit](https://rubyinstaller.org/downloads/) version of the RubyInstaller and install +the MSYS2 build tools to successfully install the `wdm` gem. diff --git a/docs/_docs/layouts.md b/docs/_docs/layouts.md new file mode 100644 index 0000000..1ef7c2a --- /dev/null +++ b/docs/_docs/layouts.md @@ -0,0 +1,141 @@ +--- +title: Layouts +description: placeholder +permalink: /docs/layouts/ +--- +Layouts are templates that wrap around your content. They allow you to have the +source code for your template in one place so you don't have to repeat things +like your navigation and footer on every page. + +Layouts live in the `_layouts` directory. The convention is to have a base +template called `default.html` and have other layouts [inherit](#inheritance) +from this as needed. + +<div class="note"> + <h5>Layouts Directory</h5> + <p> + Jekyll looks for the <code>_layouts</code> directory either at the root of + your site's <code>source</code> or at the root of your theme. + </p> + <p> + While you can configure the directory name in which your layouts can reside by + setting the <code>layouts_dir</code> key in your config file, the directory + itself should be located at the root of your site's <code>source</code> directory. + </p> +</div> + +## Usage + +The first step is to put the template source code in `default.html`. `content` +is a special variable, the value is the rendered content of the post or page +being wrapped. + +{% raw %} +```liquid +<!doctype html> +<html lang="en"> + <head> + <meta charset="utf-8"> + <title>{{ page.title }}</title> + <link rel="stylesheet" href="/css/style.css"> + </head> + <body> + <nav> + <a href="/">Home</a> + <a href="/blog/">Blog</a> + </nav> + <h1>{{ page.title }}</h1> + <section> + {{ content }} + </section> + <footer> + © to me + </footer> + </body> +</html> +``` +{% endraw %} + +You have full access to the front matter of the origin. In the +example above, `page.title` comes from the page front matter. + +Next you need to specify what layout you're using in your page's front matter. +You can also use +[front matter defaults](/docs/configuration/front-matter-defaults/) to save you +from having to set this on every page. + +```markdown +--- +title: My First Page +layout: default +--- + +This is the content of my page +``` + +The rendered output of this page is: + +```html +<!doctype html> +<html lang="en"> + <head> + <meta charset="utf-8"> + <title>My First Page</title> + <link rel="stylesheet" href="/css/style.css"> + </head> + <body> + <nav> + <a href="/">Home</a> + <a href="/blog/">Blog</a> + </nav> + <h1>My First Page</h1> + <section> + This is the content of my page + </section> + <footer> + © to me + </footer> + </body> +</html> +``` + +## Inheritance + +Layout inheritance is useful when you want to add something to an existing +layout for a portion of documents on your site. A common example of this is +blog posts, you might want a post to display the date and author but otherwise +be identical to your base layout. + +To achieve this you need to create another layout which specifies your original +layout in front matter. For example this layout will live at +`_layouts/post.html`: + +{% raw %} +```liquid +--- +layout: default +--- +<p>{{ page.date }} - Written by {{ page.author }}</p> + +{{ content }} +``` +{% endraw %} + +Now posts can use this layout while the rest of the pages use the default. + +## Variables + +You can set front matter in layouts, the only difference is when you're +using in Liquid, you need to use the `layout` variable instead of `page`. For +example: + +{% raw %} +```liquid +--- +city: San Francisco +--- +<p>{{ layout.city }}</p> + +{{ content }} +``` +{% endraw %} diff --git a/docs/_docs/liquid.md b/docs/_docs/liquid.md new file mode 100644 index 0000000..9dead2b --- /dev/null +++ b/docs/_docs/liquid.md @@ -0,0 +1,19 @@ +--- +title: Liquid +permalink: /docs/liquid/ +redirect_from: "/docs/templates/" +--- + +Jekyll uses the [Liquid](https://shopify.github.io/liquid/) templating language +to process templates. + +Generally in Liquid you output content using two curly braces e.g. +{% raw %}`{{ variable }}`{% endraw %} and perform logic statements by +surrounding them in a curly brace percentage sign e.g. +{% raw %}`{% if statement %}`{% endraw %}. To learn more about Liquid, check +out the [official Liquid Documentation](https://shopify.github.io/liquid/). + +Jekyll provides a number of useful Liquid additions to help you build your site: + +* [Filters]({{ '/docs/liquid/filters/' | relative_url }}) +* [Tags]({{ '/docs/liquid/tags/' | relative_url }}) diff --git a/docs/_docs/liquid/filters.md b/docs/_docs/liquid/filters.md new file mode 100644 index 0000000..26609b8 --- /dev/null +++ b/docs/_docs/liquid/filters.md @@ -0,0 +1,154 @@ +--- +title: Liquid Filters +permalink: "/docs/liquid/filters/" +shopify_filter_url: https://shopify.github.io/liquid/filters/ +shopify_filters: +- abs +- append +- at_least +- at_most +- capitalize +- ceil +- compact +- concat +- date +- default +- divided_by +- downcase +- escape +- escape_once +- first +- floor +- join +- last +- lstrip +- map +- minus +- modulo +- newline_to_br +- plus +- prepend +- remove +- remove_first +- replace +- replace_first +- reverse +- round +- rstrip +- size +- slice +- sort +- sort_natural +- split +- strip +- strip_html +- strip_newlines +- times +- truncate +- truncatewords +- uniq +- upcase +- url_decode +- url_encode +--- + +All of the standard Liquid [filters](#standard-liquid-filters) are supported (see below). + +To make common tasks easier, Jekyll even adds a few handy filters of its own, +all of which you can find on this page. You can also create your own filters +using [plugins](/docs/plugins/). + +<div class="mobile-side-scroller"> +<table> + <thead> + <tr> + <th>Description</th> + <th><span class="filter">Filter</span> and <span class="output">Output</span></th> + </tr> + </thead> + <tbody> + {% for filter in site.data.jekyll_filters %} + <tr> + <td> + <p id="{{ filter.name | slugify }}" class="name"><strong>{{ filter.name }}</strong></p> + <p> + {{- filter.description -}} + {%- if filter.version_badge %} + <span class="version-badge" title="This filter is available from version {{ filter.version_badge }}"> + {{- filter.version_badge -}} + </span> + {% endif -%} + </p> + </td> + <td class="align-center"> + {%- for example in filter.examples %} + <p><code class="filter">{{ example.input }}</code></p> + {% if example.output %}<p><code class="output">{{ example.output }}</code></p>{% endif %} + {% endfor -%} + </td> + </tr> + {% endfor %} + </tbody> +</table> +</div> + +### Options for the `slugify` filter + +The `slugify` filter accepts an option, each specifying what to filter. +The default is `default`. They are as follows (with what they filter): + +- `none`: no characters +- `raw`: spaces +- `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`) {%- include docs_version_badge.html version="3.7.0" -%}. + +### Detecting `nil` values with `where` filter {%- include docs_version_badge.html version="4.0" -%} + +You can use the `where` filter to detect documents and pages with properties that are `nil` or `""`. For example, + +{% raw %} +```liquid +// Using `nil` to select posts that either do not have `my_prop` +// defined or `my_prop` has been set to `nil` explicitly. +{% assign filtered_posts = site.posts | where: 'my_prop', nil %} +``` +{% endraw %} + +{% raw %} +```liquid +// Using Liquid's special literal `empty` or `blank` to select +// posts that have `my_prop` set to an empty value. +{% assign filtered_posts = site.posts | where: 'my_prop', empty %} +``` +{% endraw %} + +### Binary operators in `where_exp` filter {%- include docs_version_badge.html version="4.0" -%} + +You can use Liquid binary operators `or` and `and` in the expression passed to the `where_exp` filter to employ multiple +conditionals in the operation. + +For example, to get a list of documents on English horror flicks, one could use the following snippet: + +{% raw %} +```liquid +{{ site.movies | where_exp: "item", "item.genre == 'horror' and item.language == 'English'" }} +``` +{% endraw %} + +Or to get a list of comic-book based movies, one may use the following: + +{% raw %} +```liquid +{{ site.movies | where_exp: "item", "item.sub_genre == 'MCU' or item.sub_genre == 'DCEU'" }} +``` +{% endraw %} + +### Standard Liquid Filters + +For your convenience, here is the list of all [Liquid filters]({{ page.shopify_filter_url }}) with links to examples in the official Liquid documentation. + +{% for filter in page.shopify_filters %} +- [{{ filter }}]({{ filter | prepend: page.shopify_filter_url | append: '/' }}) +{% endfor %} diff --git a/docs/_docs/liquid/tags.md b/docs/_docs/liquid/tags.md new file mode 100644 index 0000000..0e134fe --- /dev/null +++ b/docs/_docs/liquid/tags.md @@ -0,0 +1,170 @@ +--- +title: Tags Filters +permalink: "/docs/liquid/tags/" +--- +All of the standard Liquid +[tags](https://shopify.github.io/liquid/tags/control-flow/) are supported. +Jekyll has a few built in tags to help you build your site. You can also create +your own tags using [plugins]({{ '/docs/plugins/' | relative_url }}). + +## Includes + +If you have page snippets that you use repeatedly across your site, an +[include]({{ '/docs/includes/' | relative_url }}) is the perfect way to make this more maintainable. + +## Code snippet highlighting + +Jekyll has built in support for syntax highlighting of over 100 languages +thanks to [Rouge](http://rouge.jneen.net). Rouge is the default highlighter +in Jekyll 3 and above. + +{: .note .warning} +Using Pygments has been deprecated and is not supported in +Jekyll 4; the configuration setting <code>highlighter: pygments</code> +now automatically falls back to using <em>Rouge</em> which is written in Ruby +and 100% compatible with stylesheets for Pygments. + +To render a code block with syntax highlighting, surround your code as follows: + +{% raw %} +```liquid +{% highlight ruby %} +def foo + puts 'foo' +end +{% endhighlight %} +``` +{% endraw %} + +The argument to the `highlight` tag (`ruby` in the example above) is the +language identifier. To find the appropriate identifier to use for the language +you want to highlight, look for the “short name” on the [Rouge +wiki](https://github.com/jayferd/rouge/wiki/List-of-supported-languages-and-lexers). + +<div class="note"> + <h5>Jekyll processes all Liquid filters in code blocks</h5> + <p>If you are using a language that contains curly braces, you + will likely need to place <code>{% raw %}</code> and + <code>{% endraw %}</code> tags around your code. + Since Jekyll {% include docs_version_badge.html version="4.0" %}, you can add <code>render_with_liquid: false</code> in your front matter to disable Liquid entirely for a particular document.</p> +</div> + +### Line numbers + +There is a second argument to `highlight` called `linenos` that is optional. +Including the `linenos` argument will force the highlighted code to include line +numbers. For instance, the following code block would include line numbers next +to each line: + +{% raw %} +```liquid +{% highlight ruby linenos %} +def foo + puts 'foo' +end +{% endhighlight %} +``` +{% endraw %} + +### Stylesheets for syntax highlighting + +In order for the highlighting to show up, you’ll need to include a highlighting +stylesheet. For Pygments or Rouge you can use a stylesheet for Pygments, you +can find an example gallery +[here](https://jwarby.github.io/jekyll-pygments-themes/languages/ruby.html) +or from [its repository](https://github.com/jwarby/jekyll-pygments-themes). + +Copy the CSS file (`native.css` for example) into your css directory and import +the syntax highlighter styles into your `main.css`: + +```css +@import "native.css"; +``` + +## Links + +{: .note} +Since Jekyll {% include docs_version_badge.html version="4.0"%}, you don't need to prepend `link` and `post_url` tags with `site.baseurl`. + +### Linking to pages {#link} + +To link to a post, a page, collection item, or file, the `link` tag will generate the correct permalink URL for the path you specify. For example, if you use the `link` tag to link to `mypage.html`, even if you change your permalink style to include the file extension or omit it, the URL formed by the `link` tag will always be valid. + +You must include the file's original extension when using the `link` tag. Here are some examples: + +{% raw %} +```liquid +{% link _collection/name-of-document.md %} +{% link _posts/2016-07-26-name-of-post.md %} +{% link news/index.html %} +{% link /assets/files/doc.pdf %} +``` +{% endraw %} + +You can also use the `link` tag to create a link in Markdown as follows: + +{% raw %} +```liquid +[Link to a document]({% link _collection/name-of-document.md %}) +[Link to a post]({% link _posts/2016-07-26-name-of-post.md %}) +[Link to a page]({% link news/index.html %}) +[Link to a file]({% link /assets/files/doc.pdf %}) +``` +{% endraw %} + +The path to the post, page, or collection is defined as the path relative to the root directory (where your config file is) to the file, not the path from your existing page to the other page. + +For example, suppose you're creating a link in `page_a.md` (stored in `pages/folder1/folder2`) to `page_b.md` (stored in `pages/folder1`). Your path in the link would not be `../page_b.html`. Instead, it would be `/pages/folder1/page_b.md`. + +If you're unsure of the path, add {% raw %}`{{ page.path }}`{% endraw %} to the page and it will display the path. + +One major benefit of using the `link` or `post_url` tag is link validation. If the link doesn't exist, Jekyll won't build your site. This is a good thing, as it will alert you to a broken link so you can fix it (rather than allowing you to build and deploy a site with broken links). + +Note you cannot add filters to `link` tags. For example, you cannot append a string using Liquid filters, such as {% raw %}`{% link mypage.html | append: "#section1" %}`{% endraw %}. To link to sections on a page, you will need to use regular HTML or Markdown linking techniques. + +The name of the file you want to link can be specified as a variable instead of an actual file name. For example, suppose you defined a variable in your page's front matter like this: + +```yaml +--- +title: My page +my_variable: footer_company_a.html +--- +``` + +You could then reference that variable in your link: + +{% raw %} +```liquid +{% link {{ page.my_variable }} %} +``` +{% endraw %} + +In this example, the `link` tag would render a link to the file `footer_company_a.html`. + +### Linking to posts + +If you want to include a link to a post on your site, the `post_url` tag will generate the correct permalink URL for the post you specify. + +{% raw %} +```liquid +{% post_url 2010-07-21-name-of-post %} +``` +{% endraw %} + +If you organize your posts in subdirectories, you need to include subdirectory path to the post: + +{% raw %} +```liquid +{% post_url /subdir/2010-07-21-name-of-post %} +``` +{% endraw %} + +There is no need to include the file extension when using the `post_url` tag. + +You can also use this tag to create a link to a post in Markdown as follows: + +{% raw %} +```liquid +[Name of Link]({% post_url 2010-07-21-name-of-post %}) +``` +{% endraw %} diff --git a/docs/_docs/maintaining/affinity-team-captain.md b/docs/_docs/maintaining/affinity-team-captain.md new file mode 100644 index 0000000..a1d982f --- /dev/null +++ b/docs/_docs/maintaining/affinity-team-captain.md @@ -0,0 +1,28 @@ +--- +title: Affinity Team Captains +--- + +**This guide is for affinity team captains.** These special people are **team maintainers** of one of our [affinity teams][] and help triage and evaluate the issues and contributions of others. You may find what is written here interesting, but it’s definitely not for everyone. +{: .note .info} + +## Affinity teams & their captains + +The Jekyll project uses [affinity teams][] to help break up the work of incoming issues and pull requests from community members. We receive a sizeable number of issues and pull requests each week; the use of affinity teams helps distribute this load across a number of specialized groups instead of pushing it all onto @jekyll/core. + +## Responsibilities of Team Captains + +Each affinity team has a few captains who manage the issues and pull requests for that team. When an issue or PR is opened with a `/cc` for a given affinity team, @jekyllbot automatically assigns a random affinity team captain to the issue to triage it. They have access to add labels, reassign the issue, give LGTM's, and so forth. While they do not merge PR's today, they are still asked to review PR's for parts of the codebase under their purview. + +## How do I become a team captain? + +Just ask! Feel free to open an issue on `jekyll/jekyll` and add `/cc @jekyll/core`. We can add you. :smile: + +Alternatively, you can email or otherwise reach out to [@oe](https://github.com/oe) directly if you prefer the more private route. + +## Ugh, I'm tired and don't have time to be a captain anymore. What now? + +No sweat at all! Email [@oe](https://github.com/oe) and ask to be removed. Alternatively, you should be able to go to your team's page on GitHub.com (go to https://github.com/jekyll, click "Teams", click the link to your team) and change your status to either "member" or leave the team. + +We realize that being a captain is no easy feat so we want to make it a great experience. As always, communicate as much as you can with us about what is working, and what isn't. Thanks for dedicating some time to Jekyll! :sparkles: + +[affinity teams]: https://teams.jekyllrb.com/ diff --git a/docs/_docs/maintaining/avoiding-burnout.md b/docs/_docs/maintaining/avoiding-burnout.md new file mode 100644 index 0000000..42f4b7d --- /dev/null +++ b/docs/_docs/maintaining/avoiding-burnout.md @@ -0,0 +1,30 @@ +--- +title: "Avoiding Burnout" +--- + +**This guide is for maintainers.** These special people have **write access** to one or more of Jekyll's repositories and help merge the contributions of others. You may find what is written here interesting, but it’s definitely not for everyone. +{: .note .info} + +# 1. Use Jekyll + +Maintainers of Jekyll should be using it regularly. This is partly because you won't be a good maintainer unless you can put yourself in the shoes of our users, but also because you may at some point decide to stop using Jekyll, and at that point you should also decide to stop being a maintainer and find other things to work on. + +# 2. No Guilt About Leaving + +All maintainers can stop working on Jekyll at any time without any guilt or explanation (like at a job). We may still ask for your help with questions after you leave but you are under no obligation to answer them. If you create a big mess and then leave you still have no obligations but we may think less of you (or, realistically, probably just revert the problematic work). Also, you should probably take a break from Jekyll at least a few times a year. + +This also means contributors should be consumers. If a maintainer finds they are not using a project in the real-world, they should reconsider their involvement with the project. + +# 3. Prioritise Maintainers Over Users + +It's important to be user-focused but ultimately, as long as you follow #1 above, Jekyll's minimum number of users will be the number of maintainers. However, if Jekyll has no maintainers it will quickly become useless to all users and the project will die. As a result, no user complaint, behaviour or need takes priority over the burnout of maintainers. If users do not like the direction of the project, the easiest way to influence it is to make significant, high-quality code contributions and become a maintainer. + +# 4. Learn To Say No + +Jekyll gets a lot of feature requests, non-reproducible bug reports, usage questions and PRs we won't accept. These should be closed out as soon as we realise that they aren't going to be resolved or merged. This is kinder than deciding this after a long period of review. Our issue tracker should reflect work to be done. + +--- + +Thanks to https://gist.github.com/ryanflorence/124070e7c4b3839d4573 which influenced this document. + +Thanks to [Homebrew's "Avoiding Burnout" document](https://github.com/Homebrew/brew/blob/master/docs/Maintainers-Avoiding-Burnout.md) for providing a perfect base for this document. diff --git a/docs/_docs/maintaining/becoming-a-maintainer.md b/docs/_docs/maintaining/becoming-a-maintainer.md new file mode 100644 index 0000000..e8a45d2 --- /dev/null +++ b/docs/_docs/maintaining/becoming-a-maintainer.md @@ -0,0 +1,38 @@ +--- +title: "Becoming a Maintainer" +--- + +**This guide is for contributors.** These special people have contributed to one or more of Jekyll's repositories, but do not yet have write access to any. You may find what is written here interesting, but it’s definitely not for everyone. +{: .note .info} + +So you want to become a maintainer of a Jekyll project? We'd love to have you! Here are some things we like to see from community members before we promote them to maintainers. + +## 1. Use Jekyll + +You want to maintain Jekyll? Use it often. Do weird things with it. Do normal things with it. Does it work? Does it have any weaknesses? Is there a gap in the product that you think should be filled? + +## 2. Help Triage Issues + +Watch the repository you're interested in. Join [an Affinity Team](https://teams.jekyllrb.com) and receive mentions regarding a particular interest area of the project. When you receive a notification for an issue that has not been triaged by a maintainer, dive in. Can you reproduce the issue? Can you determine the fix? [More tips on Triaging an Issue in our maintainer guide](../triaging-an-issue/). Every maintainer loves an issue that is resolved before they get to it. :smiley: + +## 3. Write Documentation + +Good documentation means less confusion for our users and fewer issues to triage. Documentation is always in need of fixes and updates as we change the code. Read through the documentation during your normal usage of the product and submit changes as you feel they are necessary. + +## 4. Write Code + +As a maintainer, you will be reviewing pull requests which update code. You should feel comfortable with the Jekyll codebase enough to confidently review any pull request put forward. In order to become more comfortable, write some code of your own and send a pull request. A great place to start is with any issue labeled "bug" in the issue tracker. Write a test which replicates the problem and fails, then work on fixing the code such that the test passes. + +## 5. Review Pull Requests + +Start by reviewing one pull request a week. Leave detailed comments and [follow our guide for reviewing pull requests](../reviewing-a-pull-request/). + +## 6. Ask! + +Open an issue describing your contributions to the project and why you wish to be a maintainer. Issues are nice because you can easily reference where you have demonstrated that you help triage issues, write code & documentation, and review pull requests. You may also email any maintainer privately if you do not feel comfortable asking in the open. + +We would love to expand the team and look forward to many more community members becoming maintainers! + +# Helping Out Elsewhere + +In addition to maintainers of our core and plugin code, the Jekyll team is comprised of moderators for our forums. These helpful community members take a look at the topics posted to [https://talk.jekyllrb.com](https://talk.jekyllrb.com) and ensure they are properly categorized and are acceptable under our Code of Conduct. If you would like to be a moderator, email one of the maintainers with links to where you have answered questions and a request to be added as a moderator. More help is always welcome. diff --git a/docs/_docs/maintaining/index.md b/docs/_docs/maintaining/index.md new file mode 100644 index 0000000..e7a1b60 --- /dev/null +++ b/docs/_docs/maintaining/index.md @@ -0,0 +1,22 @@ +--- +title: Maintaining Jekyll +permalink: /docs/maintaining/ +--- + +**This guide is for Jekyll contributors and maintainers.** These special people contribute to one or more of Jekyll's repositories or help merge the contributions of others. You may find what is written here interesting, but it’s definitely not for everyone. +{: .note .info} + +Hello! This is where we document various processes for maintaining Jekyll. Being a maintainer for any Jekyll project is a big responsibility, so we put together some helpful documentation for various tasks you might do as a maintainer. + +- [Affinity teams & their captains](affinity-team-captain/) +- [Triaging an issue](triaging-an-issue/) +- [Reviewing a pull request](reviewing-a-pull-request/) +- [Merging a pull request](merging-a-pull-request/) +- [Avoiding burnout](avoiding-burnout/) +- [Special Labels](special-labels/) +- [Releasing a new version](releasing-a-new-version/) +- [Releasing a new version off `*-stable` branches](releasing-off-stable-branches/) + +Interested in becoming a maintainer? Here is some documentation for **contributors**: + +- [Becoming a maintainer](becoming-a-maintainer/) diff --git a/docs/_docs/maintaining/merging-a-pull-request.md b/docs/_docs/maintaining/merging-a-pull-request.md new file mode 100644 index 0000000..603194c --- /dev/null +++ b/docs/_docs/maintaining/merging-a-pull-request.md @@ -0,0 +1,55 @@ +--- +title: "Merging a Pull Request" +--- + +**This guide is for maintainers.** These special people have **write access** to one or more of Jekyll's repositories and help merge the contributions of others. You may find what is written here interesting, but it’s definitely not for everyone. +{: .note .info} + +## Code Review + +All pull requests should be subject to code review. Code review is a [foundational value](https://blog.fullstory.com/what-we-learned-from-google-code-reviews-arent-just-for-catching-bugs/) of good engineering teams. Besides providing validation of correctness, it promotes a sense of community and gives other maintainers understanding of all parts of the code base. In short, code review is crucial to a healthy open source project. + +**Read our guide for [Reviewing a pull request](../reviewing-a-pull-request/) before merging.** Notably, the change must have tests if for code, and at least two maintainers must give it an OK. + +## Merging + +We have [a helpful little bot](https://github.com/jekyllbot) which we use to merge pull requests. We don't use the GitHub.com interface for two reasons: + +1. You can't modify anything on mobile (e.g. titles, labels) +2. We like to provide a consistent paper trail in the `History.markdown` file for each release + +To merge a pull request, leave a comment thanking the contributor, then add the special merge request: + +``` +Thank you very much for your contribution. Folks like you make this project and community strong. :heart: + +@jekyllbot: merge +dev +``` + +The merge request is made up of three things: + +1. `@jekyllbot:` – this is the prefix our bot looks for when processing commands +2. `merge` – the command +3. `+dev` – the category to which the changes belong. + +The categories match the headings in the `History.markdown` file, and they are: + +1. Major Enhancements (`+major`) – major updates or breaking changes to the code which necessitate a major version bump (v3 ~> v4) +2. Minor Enhancements (`+minor`) – minor updates (with the labels `feature` or `enhancement`) which necessitate a minor version bump (v3.1 ~> v3.2) +3. Bug Fixes (`+bug`) – corrections to code which do not change or add functionality, which necessitate a patch version bump (v3.1.0 ~> v3.1.1) +4. Documentation (`+doc`) - changes to the documentation found in `docs/_docs/` +5. Site Enhancements (`+site`) – changes to the source of [https://jekyllrb.com](https://jekyllrb.com) found in `docs/` +6. Development Fixes (`+dev`) – changes which do not affect user-facing functionality or documentation, such as test fixes or bumping internal dependencies +7. Forward Ports (`+port`) — bug fixes applied to a previous version of Jekyll pulled onto `master`, e.g. cherry-picked commits from `3-1-stable` to `master` + +Once @jekyllbot has merged the pull request, you should see three things: + +1. A successful merge +2. Addition of labels for the necessary category if they aren't already applied +3. A commit to the `History.markdown` file which adds a note about the change + +If you forget the category, that's just fine. You can always go back and move the line to the proper category header later. The category is always necessary for `jekyll/jekyll`, but many plugins have too few changes to necessitate changelog categories. + +## Rejoice + +You did it! Thanks for being a maintainer for one of our official Jekyll projects. Your work means the world to our thousands of users who rely on Jekyll daily. :heart: diff --git a/docs/_docs/maintaining/releasing-a-new-version.md b/docs/_docs/maintaining/releasing-a-new-version.md new file mode 100644 index 0000000..1d8c347 --- /dev/null +++ b/docs/_docs/maintaining/releasing-a-new-version.md @@ -0,0 +1,162 @@ +--- +title: "Releasing a new version" +--- + +**This guide is for maintainers.** These special people have **write access** to one or more of Jekyll's repositories and help merge the +contributions of others. You may find what is written here interesting, but it's definitely not for everyone. +{: .note .info} + +The most important thing to understand before making a release is that there's no need to feel nervous. Most things are revertable, and even if +you do publish an incomplete gem version, we can always skip that one. Don't hesitate to contact the other maintainers if you feel unsure or +don't know what to do next. + +### Bump the version + +The only important place you need to manually bump the version is in `lib/jekyll/version.rb`. Adjust that, and everything else should work fine. + +The version will mostly be of the format `"major.minor.patch"`. At times, we may decide to ship pre-releases which will be in the format +`"major.minor.patch.suffix"`. `suffix` is not standardized and may be anything like `pre.alpha1`, `pre.rc2`, or simply `beta3`, etc. + +To determine the correct version, consult the `## HEAD` section of our history document, `History.markdown`, first. + +- If there's a subsection titled `Major Enhancements` + - Increment the `major` component of the version string and reset both `minor` and `patch` components to `0`. + - Add `suffix` if applicable. + - For example, `"3.9.1" => "4.0.0"` or, `"3.9.1 => "4.0.0.alpha1"`. + - Skip to next step in the release process. + +- If there's a subsection titled `Minor Enhancements` + - Increment just the `minor` component and reset the patch component to `0`. + - Add `suffix` if applicable. + - For example, `"4.0.2" => "4.1.0"` or `"4.1.0" => "4.2.0.pre"`. + - Skip to next step in the release process. + +- For anything else, increment just the `patch` component or `suffix` component as applicable. For example, `"4.0.2" => "4.0.3"` or + `"4.1.0.beta3" => "4.1.0.rc"`. + +### Write a release post + +In case this wasn't done already, you can generate a new release post scaffold using the included `rake` command: + +```sh +bundle exec rake site:releases:new[3.8.0] +``` + +where `3.8.0` should be replaced with the new version. + +Then, write the post. Be sure to thank all of the collaborators and maintainers who have contributed since the last release. You can generate +a log of their names using the following command: + +```sh +git shortlog -sn master...v3.7.2 +``` + +where `v3.7.2` is the git tag for the previous release. In case the tag doesn't exist in your repository, run: + +```sh +git pull +``` + +Be sure to open a pull request for your release post once its finished. + +### Update the History document + +Replace the first header of `History.markdown` with a version milestone. This looks like the following: + +```diff +- ## HEAD ++ ## 3.7.1 / 2018-01-25 +``` + +Adjust the version number and the date. The `## HEAD` heading will be regenerated the next time a pull request is merged. + +Rearrange the subsections (as a whole) based on decreasing priorities as illustrated below: + +``` +## 4.2.0 / 2020-12-14 + +### Major Enhancements + +... + +### Minor Enhancements + +... + +### Bug Fixes + +... + +### Security Fixes + +... + +### Optimization Fixes + +... + +### Development Fixes + +... + +### Site Enhancements + +... +``` + +Once you've done this, update the website by running the following command: + +```sh +bundle exec rake site:generate +``` + +This updates the website's changelog, and pushes the versions in various other places. + +It's recommended that you go over the `History.markdown` file manually one more time, in case there are any spelling errors or such. Feel free +to fix those manually, and after you're done generating the website changelog, commit your changes. + +### Push the version + +Before you do this step, make sure the following things are done: + +- A release post has been prepared, and is ideally already live via a prior pull request. +- All of the prior steps are done, especially the change to `lib/jekyll/version.rb` has been staged for commit. +- Commit staged changes to the local `master` branch preferably with commit message `"Release :gem: v[CURRENT_VERSION]"`. + +The only thing left to do now is to run this command: + +```sh +git push upstream master +``` + +where `upstream` references `git@github.com:jekyll/jekyll.git`. + +This will trigger a GitHub Actions workflow that will automatically build the new gem, tag the release commit, push the tag to GitHub and +then finally, push the new gem to RubyGems. Don't worry about creating a GitHub release either, @jekyllbot will take care of that when the +release workflow publishes the new tag. + +And then, if the workflow has completed successfully, you're done! :tada: +Feel free to celebrate! + +If you have access to the [@jekyllrb](https://twitter.com/jekyllrb) Twitter account, you should tweet the release post from there. If not, just +ask another maintainer to do it or to give you access. + +### Build the docs + +We package our documentation as a :gem: Gem for offline use. + +This is done with the [**jekyll-docs**](https://github.com/jekyll/jekyll-docs#building) repository, and more detailed instructions are +provided there. + +## For non-core gems + +If you're not a maintainer for `jekyll/jekyll`, the procedure is much simpler in a lot of cases. Generally, the procedure still looks like +this: + +- Bump the gem version manually, usually in `lib/<plugin_name>/version.rb` +- Adjust the history file +- Commit changes to default branch preferably with message `"Release :gem: v[CURRENT_VERSION]"` +- Push to remote repository +- Rejoice + +Be sure to ask your project's maintainers if you're unsure! diff --git a/docs/_docs/maintaining/releasing-off-stable-branches.md b/docs/_docs/maintaining/releasing-off-stable-branches.md new file mode 100644 index 0000000..1fa16fa --- /dev/null +++ b/docs/_docs/maintaining/releasing-off-stable-branches.md @@ -0,0 +1,63 @@ +--- +title: Releasing off older stable branches +--- + +Apart from having releases cut from the default `master` branch, Jekyll Core may occasionally cut releases containing security patches and +critical bug-fixes for older versions under maintenance. Such releases are cut from specially named branches, following the pattern +`[x].[y]-stable` where `[x]` denotes semver-major-version and `[y]`, the semver-minor-version. For example, the branch `3.9-stable` refers to +commits released as part of `jekyll-3.9.x` series. + +Co-ordinating a release off a `*-stable` branch is complicated mainly because the default branch has to inevitably reflect the release as well. + +## Requirements + +- The maintainer has to have **write-access** to both the concerned `*-stable` and `master` branches. +- The maintainer needs to complete the task using their **local CLI program** instead of dispatching via GitHub Web UI. +- The maintainer is abreast with the workflow to [release off `master`]({{ 'docs/maintaining/releasing-a-new-version/' | relative_url }}). The + procedure documented in the following section is an abridged adaptation of the workflow for `master`. +- A release post has been drafted and **is awaiting publish to `master`** via an approved pull request. +- Stable internet connection. + +## Trigger release workflow + +1. Ensure that you've **checked out the concerned `*-stable` branch** and is up-to-date with its counterpart at `jekyll/jekyll` at GitHub. +2. Bump the `VERSION` string in `lib/jekyll/version.rb`. +3. Update the **History document** as documented [here]({{ 'docs/maintaining/releasing-a-new-version/#update-the-history-document' | relative_url }}).<br/> + (**IMPORTANT: Do not run `rake site:generate` on the stable branch though**). +4. Copy the entire History section pertaining to current release and paste into a new tab / window of your text-editor. We will use this + temporary snippet at a future stage. +5. Commit changes to the version-file and History document with commit message `Release :gem: v[CURRENT_VERSION]`. +6. Push commit to upstream remote `jekyll/jekyll` at GitHub. + +## Publish release post + +1. Ensure the `Release Gem` workflow has completed successfully. +2. Merge release-post pull request to `master`. + +## Update default branch to reflect release off the stable branch + +1. Locally, check out `master` and ensure it is up-to-date with its remote counterpart at `jekyll/jekyll` at GitHub. +2. Update History document using the snippet in the temporary tab / window created earlier. The various sections in the History document are + primarily in reverse chronological order and secondarily scoped to the semver-major-version. For example, a release section for `v3.9.2` + will be listed above the section for `v3.9.1` but under release sections for v4.x. + The snippet stashed earlier has to be injected into the correct location manually. +3. Optionally, update `VERSION` string in `lib/jekyll/version.rb`. (*If existing version is lesser than latest version*). +4. Now **run `rake site:generate`** to update various meta files: + - docs/_config.yml + - docs/_docs/history.md + - docs/latest_version.txt +5. Commit changes to various meta files with commit message `Release :gem: v[CURRENT_VERSION]`. +6. Push commit to upstream remote. + +## Publish GitHub Release + +Unlike releases cut off the `master` branch, our JekyllBot does not automatically create and publish a GitHub Release for tags created from +*non-default* branches. Therefore, the maintainer has to **manually create and publish** the concerned GitHub Release. +1. Choose the newly pushed tag. +2. Title is same as the name of the selected tag. +3. The release snippet stashed previously forms the body. +4. Delete the snippet's title (`## x.y.z / YYYY-MM-DD`) from the release body. +5. Publish. + +Note: The GitHub Release may optionally be *drafted* prior to updating the default branch and then *published* immediately after pushing the +update commit to the default branch to streamline the procedure. diff --git a/docs/_docs/maintaining/reviewing-a-pull-request.md b/docs/_docs/maintaining/reviewing-a-pull-request.md new file mode 100644 index 0000000..cff0efd --- /dev/null +++ b/docs/_docs/maintaining/reviewing-a-pull-request.md @@ -0,0 +1,46 @@ +--- +title: "Reviewing a Pull Request" +--- + +**This guide is for maintainers.** These special people have **write access** to one or more of Jekyll's repositories and help merge the contributions of others. You may find what is written here interesting, but it’s definitely not for everyone. +{: .note .info} + +## Respond Kindly + +Above all else, please review a pull request kindly. Our community can only be strong if we make it a welcoming and inclusive environment. To further promote this, the Jekyll community is governed by a [Code of Conduct](/docs/conduct/) by which all community members must abide. + +Use emoji liberally :heart: :tada: :sparkles: :confetti_ball: and feel free to be emotive!! Contributions keep this project moving forward and we're always happy to receive them, even if the pull request isn't ultimately merged. + +Mike McQuaid's post on the GitHub blog entitled ["Kindly Closing Pull Requests"](https://github.com/blog/2124-kindly-closing-pull-requests) is a great place to start. It describes various scenarios in which it would be acceptable to close a pull request for reasons other than lack of technical integrity or accuracy. Part of being kind is responding to and resolving pull requests quickly. + +## Respond Quickly + +We should be able to review all pull requests within one week. The only time initial review should take longer is if all the maintainers mysteriously took vacation during the same week. Promptness encourages frequent, high-quality contributions from community members and other maintainers. + +If your response requires a response on the part of the author, please add the `pending-feedback` tag. @jekyllbot will automatically remove the tag once the author of the pull request responds. + +## Resolve Quickly + +Similarly, we should aim to resolve pull requests quickly. If a pull request introduces a feature which does not fit into the core purpose or goal of the project, close it promptly with a kind explanation of why it is not acceptable. + +Leave detailed comments wherever possible. Provide the contributor with context around why the change you are requesting is necessary, or why the question you are asking is important to resolve. The more context we can clearly communicate to the contributor, the better able the contributor is to provide high-quality patches. + +You may close a pull request if more than 30 days pass without a response from the author. + +In some cases, review will involve many weeks of back-and-forth. As long as communication continues, this is fine. Ideally, any PR would be capable of resolution within 30 days of it being opened. + +## Look for Tests + +If this is a code change, are there tests for the updated or added behaviour? Shipping a version with bugs is inevitable, but ensuring changes are tested helps keep bugs and regressions to a minimum. + +## CI Must Pass + +It is fine to ask a contributor to investigate failures on Travis and patch them up before you begin your review. It is helpful to leave a message for the contributor indicating that the tests have failed and that no review will occur before the tests pass. If they ask for help, take a look and assist if you can. + +## Rule of Two + +A pull request may be merged once two maintainers have reviewed the pull request and indicated that it is acceptable to them. There is no need to wait for a third unless one of the two reviewers wishes for another set of eyes. + +## Think Security + +We owe it to our users to ensure that using a theme from the community or building someone else's site doesn't come with built-in security vulnerabilities. Things like where files may be read from and written to are important to keep secure. Jekyll is also the basis for hosted services such as [GitHub Pages](https://pages.github.com), which cannot upgrade when security issues are introduced. diff --git a/docs/_docs/maintaining/special-labels.md b/docs/_docs/maintaining/special-labels.md new file mode 100644 index 0000000..4d89841 --- /dev/null +++ b/docs/_docs/maintaining/special-labels.md @@ -0,0 +1,24 @@ +--- +title: "Special Labels" +--- + +**This guide is for maintainers.** These special people have **write access** to one or more of Jekyll's repositories and help merge the contributions of others. You may find what is written here interesting, but it’s definitely not for everyone. +{: .note .info} + +We use a series of "special labels" on GitHub.com to automate handling of some parts of the pull request and issue process. @jekyllbot may automatically apply or remove certain labels based on actions taken by users or maintainers. Below are the labels and how they work: + +## `pending-feedback` + +This label is used to indicate that we need more information from the issue/PR author in order to continue. It may be that you need more info before you can properly triage a bug report, or that you have some unanswered questions about a PR that need to be resolved before moving forward. You can safely ignore any issue with this label, as it is waiting for feedback. + +## `needs-work` & `pending-rebase` + +These labels are used to indicate that the Git state of a pull request must change. Both are removed once a push is registered (a "synchronize" event for the pull request) and the pull request becomes mergable. Add `needs-work` to a PR if, after your review, it requires code changes. Add `pending-rebase` to a PR if the code is fine but the branch is not automatically mergable with the target branch (e.g. `master`). + +## `stale` + +This label is automatically added and removed by @jekyllbot based on activity on an issue or pull request. The rules for this label are laid out in [Triaging an Issue: Staleness and automatic closure](../triaging-an-issue/#staleness-and-automatic-closure). + +## `pinned` + +This label is for @jekyllbot to ignore the age of the issue, which means that the `stale` label won't be automatically added, and the issue won't be closed after a while. This needs to be set manually, and should be set with care. (The `has-pull-request` label does the same thing, but shouldn't be used to _only_ keep an issue open) diff --git a/docs/_docs/maintaining/triaging-an-issue.md b/docs/_docs/maintaining/triaging-an-issue.md new file mode 100644 index 0000000..ac0833b --- /dev/null +++ b/docs/_docs/maintaining/triaging-an-issue.md @@ -0,0 +1,54 @@ +--- +title: "Triaging an Issue" +--- + +**This guide is for maintainers.** These special people have **write access** to one or more of Jekyll's repositories and help merge the contributions of others. You may find what is written here interesting, but it’s definitely not for everyone. +{: .note .info} + +Before evaluating an issue, it is important to identify if it is a feature +request or a bug. For the Jekyll project the following definitions are used +to identify a feature or a bug: + +**Feature** - A feature is defined as a request that adds functionality to +Jekyll outside of its current capabilities. +**Bug** - A bug is defined as an issue that identifies an error that a user +(or users) encounter when using current Jekyll functionalities. + +## Feature? + +If the issue describes a feature request, ask: + +1. Is this a setting? [Settings are a crutch](http://ben.balter.com/2016/03/08/optimizing-for-power-users-and-edge-cases/#settings-are-a-crutch) for doing "the right thing". Settings usually point to a bad default or an edge case that could be solved easily with a plugin. Keep the :christmas_tree: of settings as small as possible so as not to reduce the usability of the product. We like the philosophy "decisions not options." +2. Would at least 80% of users find it useful? If even a quarter of our users won't use it, it's very likely that the request doesn't fit our product's core goal. +3. Is there another way to accomplish the end goal of the request? Most feature requests are due to bad documentation for or understanding of a pre-existing feature. See if you can clarify the end goal of the request. What is the user trying to do? Could they accomplish that goal through another feature we already support? +4. Even if 80% of our users will use it, does it fit the core goal of our project? We are writing a tool for making static websites, not a swiss army knife for publishing more generally. + +Feel free to get others' opinions and ask questions of the issue author, but depending upon the answers to the questions above, it may be out of scope for our project. + +If the request is within scope, prioritize it on the product roadmap with the other maintainers. Apply the appropriate tags and ensure the right people have weighed in to define the feature's scope and implementation. If you want to be the _best ever_, submit a PR yourself which adds the feature. + +## Bug? + +### Reproducibility + +If the bug has clear reproduction steps, take a minute to try them. If it helps, write a test in our test suite for the scenario which replicates the problem. Can you reliably replicate the issue? + +If you can't replicate the issue, post your replication steps which didn't work and ask for clarification from the issue author. + +### Supported Platform + +Is the author using a supported platform? We support the latest versions of macOS, Ubuntu, Debian, CentOS, Fedora, and Arch Linux. + +You may close the issue immediately if the author cannot reproduce the issue on a supported platform. For Windows-related problems, leave a comment letting the user know that Windows is not officially supported, but that they may absolutely continue using the issue to communicate with folks from `@jekyll/windows` to further investigate. Additionally, you can point them to Jekyll Talk (https://talk.jekyllrb.com) as a means of getting support from the community. + +If the user is experiencing issues with GitHub Pages or another hosted platform that we cannot reproduce, please direct them to the platform's support channel and close the issue. + +### What they wanted vs. what they got + +An issue without a clear explanation of what the user got and what they were expecting to get is not an issue we can accurately respond to. If the user doesn't provide this information, please ask for clarification and apply the `pending-feedback` label. This information helps us build test cases such that we do not break the behaviour again in the future. The `pending-feedback` label will be removed automatically once the issue author posts a reply. + +Is what they wanted to get something we want to happen? Sometimes a bug report is actually masquerading as a feature request. See the guidance above for handling feature requests. + +### Staleness and automatic closure + +@jekyllbot will automatically mark issues as `stale` if no activity occurs for at least one month. @jekyllbot leaves a comment asking for information about reproducibility in current versions. If no one responds after another month, the issue is automatically closed. This behavior can be suppressed by setting the [`pinned` label](/docs/maintaining/special-labels/#pinned). diff --git a/docs/_docs/markdown-101.md b/docs/_docs/markdown-101.md new file mode 100644 index 0000000..c1090db --- /dev/null +++ b/docs/_docs/markdown-101.md @@ -0,0 +1,6 @@ +--- +title: Markdown 101 +permalink: /docs/markdown-101/ +--- + +# TO WRITE diff --git a/docs/_docs/migrations.md b/docs/_docs/migrations.md new file mode 100644 index 0000000..c3a69ca --- /dev/null +++ b/docs/_docs/migrations.md @@ -0,0 +1,8 @@ +--- +title: Blog Migrations +permalink: /docs/migrations/ +--- + +If you’re switching to Jekyll from another blogging system, Jekyll’s importers +can help you with the move. To learn more about importing your site to Jekyll, +visit our [`jekyll-import` docs site](https://import.jekyllrb.com/docs/home/). diff --git a/docs/_docs/pages.md b/docs/_docs/pages.md new file mode 100644 index 0000000..369ab13 --- /dev/null +++ b/docs/_docs/pages.md @@ -0,0 +1,41 @@ +--- +title: Pages +permalink: /docs/pages/ +--- + +Pages are the most basic building block for content. They're useful for standalone +content (content which is not date based or is not a group of content such as staff +members or recipes). + +The simplest way of adding a page is to add an HTML file in the root +directory with a suitable filename. You can also write a page in Markdown using +a `.md` extension which converts to HTML on build. For a site with +a homepage, an about page, and a contact page, here’s what the root directory +and associated URLs might look like: + +``` +. +├── about.md # => http://example.com/about.html +├── index.html # => http://example.com/ +└── contact.html # => http://example.com/contact.html +``` + +If you have a lot of pages, you can organize them into subfolders. The same subfolders that are used to group your pages in your project's source will then exist in the `_site` folder when your site builds. However, when a page has a *different* permalink set in the front matter, the subfolder at `_site` changes accordingly. + +``` +. +├── about.md # => http://example.com/about.html +├── documentation # folder containing pages +│ └── doc1.md # => http://example.com/documentation/doc1.html +├── design # folder containing pages +│ └── draft.md # => http://example.com/design/draft.html +``` + +## Changing the output URL + +You might want to have a particular folder structure for your source files that changes for the built site. With [permalinks](/docs/permalinks/) you have full control of the output URL. + +## Excerpts for pages + +From Jekyll 4.1.1 onwards, one can *choose* to generate excerpts for their pages by setting `page_excerpts` to `true` in their +config file. diff --git a/docs/_docs/pagination.md b/docs/_docs/pagination.md new file mode 100644 index 0000000..a9ec1a7 --- /dev/null +++ b/docs/_docs/pagination.md @@ -0,0 +1,171 @@ +--- +title: Pagination +permalink: /docs/pagination/ +--- + +With many websites — especially blogs — it’s very common to +break the main listing of posts up into smaller lists and display them over +multiple pages. Jekyll offers a pagination plugin, so you can automatically +generate the appropriate files and folders you need for paginated listings. + +For Jekyll 3, include the `jekyll-paginate` plugin in your Gemfile and in +your `_config.yml` under `plugins`. For Jekyll 2, this is standard. + +<div class="note info"> + <h5>Pagination only works within HTML files</h5> + <p> + Pagination does not work from within Markdown files from + your Jekyll site. Pagination works when called from within the HTML + file, named <code>index.html</code>, which optionally may reside in and + produce pagination from within a subdirectory, via the + <code>paginate_path</code> configuration value. + </p> +</div> + +## Enable pagination + +To enable pagination for posts on your blog, add a line to the `_config.yml` file that +specifies how many items should be displayed per page: + +```yaml +paginate: 5 +``` + +The number should be the maximum number of Posts you’d like to be displayed +per-page in the generated site. + +You may also specify the destination of the pagination pages: + +```yaml +paginate_path: "/blog/page:num/" +``` + +This will read in `blog/index.html`, send it each pagination page in Liquid as +`paginator` and write the output to `blog/page:num/`, where `:num` is the +pagination page number, starting with `2`. <br/> +If a site has 12 posts and specifies `paginate: 5`, Jekyll will write `blog/index.html` +with the first 5 posts, `blog/page2/index.html` with the next 5 posts and +`blog/page3/index.html` with the last 2 posts into the destination directory. + +<div class="note warning"> + <h5>Don't set a permalink</h5> + <p> + Setting a permalink in the front matter of your blog page will cause + pagination to break. Just omit the permalink. + </p> +</div> + +<div class="note info"> + <h5>Pagination for categories, tags and collections</h5> + <p> + The more recent <a href="https://github.com/sverrirs/jekyll-paginate-v2"> + jekyll-paginate-v2</a> plugin supports more features. See the + <a href="https://github.com/sverrirs/jekyll-paginate-v2/tree/master/examples"> + pagination examples</a> in the repository. <strong>This plugin is not + supported by GitHub Pages</strong>. + </p> +</div> + +## Liquid Attributes Available + +The pagination plugin exposes the `paginator` liquid object with the following +attributes: + +{% include docs_variables_table.html scope=site.data.jekyll_variables.paginator %} + +<div class="note info"> + <h5>Pagination does not support tags or categories</h5> + <p>Pagination pages through every post in the <code>posts</code> + variable unless a post has <code>hidden: true</code> in its front matter. + It does not currently allow paging over groups of posts linked + by a common tag or category. It cannot include any collection of + documents because it is restricted to posts.</p> +</div> + +## Render the paginated Posts + +The next thing you need to do is to actually display your posts in a list using +the `paginator` variable that will now be available to you. You’ll probably +want to do this in one of the main pages of your site. Here’s one example of a +simple way of rendering paginated Posts in a HTML file: + +{% raw %} +```liquid +--- +layout: default +title: My Blog +--- + +<!-- This loops through the paginated posts --> +{% for post in paginator.posts %} + <h1><a href="{{ post.url }}">{{ post.title }}</a></h1> + <p class="author"> + <span class="date">{{ post.date }}</span> + </p> + <div class="content"> + {{ post.content }} + </div> +{% endfor %} + +<!-- Pagination links --> +<div class="pagination"> + {% if paginator.previous_page %} + <a href="{{ paginator.previous_page_path }}" class="previous"> + Previous + </a> + {% else %} + <span class="previous">Previous</span> + {% endif %} + <span class="page_number "> + Page: {{ paginator.page }} of {{ paginator.total_pages }} + </span> + {% if paginator.next_page %} + <a href="{{ paginator.next_page_path }}" class="next">Next</a> + {% else %} + <span class="next ">Next</span> + {% endif %} +</div> +``` +{% endraw %} + +<div class="note warning"> + <h5>Beware the page one edge-case</h5> + <p> + Jekyll does not generate a ‘page1’ folder, so the above code will not work + when a <code>/page1</code> link is produced. See below for a way to handle + this if it’s a problem for you. + </p> +</div> + +The following HTML snippet should handle page one, and render a list of each +page with links to all but the current page. + +{% raw %} +```liquid +{% if paginator.total_pages > 1 %} +<div class="pagination"> + {% if paginator.previous_page %} + <a href="{{ paginator.previous_page_path | relative_url }}">« Prev</a> + {% else %} + <span>« Prev</span> + {% endif %} + + {% for page in (1..paginator.total_pages) %} + {% if page == paginator.page %} + <em>{{ page }}</em> + {% elsif page == 1 %} + <a href="{{ '/' | relative_url }}">{{ page }}</a> + {% else %} + <a href="{{ site.paginate_path | relative_url | replace: ':num', page }}">{{ page }}</a> + {% endif %} + {% endfor %} + + {% if paginator.next_page %} + <a href="{{ paginator.next_page_path | relative_url }}">Next »</a> + {% else %} + <span>Next »</span> + {% endif %} +</div> +{% endif %} +``` +{% endraw %} diff --git a/docs/_docs/permalinks.md b/docs/_docs/permalinks.md new file mode 100644 index 0000000..7f19977 --- /dev/null +++ b/docs/_docs/permalinks.md @@ -0,0 +1,488 @@ +--- +title: Permalinks +permalink: /docs/permalinks/ +--- + +Permalinks are the output path for your pages, posts, or collections. They +allow you to structure the directories of your source code different from the +directories in your output. + +## Front Matter + +The simplest way to set a permalink is using front matter. You set the +`permalink` variable in front matter to the output path you'd like. + +For example, you might have a page on your site located at +`/my_pages/about-me.html` and you want the output url to be `/about/`. In +front matter of the page you would set: + +```yaml +--- +permalink: /about/ +--- +``` + +## Global + +Setting a permalink in front matter for every page on your site is no fun. +Luckily, Jekyll lets you set the permalink structure globally in your `_config.yml`. + +To set a global permalink, you use the `permalink` variable in `_config.yml`. +You can use placeholders to your desired output. For example: + +```yaml +permalink: /:categories/:year/:month/:day/:title:output_ext +``` + +Note that pages and collections (excluding `posts` and `drafts`) don't have time +and categories (for pages, the above `:title` is equivalent to `:basename`), these +aspects of the permalink style are ignored for the output. + +For example, a permalink style of +`/:categories/:year/:month/:day/:title:output_ext` for the `posts` collection becomes +`/:title.html` for pages and collections (excluding `posts` and `drafts`). + +### Placeholders + +Here's the full list of placeholders available: + +<div class="mobile-side-scroller"> +<table> + <thead> + <tr> + <th>Variable</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td> + <p><code>year</code></p> + </td> + <td> + <p> + Year from the post’s filename with four digits. + May be overridden via the document’s <code>date</code> front matter. + </p> + </td> + </tr> + <tr> + <td> + <p><code>short_year</code></p> + </td> + <td> + <p> + Year from the post’s filename without the century. (00..99) + May be overridden via the document’s <code>date</code> front matter. + </p> + </td> + </tr> + <tr> + <td> + <p><code>month</code></p> + </td> + <td> + <p> + Month from the post’s filename. (01..12) + May be overridden via the document’s <code>date</code> front matter. + </p> + </td> + </tr> + <tr> + <td> + <p><code>i_month</code></p> + </td> + <td> + <p> + Month without leading zeros from the post’s filename. May be + overridden via the document’s <code>date</code> front matter. + </p> + </td> + </tr> + <tr> + <td> + <p><code>short_month</code></p> + </td> + <td> + <p>Three-letter month abbreviation, e.g. “Jan”.</p> + </td> + </tr> + <tr> + <td> + <p><code>long_month</code></p> + <small>{% include docs_version_badge.html version="4.0" %}</small> + </td> + <td> + <p>Full month name, e.g. “January”.</p> + </td> + </tr> + <tr> + <td> + <p><code>day</code></p> + </td> + <td> + <p> + Day of the month from the post’s filename. (01..31) + May be overridden via the document’s <code>date</code> front matter. + </p> + </td> + </tr> + <tr> + <td> + <p><code>i_day</code></p> + </td> + <td> + <p> + Day of the month without leading zeros from the post’s filename. + May be overridden via the document’s <code>date</code> front matter. + </p> + </td> + </tr> + <tr> + <td> + <p><code>y_day</code></p> + </td> + <td> + <p>Ordinal day of the year from the post’s filename, with leading zeros. (001..366)</p> + </td> + </tr> + <tr> + <td> + <p><code>w_year</code></p> + <small>{% include docs_version_badge.html version="4.0" %}</small> + </td> + <td> + <p>Week year which may differ from the month year for up to three days at the start of January and end of December</p> + </td> + </tr> + <tr> + <td> + <p><code>week</code></p> + <small>{% include docs_version_badge.html version="4.0" %}</small> + </td> + <td> + <p>Week number of the current year, starting with the first week having a majority of its days in January. (01..53)</p> + </td> + </tr> + <tr> + <td> + <p><code>w_day</code></p> + <small>{% include docs_version_badge.html version="4.0" %}</small> + </td> + <td> + <p>Day of the week, starting with Monday. (1..7)</p> + </td> + </tr> + <tr> + <td> + <p><code>short_day</code></p> + <small>{% include docs_version_badge.html version="4.0" %}</small> + </td> + <td> + <p>Three-letter weekday abbreviation, e.g. “Sun”.</p> + </td> + </tr> + <tr> + <td> + <p><code>long_day</code></p> + <small>{% include docs_version_badge.html version="4.0" %}</small> + </td> + <td> + <p>Weekday name, e.g. “Sunday”.</p> + </td> + </tr> + <tr> + <td> + <p><code>hour</code></p> + </td> + <td> + <p> + Hour of the day, 24-hour clock, zero-padded from the post’s + <code>date</code> front matter. (00..23) + </p> + </td> + </tr> + <tr> + <td> + <p><code>minute</code></p> + </td> + <td> + <p> + Minute of the hour from the post’s <code>date</code> front matter. (00..59) + </p> + </td> + </tr> + <tr> + <td> + <p><code>second</code></p> + </td> + <td> + <p> + Second of the minute from the post’s <code>date</code> front matter. (00..59) + </p> + </td> + </tr> + <tr> + <td> + <p><code>title</code></p> + </td> + <td> + <p> + Title from the document’s filename. May be overridden via + the document’s <code>slug</code> front matter. Preserves case from the source. + </p> + </td> + </tr> + <tr> + <td> + <p><code>slug</code></p> + </td> + <td> + <p> + Slugified title from the document’s filename (any character + except numbers and letters is replaced as hyphen). May be + overridden via the document’s <code>slug</code> front matter. + </p> + </td> + </tr> + <tr> + <td> + <p><code>categories</code></p> + </td> + <td> + <p> + The specified categories for this post. If a post has multiple + categories, Jekyll will create a hierarchy (e.g. <code>/category1/category2</code>). + Also Jekyll automatically parses out double slashes in the URLs, + so if no categories are present, it will ignore this. + </p> + </td> + </tr> + <tr> + <td> + <p><code>slugified_categories</code></p> + <small>{% include docs_version_badge.html version="4.1" %}</small> + </td> + <td> + <p> + The specified categories for this post but <em>slugified</em>. If a category is a + composite of multiple words, Jekyll will downcase all alphabets and replace any + non-alphanumeric character with a hyphen. (e.g. <code>"Work 2 Progress"</code> + will be converted into <code>"work-2-progress"</code>) + </p> + <p> + If a post has multiple categories, Jekyll will create a hierarchy + (e.g. <code>/work-2-progress/category2</code>). + Also Jekyll automatically parses out double slashes in the URLs, + so if no categories are present, it will ignore this. + </p> + </td> + </tr> + <tr> + <td> + <p><code>:output_ext</code></p> + </td> + <td> + <p>Extension of the output file. (Included by default and usually unnecessary.)</p> + </td> + </tr> + </tbody> +</table> +</div> + +### Built-in formats + +For posts, Jekyll also provides the following built-in styles for convenience: + +<div class="mobile-side-scroller"> +<table> + <thead> + <tr> + <th>Permalink Style</th> + <th>URL Template</th> + </tr> + </thead> + <tbody> + <tr> + <td> + <p><code>date</code></p> + </td> + <td> + <p><code>/:categories/:year/:month/:day/:title:output_ext</code></p> + </td> + </tr> + <tr> + <td> + <p><code>pretty</code></p> + </td> + <td> + <p><code>/:categories/:year/:month/:day/:title/</code></p> + </td> + </tr> + <tr> + <td> + <p><code>ordinal</code></p> + </td> + <td> + <p><code>/:categories/:year/:y_day/:title:output_ext</code></p> + </td> + </tr> + <tr> + <td> + <p><code>weekdate</code></p> + <small>{% include docs_version_badge.html version="4.0" %}</small> + </td> + <td> + <p> + <code>/:categories/:year/W:week/:short_day/:title:output_ext</code><br/> + <small>(<code>W</code> will be prefixed to the value of <code>:week</code>)</small> + </p> + </td> + </tr> + <tr> + <td> + <p><code>none</code></p> + </td> + <td> + <p><code>/:categories/:title:output_ext</code></p> + </td> + </tr> + </tbody> +</table> +</div> + +Rather than typing `permalink: /:categories/:year/:month/:day/:title/`, you can just type `permalink: pretty`. + +<div class="note info"> +<h5>Specifying permalinks through the front matter</h5> +<p>Built-in permalink styles are not recognized in front matter. As a result, <code>permalink: pretty</code> will not work.</p> +</div> + +### Collections + +For collections (including `posts` and `drafts`), you have the option to override +the global permalink in the collection configuration in `_config.yml`: + +```yaml +collections: + my_collection: + output: true + permalink: /:collection/:name +``` + +Collections have the following placeholders available: + +<div class="mobile-side-scroller"> +<table> + <thead> + <tr> + <th>Variable</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td> + <p><code>:collection</code></p> + </td> + <td> + <p>Label of the containing collection.</p> + </td> + </tr> + <tr> + <td> + <p><code>:path</code></p> + </td> + <td> + <p> + Path to the document relative to the collection's directory, + including base filename of the document. + </p> + </td> + </tr> + <tr> + <td> + <p><code>:name</code></p> + </td> + <td> + <p>The document's base filename, with every sequence of spaces + and non-alphanumeric characters replaced by a hyphen.</p> + </td> + </tr> + <tr> + <td> + <p><code>:title</code></p> + </td> + <td> + <p> + The <code>:title</code> template variable will take the + <code>slug</code> <a href="/docs/front-matter/">front matter</a> + variable value if any is present in the document; if none is + defined then <code>:title</code> will be equivalent to + <code>:name</code>, aka the slug generated from the filename. + Preserves case from the source. + </p> + </td> + </tr> + <tr> + <td> + <p><code>:output_ext</code></p> + </td> + <td> + <p>Extension of the output file. (Included by default and usually unnecessary.)</p> + </td> + </tr> + </tbody> +</table> +</div> + +### Pages + +For pages, you have to use front matter to override the global permalink, +and if you set a permalink via front matter defaults in `_config.yml`, +it will be ignored. + +Pages have the following placeholders available: + +<div class="mobile-side-scroller"> +<table> + <thead> + <tr> + <th>Variable</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td> + <p><code>:path</code></p> + </td> + <td> + <p> + Path to the page relative to the site's source directory, excluding + base filename of the page. + </p> + </td> + </tr> + <tr> + <td> + <p><code>:basename</code></p> + </td> + <td> + <p>The page's base filename</p> + </td> + </tr> + <tr> + <td> + <p><code>:output_ext</code></p> + </td> + <td> + <p> + Extension of the output file. (Included by default and usually + unnecessary.) + </p> + </td> + </tr> + </tbody> +</table> +</div> diff --git a/docs/_docs/plugins.md b/docs/_docs/plugins.md new file mode 100644 index 0000000..52a8694 --- /dev/null +++ b/docs/_docs/plugins.md @@ -0,0 +1,20 @@ +--- +title: Plugins +permalink: /docs/plugins/ +--- + +Jekyll has a plugin system with hooks that allow you to create custom generated +content specific to your site. You can run custom code for your site without +having to modify the Jekyll source itself. + +{: .note .info} +You can add specific plugins to the `whitelist` key in `_config.yml` to allow them to run in safe mode. + +* [Installation]({{ '/docs/plugins/installation/' | relative_url }}) - How to install plugins +* [Your first plugin]({{ '/docs/plugins/your-first-plugin/' | relative_url }}) - How to write plugins +* [Generators]({{ '/docs/plugins/generators/' | relative_url }}) - Create additional content on your site +* [Converters]({{ '/docs/plugins/converters/' | relative_url }}) - Change a markup language into another format +* [Commands]({{ '/docs/plugins/commands/' | relative_url }}) - Extend the `jekyll` executable with subcommands +* [Tags]({{ '/docs/plugins/tags/' | relative_url }}) - Create custom Liquid tags +* [Filters]({{ '/docs/plugins/filters/' | relative_url }}) - Create custom Liquid filters +* [Hooks]({{ '/docs/plugins/hooks/' | relative_url }}) - Fine-grained control to extend the build process diff --git a/docs/_docs/plugins/commands.md b/docs/_docs/plugins/commands.md new file mode 100644 index 0000000..0666c77 --- /dev/null +++ b/docs/_docs/plugins/commands.md @@ -0,0 +1,62 @@ +--- +title: Commands +permalink: /docs/plugins/commands/ +--- +As of version {% include docs_version_badge.html version="2.5.0"%}, Jekyll can be extended with plugins which provide +subcommands for the `jekyll` executable. This is possible by including the +relevant plugins in a `Gemfile` group called `:jekyll_plugins`: + +```ruby +group :jekyll_plugins do + gem "my_fancy_jekyll_plugin" +end +``` + +Each `Command` must be a subclass of the `Jekyll::Command` class and must +contain one class method: `init_with_program`. An example: + +```ruby +class MyNewCommand < Jekyll::Command + class << self + def init_with_program(prog) + prog.command(:new) do |c| + c.syntax "new [options]" + c.description 'Create a new Jekyll site.' + + c.option 'dest', '-d DEST', 'Where the site should go.' + + c.action do |args, options| + Jekyll::Site.new_site_at(options['dest']) + end + end + end + end +end +``` + +Commands should implement this single class method: + +<div class="mobile-side-scroller"> +<table> + <thead> + <tr> + <th>Method</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td> + <p><code>init_with_program</code></p> + </td> + <td><p> + This method accepts one parameter, the + <code><a href="https://github.com/jekyll/mercenary#readme">Mercenary::Program</a></code> + instance, which is the Jekyll program itself. Upon the program, + commands may be created using the above syntax. For more details, + visit the Mercenary repository on GitHub.com. + </p></td> + </tr> + </tbody> +</table> +</div> diff --git a/docs/_docs/plugins/converters.md b/docs/_docs/plugins/converters.md new file mode 100644 index 0000000..31c99e9 --- /dev/null +++ b/docs/_docs/plugins/converters.md @@ -0,0 +1,91 @@ +--- +title: Converters +permalink: /docs/plugins/converters/ +--- + +If you have a new markup language you’d like to use with your site, you can +include it by implementing your own converter. Both the Markdown and +[Textile](https://github.com/jekyll/jekyll-textile-converter) markup +languages are implemented using this method. + +<div class="note info"> + <h5>Remember your Front Matter</h5> + <p> + Jekyll will only convert files that have a YAML header at the top, even for + converters you add using a plugin. + </p> +</div> + +Below is a converter that will take all posts ending in `.upcase` and process +them using the `UpcaseConverter`: + +```ruby +module Jekyll + class UpcaseConverter < Converter + safe true + priority :low + + def matches(ext) + ext =~ /^\.upcase$/i + end + + def output_ext(ext) + ".html" + end + + def convert(content) + content.upcase + end + end +end +``` + +Converters should implement at a minimum 3 methods: + +<div class="mobile-side-scroller"> +<table> + <thead> + <tr> + <th>Method</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td> + <p><code>matches</code></p> + </td> + <td><p> + Does the given extension match this converter’s list of acceptable + extensions? Takes one argument: the file’s extension (including the + dot). Must return <code>true</code> if it matches, <code>false</code> + otherwise. + </p></td> + </tr> + <tr> + <td> + <p><code>output_ext</code></p> + </td> + <td><p> + The extension to be given to the output file (including the dot). + Usually this will be <code>".html"</code>. + </p></td> + </tr> + <tr> + <td> + <p><code>convert</code></p> + </td> + <td><p> + Logic to do the content conversion. Takes one argument: the raw content + of the file (without front matter). Must return a String. + </p></td> + </tr> + </tbody> +</table> +</div> + +In our example, `UpcaseConverter#matches` checks if our filename extension is +`.upcase`, and will render using the converter if it is. It will call +`UpcaseConverter#convert` to process the content. In our simple converter we’re +simply uppercasing the entire content string. Finally, when it saves the page, +it will do so with a `.html` extension. diff --git a/docs/_docs/plugins/filters.md b/docs/_docs/plugins/filters.md new file mode 100644 index 0000000..a07df69 --- /dev/null +++ b/docs/_docs/plugins/filters.md @@ -0,0 +1,32 @@ +--- +title: Filters +permalink: /docs/plugins/filters/ +--- + +Filters are modules that export their methods to liquid. +All methods will have to take at least one parameter which represents the input +of the filter. The return value will be the output of the filter. + +```ruby +module Jekyll + module AssetFilter + def asset_url(input) + "http://www.example.com/#{input}?#{Time.now.to_i}" + end + end +end + +Liquid::Template.register_filter(Jekyll::AssetFilter) +``` + +For more details on creating custom Liquid Filters, head to the [Liquid docs](https://github.com/Shopify/liquid/wiki/Liquid-for-Programmers#create-your-own-filters). + +<div class="note"> + <h5>ProTip™: Access the site object using Liquid</h5> + <p> + Jekyll lets you access the <code>site</code> object through the + <code>@context.registers</code> feature of Liquid at <code>@context.registers[:site]</code>. For example, you can + access the global configuration file <code>_config.yml</code> using + <code>@context.registers[:site].config</code>. + </p> +</div> diff --git a/docs/_docs/plugins/generators.md b/docs/_docs/plugins/generators.md new file mode 100644 index 0000000..06716eb --- /dev/null +++ b/docs/_docs/plugins/generators.md @@ -0,0 +1,144 @@ +--- +title: Generators +permalink: /docs/plugins/generators/ +--- + +You can create a generator when you need Jekyll to create additional content based on your own rules. + +A generator is a subclass of `Jekyll::Generator` that defines a `generate` method, which receives an instance of +[`Jekyll::Site`]({{ site.repository }}/blob/master/lib/jekyll/site.rb). The return value of `generate` is ignored. + +Generators run after Jekyll has made an inventory of the existing content, and before the site is generated. Pages with +front matter are stored as instances of [`Jekyll::Page`]({{ site.repository }}/blob/master/lib/jekyll/page.rb) and are +available via `site.pages`. Static files become instances of +[`Jekyll::StaticFile`]({{ site.repository }}/blob/master/lib/jekyll/static_file.rb) +and are available via `site.static_files`. See [the Variables documentation page](/docs/variables/) and +[`Jekyll::Site`]({{ site.repository }}/blob/master/lib/jekyll/site.rb) for details. + +In the following example, the generator will inject values computed at build time for template variables. The template +named `reading.html` has two undefined variables `ongoing` and `done` that will be defined or assigned a value when +the generator runs: + +```ruby +module Reading + class Generator < Jekyll::Generator + def generate(site) + book_data = site.data['books'] + ongoing = book_data.select { |book| book['status'] == 'ongoing' } + done = book_data.select { |book| book['status'] == 'finished' } + + # get template + reading = site.pages.find { |page| page.name == 'reading.html'} + + # inject data into template + reading.data['ongoing'] = ongoing + reading.data['done'] = done + end + end +end +``` + +The following example is a more complex generator that generates new pages. + +In this example, the aim of the generator is to create a page for each category registered in the `site`. The pages are +created at runtime, so their contents, front matter and other attributes need to be designed by the plugin itself. +* The pages are intended to render a list of all documents under a given category. So the basename of the rendered file +would be better as `index.html`. +* Having the ability to configure the pages via [front matter defaults](/docs/configuration/front-matter-defaults/) +would be awesome! So assigning a particular `type` to these pages would be beneficial. + +```ruby +module SamplePlugin + class CategoryPageGenerator < Jekyll::Generator + safe true + + def generate(site) + site.categories.each do |category, posts| + site.pages << CategoryPage.new(site, category, posts) + end + end + end + + # Subclass of `Jekyll::Page` with custom method definitions. + class CategoryPage < Jekyll::Page + def initialize(site, category, posts) + @site = site # the current site instance. + @base = site.source # path to the source directory. + @dir = category # the directory the page will reside in. + + # All pages have the same filename, so define attributes straight away. + @basename = 'index' # filename without the extension. + @ext = '.html' # the extension. + @name = 'index.html' # basically @basename + @ext. + + # Initialize data hash with a key pointing to all posts under current category. + # This allows accessing the list in a template via `page.linked_docs`. + @data = { + 'linked_docs' => posts + } + + # Look up front matter defaults scoped to type `categories`, if given key + # doesn't exist in the `data` hash. + data.default_proc = proc do |_, key| + site.frontmatter_defaults.find(relative_path, :categories, key) + end + end + + # Placeholders that are used in constructing page URL. + def url_placeholders + { + :category => @dir, + :basename => basename, + :output_ext => output_ext, + } + end + end +end +``` + +The generated pages can now be set up to use a particular layout or output at a particular path in the destination +directory all via the config file using front matter defaults. For example: + +```yaml +# _config.yml + +defaults: + - scope: + type: categories # select all category pages + values: + layout: category_page + permalink: categories/:category/ +``` + +## Technical Aspects + +Generators need to implement only one method: + +<div class="mobile-side-scroller"> +<table> + <thead> + <tr> + <th>Method</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td> + <p><code>generate</code></p> + </td> + <td> + <p>Generates content as a side-effect.</p> + </td> + </tr> + </tbody> +</table> +</div> + +If your generator is contained within a single file, it can be named whatever you want but it should have an `.rb` +extension. If your generator is split across multiple files, it should be packaged as a Rubygem to be published at +https://rubygems.org/. In this case, the name of the gem depends on the availability of the name at that site because +no two gems can have the same name. + +By default, Jekyll looks for generators in the `_plugins` directory. However, you can change the default directory by +assigning the desired name to the key `plugins_dir` in the config file. diff --git a/docs/_docs/plugins/hooks.md b/docs/_docs/plugins/hooks.md new file mode 100644 index 0000000..e3447cb --- /dev/null +++ b/docs/_docs/plugins/hooks.md @@ -0,0 +1,289 @@ +--- +title: Hooks +permalink: /docs/plugins/hooks/ +--- + +Using hooks, your plugin can exercise fine-grained control over various aspects of the build process. If your plugin defines any hooks, Jekyll +will call them at pre-defined points. + +Hooks are registered to an owner and an event name. To register one, you call `Jekyll::Hooks.register`, and pass the hook owner, event name, +and code to call whenever the hook is triggered. For example, if you want to execute some custom functionality every time Jekyll renders a +page, you could register a hook like this: + +```ruby +Jekyll::Hooks.register :pages, :post_render do |page| + # code to call after Jekyll renders a page +end +``` + +*Note: The `:post_convert` events mentioned hereafter is a feature introduced in v4.2.0.* + +Out of the box, Jekyll has pre-defined hook points for owners `:site`, `:pages`, `:documents` and `:clean`. Additionally, the hook points +defined for `:documents` can be utilized for individual collections only by invoking the collection type instead. i.e. `:posts` for documents +in collection `_posts` and `:movies` for documents in collection `_movies`. In all cases, Jekyll calls your hooks with the owner object as the +first callback parameter. + +Every registered hook owner supports the following events — `:post_init`, `:pre_render`, `:post_convert`, `:post_render`, `:post_write` +— however, the `:site` owner is set up to *respond* to *special event names*. Refer to the subsequent section for details. + +All `:pre_render` hooks and the `:site, :post_render` hook will also provide a `payload` hash as a second parameter. While in the case of +`:pre_render` events, the payload gives you full control over the variables that are available during rendering, with the `:site, :post_render` +event, the payload contains final values after rendering all the site (useful for sitemaps, feeds, etc). + +## Built-in Hook Owners and Events +The complete list of available hooks: + +<div class="mobile-side-scroller"> +<table id="builtin-hooks"> + <thead> + <tr> + <th>Owner</th> + <th>Event</th> + <th>Triggered at</th> + </tr> + </thead> + <tbody> + <tr> + <td rowspan="6"> + <p><code>:site</code></p> + <p>Encompasses the entire site</p> + </td> + <td> + <p><code>:after_init</code></p> + </td> + <td> + <p>Just after the site initializes. Good for modifying the configuration of the site. Triggered once per build / serve session</p> + </td> + </tr> + <tr> + <td> + <p><code>:after_reset</code></p> + </td> + <td> + <p>Just after the site resets during regeneration</p> + </td> + </tr> + <tr> + <td> + <p><code>:post_read</code></p> + </td> + <td> + <p>After all source files have been read and loaded from disk</p> + </td> + </tr> + <tr> + <td> + <p><code>:pre_render</code></p> + </td> + <td> + <p>Just before rendering the whole site</p> + </td> + </tr> + <tr> + <td> + <p><code>:post_render</code></p> + </td> + <td> + <p>After rendering the whole site, but before writing any files</p> + </td> + </tr> + <tr> + <td> + <p><code>:post_write</code></p> + </td> + <td> + <p>After writing all of the rendered files to disk</p> + </td> + </tr> + <tr> + <td rowspan="5"> + <p><code>:pages</code></p> + <p>Allows fine-grained control over all pages in the site</p> + </td> + <td> + <p><code>:post_init</code></p> + </td> + <td> + <p>Whenever a page is initialized</p> + </td> + </tr> + <tr> + <td> + <p><code>:pre_render</code></p> + </td> + <td> + <p>Just before rendering a page</p> + </td> + </tr> + <tr> + <td> + <p><code>:post_convert</code></p> + </td> + <td> + <p>After converting the page content, but before rendering the page layout</p> + </td> + </tr> + <tr> + <td> + <p><code>:post_render</code></p> + </td> + <td> + <p>After rendering a page, but before writing it to disk</p> + </td> + </tr> + <tr> + <td> + <p><code>:post_write</code></p> + </td> + <td> + <p>After writing a page to disk</p> + </td> + </tr> + <tr> + <td rowspan="5"> + <p><code>:documents</code></p> + <p>Allows fine-grained control over all documents in the site including posts and documents in user-defined collections</p> + </td> + <td> + <p><code>:post_init</code></p> + </td> + <td> + <p>Whenever any document is initialized</p> + </td> + </tr> + <tr> + <td> + <p><code>:pre_render</code></p> + </td> + <td> + <p>Just before rendering a document</p> + </td> + </tr> + <tr> + <td> + <p><code>:post_convert</code></p> + </td> + <td> + <p> + After converting the document content, but before rendering the document + layout + </p> + </td> + </tr> + <tr> + <td> + <p><code>:post_render</code></p> + </td> + <td> + <p>After rendering a document, but before writing it to disk</p> + </td> + </tr> + <tr> + <td> + <p><code>:post_write</code></p> + </td> + <td> + <p>After writing a document to disk</p> + </td> + </tr> + <tr> + <td rowspan="5"> + <p><code>:posts</code></p> + <p>Allows fine-grained control over all posts in the site without affecting documents in user-defined collections</p> + </td> + <td> + <p><code>:post_init</code></p> + </td> + <td> + <p>Whenever a post is initialized</p> + </td> + </tr> + <tr> + <td> + <p><code>:pre_render</code></p> + </td> + <td> + <p>Just before rendering a post</p> + </td> + </tr> + <tr> + <td> + <p><code>:post_convert</code></p> + </td> + <td> + <p>After converting the post content, but before rendering the postlayout</p> + </td> + </tr> + <tr> + <td> + <p><code>:post_render</code></p> + </td> + <td> + <p>After rendering a post, but before writing it to disk</p> + </td> + </tr> + <tr> + <td> + <p><code>:post_write</code></p> + </td> + <td> + <p>After writing a post to disk</p> + </td> + </tr> + <tr> + <td> + <p><code>:clean</code></p> + <p>Fine-grained control on the list of obsolete files determined to be deleted during the site's cleanup phase.</p> + </td> + <td> + <p><code>:on_obsolete</code></p> + </td> + <td> + <p>During the cleanup of a site's destination before it is built</p> + </td> + </tr> + </tbody> +</table> +</div> + +## Hooks for custom Jekyll objects + +You can also register and trigger hooks for Jekyll objects introduced by your plugin. All it takes is placing `trigger` calls under a suitable +`owner` name, at positions desired within your custom class and registering the `owner` by your plugin. + +To illustrate, consider the following plugin that implements custom functionality for every custom `Excerpt` object initialized: + +```ruby +module Foobar + class HookedExcerpt < Jekyll::Excerpt + def initialize(doc) + super + trigger_hooks(:post_init) + end + + def output + @output ||= trigger_hooks(:post_render, renderer.run) + end + + def renderer + @renderer ||= Jekyll::Renderer.new( + doc.site, self, site.site_payload + ) + end + + def trigger_hooks(hook_name, *args) + Jekyll::Hooks.trigger :excerpts, hook_name, self, *args + end + end +end + +Jekyll::Hooks.register :excerpts, :post_init do |excerpt| + Jekyll.logger.debug "Initialized:", + "Hooked Excerpt for #{excerpt.doc.inspect}" +end + +Jekyll::Hooks.register :excerpts, :post_render do |excerpt, output| + return output unless excerpt.doc.type == :posts + Foobar.transform(output) +end +``` diff --git a/docs/_docs/plugins/installation.md b/docs/_docs/plugins/installation.md new file mode 100644 index 0000000..dcfc4e4 --- /dev/null +++ b/docs/_docs/plugins/installation.md @@ -0,0 +1,125 @@ +--- +title: Plugins +permalink: /docs/plugins/installation/ +--- + +Jekyll has built-in support for using plugins to extend the core functionality. + +Primarily, any file with extension `.rb` placed within a `_plugins` directory at the root of the site's `source`, will be automatically loaded +during a build session. + +This behavior can be configured as follows: + +- The `_plugins` directory may be changed either directly via the command-line or via the configuration file(s). +- Plugins in the `_plugins` directory (or its equivalent(s)) will not be loaded when Jekyll is running in `safe` mode. +- This route cannot be used to extend the Jekyll CLI. + +To work with plugins packaged as gems, one has to list the desired gems in the configuration file under a top-level key named `plugins`. +Additionally, if you're building in `safe` mode, the gem needs to be listed under a top-level key named `whitelist`. For example: + +```yaml +plugins: + - jekyll-gist + - jekyll-coffeescript + - jekyll-seo-tag + - some-other-jekyll-plugin + +# Enable safe mode +safe: true + +# Whitelist plugins under safe mode. +# Note that `some-other-jekyll-plugin` is not listed here. Therefore, +# it will not be loaded under safe mode. +whitelist: + - jekyll-gist + - jekyll-coffeescript + - jekyll-seo-tag +``` + +In the absence of a Gemfile, one must manually ensure that listed plugins have been installed prior to invoking Jekyll. For example, the +latest versions of gems in the above list may be installed to a system-wide location by running: + +```sh +gem install jekyll-gist jekyll-coffeescript jekyll-remote-theme some-other-jekyll-plugin +``` + +## Using a Gemfile + +The maintenance of various gem dependencies may be greatly simplified by using a Gemfile (usually at the root of the site's source) in +conjunction with a Rubygem named `bundler`. The Gemfile however **should** list all the primary dependencies of your site, including Jekyll +itself, not just gem-based plugins of the site because Bundler narrows the scope of installed gems to just *runtime dependencies* resolved by +evaluating the Gemfile. For example: + +```ruby +source "https://rubygems.org" + +# Use the latest version. +gem "jekyll" + +# The theme of current site, locked to a certain version. +gem "minima", "2.4.1" + +# Plugins of this site loaded during a build with proper +# site configuration. +gem "jekyll-gist" +gem "jekyll-coffeescript" +gem "jekyll-seo-tag", "~> 1.5" +gem "some-other-jekyll-plugin" + +# A dependency of a custom-plugin inside `_plugins` directory. +gem "nokogiri", "~> 1.11" +``` + +The gems listed in the Gemfile can be collectively installed by simply running `bundle install`. + +### The `:jekyll_plugins` Gemfile group +{: #the-jekyll_plugins-group} + +Jekyll gives a special treatment to gems listed as part of the `:jekyll_plugins` group in a Gemfile. Any gem under this group is loaded at +the very beginning of any Jekyll process, irrespective of the `--safe` CLI flag or entries in the configuration file(s). + +While this route allows one to enhance Jekyll's CLI with additional subcommands and options, or avoid having to list gems in the configuration +file, the downside is the necessity to be mindful of what gems are included in the group. For example: + +```ruby +source "https://rubygems.org" + +# Use the latest version. +gem "jekyll" + +# The theme of current site, locked to a certain version. +gem "minima", "2.4.1" + +# Plugins of this site loaded only if configured correctly. +gem "jekyll-gist" +gem "jekyll-coffeescript" + +# Gems loaded irrespective of site configuration. +group :jekyll_plugins do + gem "jekyll-cli-plus" + gem "jekyll-seo-tag", "~> 1.5" + gem "some-other-jekyll-plugin" +end +``` + +<div class="note info"> + <h5>Plugins on GitHub Pages</h5> + <p> + <a href="https://pages.github.com/">GitHub Pages</a> is powered by Jekyll. All GitHub Pages sites are generated using the + <code>--safe</code> option to disable plugins (with the exception of some + <a href="https://pages.github.com/versions">whitelisted plugins</a>) for security reasons. Unfortunately, this means your plugins won't + work if you’re deploying via GitHub Pages.<br><br> + You can still use GitHub Pages to publish your site, but you’ll need to build the site locally and push the generated files to your + GitHub repository instead of the Jekyll source files. + </p> +</div> + +<div class="note"> + <h5> + <code>_plugins</code>, <code>_config.yml</code> and <code>Gemfile</code> can be used simultaneously + </h5> + <p> + You may use any of the aforementioned plugin routes simultaneously in the same site if you so choose. + Use of one does not restrict the use of the others. + </p> +</div> diff --git a/docs/_docs/plugins/tags.md b/docs/_docs/plugins/tags.md new file mode 100644 index 0000000..3695a94 --- /dev/null +++ b/docs/_docs/plugins/tags.md @@ -0,0 +1,114 @@ +--- +title: Tags +permalink: /docs/plugins/tags/ +--- + +If you’d like to include custom liquid tags in your site, you can do so by +hooking into the tagging system. Built-in examples added by Jekyll include the +`highlight` and `include` tags. Below is an example of a custom liquid tag that +will output the time the page was rendered: + +```ruby +module Jekyll + class RenderTimeTag < Liquid::Tag + + def initialize(tag_name, text, tokens) + super + @text = text + end + + def render(context) + "#{@text} #{Time.now}" + end + end +end + +Liquid::Template.register_tag('render_time', Jekyll::RenderTimeTag) +``` + +At a minimum, liquid tags must implement: + +<div class="mobile-side-scroller"> +<table> + <thead> + <tr> + <th>Method</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td> + <p><code>render</code></p> + </td> + <td> + <p>Outputs the content of the tag.</p> + </td> + </tr> + </tbody> +</table> +</div> + +You must also register the custom tag with the Liquid template engine as +follows: + +```ruby +Liquid::Template.register_tag('render_time', Jekyll::RenderTimeTag) +``` + +In the example above, we can place the following tag anywhere in one of our +pages: + +{% raw %} +```liquid +<p>{% render_time page rendered at: %}</p> +``` +{% endraw %} + +And we would get something like this on the page: + +```html +<p>page rendered at: Tue June 22 23:38:47 –0500 2010</p> +``` + +## Tag Blocks + +The `render_time` tag seen above can also be rewritten as a tag block by +inheriting the `Liquid::Block` class. Look at the example below: + +```ruby +module Jekyll + class RenderTimeTagBlock < Liquid::Block + + def render(context) + text = super + "<p>#{text} #{Time.now}</p>" + end + + end +end + +Liquid::Template.register_tag('render_time', Jekyll::RenderTimeTagBlock) +``` + +We can now use the tag block anywhere: + +{% raw %} +```liquid +{% render_time %} +page rendered at: +{% endrender_time %} +``` +{% endraw %} + +And we would still get the same output as above on the page: + +```html +<p>page rendered at: Tue June 22 23:38:47 –0500 2010</p> +``` + +{: .note .info} +In the above example, the tag block and the tag are both registered with +the name <code>render_time</code>, but to register a tag and a tag block using +the same name in the same project is not recommended as this may lead to +conflicts. diff --git a/docs/_docs/plugins/your-first-plugin.md b/docs/_docs/plugins/your-first-plugin.md new file mode 100644 index 0000000..e20bdda --- /dev/null +++ b/docs/_docs/plugins/your-first-plugin.md @@ -0,0 +1,142 @@ +--- +title: Your first plugin +permalink: /docs/plugins/your-first-plugin/ +--- + +Plugins allow you to extend Jekyll's behavior to fit your needs. There are six +types of plugins in Jekyll. + +## Generators + +[Generators](/docs/plugins/generators/) create content on your site. +For example: + +* [jekyll-feed](https://github.com/jekyll/jekyll-feed) creates an Atom feed of +blog posts. +* [jekyll-archives](https://github.com/jekyll/jekyll-archives) creates archive +pages for blog categories and tags. +* [jekyll-sitemap](https://github.com/jekyll/jekyll-sitemap) creates a sitemap. + +## Converters + +[Converters](/docs/plugins/converters/) change a markup language into another +format. For example: + +* [jekyll-textile-converter](https://github.com/jekyll/jekyll-textile-converter) +converts textile to HTML. +* [jekyll-coffeescript](https://github.com/jekyll/jekyll-coffeescript) converts +Coffeescript to JavaScript. +* [jekyll-opal](https://github.com/jekyll/jekyll-opal) converts Ruby to +JavaScript. + +## Commands + +[Commands](/docs/plugins/commands/) extend the `jekyll` executable with +subcommands. For example: + +* [jekyll-compose](https://github.com/jekyll/jekyll-compose) adds subcommands +for creating a post, page or draft. + +## Tags + +[Tags](/docs/plugins/tags/) create custom Liquid tags. For example: + +* [jekyll-youtube](https://github.com/dommmel/jekyll-youtube) embeds a YouTube +video. +* [jekyll-asset-path-plugin](https://github.com/samrayner/jekyll-asset-path-plugin) +outputs a relative URL for assets. +* [jekyll-swfobject](https://github.com/sectore/jekyll-swfobject) embeds a SWF +object. + +## Filters + +[Filters](/docs/plugins/filters/) create custom Liquid filters. For example: + +* [jekyll-time-ago](https://github.com/markets/jekyll-timeago) - The distance +between two dates in words. +* [jekyll-toc](https://github.com/toshimaru/jekyll-toc) - Generates a table of +content. +* [jekyll-email-protect](https://github.com/vwochnik/jekyll-email-protect) - +Obfuscates emails to protect them from spam bots. + +## Hooks + +[Hooks](/docs/plugins/hooks/) give fine-grained control to extend the build +process. For example: + +* [jemoji](https://github.com/jekyll/jemoji) Display emojis :+1: +* [jekyll-mentions](https://github.com/jekyll/jekyll-mentions) turns mentions @jekyll into links +* [jekyll-spaceship](https://github.com/jeffreytse/jekyll-spaceship) - advanced example. Provides +powerful supports for table, mathjax, plantuml, video, etc. + +## Flags + +There are two flags to be aware of when writing a plugin: + +<div class="mobile-side-scroller"> +<table> + <thead> + <tr> + <th>Flag</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td> + <p><code>safe</code></p> + </td> + <td> + <p> + A boolean flag that informs Jekyll whether this plugin may be safely + executed in an environment where arbitrary code execution is not + allowed. This is used by GitHub Pages to determine which core plugins + may be used, and which are unsafe to run. If your plugin does not + allow for arbitrary code execution, set this to <code>true</code>. + GitHub Pages still won’t load your plugin, but if you submit it for + inclusion in core, it’s best for this to be correct! + </p> + </td> + </tr> + <tr> + <td> + <p><code>priority</code></p> + </td> + <td> + <p> + This flag determines what order the plugin is loaded in. Valid values + are: <code>:lowest</code>, <code>:low</code>, <code>:normal</code>, + <code>:high</code>, and <code>:highest</code>. Highest priority + matches are applied first, lowest priority are applied last. + </p> + </td> + </tr> + </tbody> +</table> +</div> + +To use one of the example plugins above as an illustration, here is how you’d +specify these two flags: + +```ruby +module Jekyll + class UpcaseConverter < Converter + safe true + priority :low + ... + end +end +``` + +## Best Practices + +The guides help you with the specifics of creating plugins. We also have some +recommended best practices to help structure your plugin. + +We recommend using a [gem](/docs/ruby-101/#gems) for your plugin. This will +help you manage dependencies, keep separation from your site source code and +allow you to share functionality across multiple projects. For tips on creating +a gem take a look at the +[Ruby gems guide](https://guides.rubygems.org/make-your-own-gem/) or look +through the source code of an existing plugin such as +[jekyll-feed](https://github.com/jekyll/jekyll-feed). diff --git a/docs/_docs/posts.md b/docs/_docs/posts.md new file mode 100644 index 0000000..7167f67 --- /dev/null +++ b/docs/_docs/posts.md @@ -0,0 +1,240 @@ +--- +title: Posts +permalink: /docs/posts/ +redirect_from: + - /docs/drafts/ +--- + +Blogging is baked into Jekyll. You write blog posts as text files and Jekyll +provides everything you need to turn it into a blog. + +## The Posts Folder + +The `_posts` folder is where your blog posts live. You typically write posts +in [Markdown](https://daringfireball.net/projects/markdown/), HTML is +also supported. + +## Creating Posts + +To create a post, add a file to your `_posts` directory with the following +format: + +``` +YEAR-MONTH-DAY-title.MARKUP +``` + +Where `YEAR` is a four-digit number, `MONTH` and `DAY` are both two-digit +numbers, and `MARKUP` is the file extension representing the format used in the +file. For example, the following are examples of valid post filenames: + +``` +2011-12-31-new-years-eve-is-awesome.md +2012-09-12-how-to-write-a-blog.md +``` + +All blog post files must begin with [front matter](/docs/front-matter/) which is +typically used to set a [layout](/docs/layouts/) or other meta data. For a simple +example this can just be empty: + +```markdown +--- +layout: post +title: "Welcome to Jekyll!" +--- + +# Welcome + +**Hello world**, this is my first Jekyll blog post. + +I hope you like it! +``` + +<div class="note"> + <h5>ProTip™: Link to other posts</h5> + <p> + Use the <a href="/docs/liquid/tags/#linking-to-posts"><code>post_url</code></a> + tag to link to other posts without having to worry about the URLs + breaking when the site permalink style changes. + </p> +</div> + +<div class="note info"> + <h5>Be aware of character sets</h5> + <p> + Content processors can modify certain characters to make them look nicer. + For example, the <code>smart</code> extension in Redcarpet converts standard, + ASCII quotation characters to curly, Unicode ones. In order for the browser + to display those characters properly, define the charset meta value by + including <code><meta charset="utf-8"></code> in the + <code><head></code> of your layout. + </p> +</div> + +## Including images and resources + +At some point, you'll want to include images, downloads, or other +digital assets along with your text content. One common solution is to create +a folder in the root of the project directory called something like `assets`, +into which any images, files or other resources are placed. Then, from within +any post, they can be linked to using the site’s root as the path for the asset +to include. The best way to do this depends on the way your site’s (sub)domain +and path are configured, but here are some simple examples in Markdown: + +Including an image asset in a post: + +```markdown +... which is shown in the screenshot below: + +``` + +Linking to a PDF for readers to download: + +```markdown +... you can [get the PDF](/assets/mydoc.pdf) directly. +``` + +## Displaying an index of posts + +Creating an index of posts on another page should be easy thanks to +[Liquid](https://shopify.github.io/liquid/) and its tags. Here’s a +simple example of how to create a list of links to your blog posts: + +{% raw %} +```liquid +<ul> + {% for post in site.posts %} + <li> + <a href="{{ post.url }}">{{ post.title }}</a> + </li> + {% endfor %} +</ul> +``` +{% endraw %} + +You have full control over how (and where) you display your posts, +and how you structure your site. You should read more about [how templates +work](/docs/templates/) with Jekyll if you want to know more. + +Note that the `post` variable only exists inside the `for` loop above. If +you wish to access the currently-rendering page/posts's variables (the +variables of the post/page that has the `for` loop in it), use the `page` +variable instead. + +## Tags and Categories + +Jekyll has first class support for *tags* and *categories* in blog posts. + +### Tags + +Tags for a post are defined in the post's front matter using either the key +`tag` for a single entry or `tags` for multiple entries. <br/> Since Jekyll +expects multiple items mapped to the key `tags`, it will automatically *split* +a string entry if it contains whitespace. For example, while front matter +`tag: classic hollywood` will be processed into a singular entity +`"classic hollywood"`, front matter `tags: classic hollywood` will be processed +into an array of entries `["classic", "hollywood"]`. + +Irrespective of the front matter key chosen, Jekyll stores the metadata mapped +to the plural key which is exposed to Liquid templates. + +All tags registered in the current site are exposed to Liquid templates via +`site.tags`. Iterating over `site.tags` on a page will yield another array with +two items, where the first item is the name of the tag and the second item being +*an array of posts* with that tag. + +{% raw %} +```liquid +{% for tag in site.tags %} + <h3>{{ tag[0] }}</h3> + <ul> + {% for post in tag[1] %} + <li><a href="{{ post.url }}">{{ post.title }}</a></li> + {% endfor %} + </ul> +{% endfor %} +``` +{% endraw %} + + +### Categories + +Categories of a post work similar to the tags above: + * They can be defined via the front matter using keys `category` or + `categories` (that follow the same logic as for tags) + * All categories registered in the site are exposed to Liquid templates via + `site.categories` which can be iterated over (similar to the loop for tags + above.) + +*The similarity between categories and tags however, ends there.* + +Unlike tags, categories for posts can also be defined by a post's file path. +Any directory above `_posts` will be read-in as a category. For example, +if a post is at path `movies/horror/_posts/2019-05-21-bride-of-chucky.markdown`, +then `movies` and `horror` are automatically registered as categories for that +post. + +When the post also has front matter defining categories, they just get added to +the existing list if not present already. + +The hallmark difference between categories and tags is that categories of a post +may be incorporated into [the generated URL](/docs/permalinks/#global) for the +post, while tags cannot be. + +Therefore, depending on whether front matter has `category: classic hollywood`, +or `categories: classic hollywood`, the example post above would have the URL as +either +`movies/horror/classic%20hollywood/2019/05/21/bride-of-chucky.html` or +`movies/horror/classic/hollywood/2019/05/21/bride-of-chucky.html` respectively. + + +## Post excerpts + +You can access a snippet of a posts's content by using `excerpt` variable on a +post. By default this is the first paragraph of content in the post, however it +can be customized by setting a `excerpt_separator` variable in front matter or +`_config.yml`. + +```markdown +--- +excerpt_separator: <!--more--> +--- + +Excerpt with multiple paragraphs + +Here's another paragraph in the excerpt. +<!--more--> +Out-of-excerpt +``` + +Here's an example of outputting a list of blog posts with an excerpt: + +{% raw %} +```liquid +<ul> + {% for post in site.posts %} + <li> + <a href="{{ post.url }}">{{ post.title }}</a> + {{ post.excerpt }} + </li> + {% endfor %} +</ul> +``` +{% endraw %} + +## Drafts + +Drafts are posts without a date in the filename. They're posts you're still +working on and don't want to publish yet. To get up and running with drafts, +create a `_drafts` folder in your site's root and create your first draft: + +``` +. +├── _drafts +│ └── a-draft-post.md +... +``` + +To preview your site with drafts, run `jekyll serve` or `jekyll build` +with the `--drafts` switch. Each will be assigned the value modification time +of the draft file for its date, and thus you will see currently edited drafts +as the latest posts. diff --git a/docs/_docs/rendering-process.md b/docs/_docs/rendering-process.md new file mode 100644 index 0000000..6657bc5 --- /dev/null +++ b/docs/_docs/rendering-process.md @@ -0,0 +1,28 @@ +--- +--- + +For any Jekyll site, a *build session* consists of discrete phases in the following order --- *setting up plugins, +reading source files, running generators, rendering templates*, and finally *writing files to disk*. + +While the phases above are self-explanatory, the one phase that warrants dissection is *the rendering phase*. + +The rendering phase is further divisible into three optional stages. Every file rendered, passes through one or more of +these stages as determined by the file's content string, front matter and extension. The stages are akin to an assembly +line, with the *output* from a stage being the *input* for the succeeding stage: +- **Interpreting Liquid expressions in the file**<br/> + This stage evaluates Liquid expressions in the current file. By default, the interpretation is *shallow* --- in that + any Liquid expression in resulting output is not further interpreted. Moreover, any Liquid expression in the file's + front matter is left untouched. +- **Unleashing the converters**<br/> + This stage invokes the converter mapped to the current file's extension and converts the input string. This is when + Markdown gets converted into HTML and Sass / Scss into CSS or CoffeeScript into JavaScript, etc, etc. Since this stage + is determined by the file's extension, Markdown or Sass inside a `.html` file will remain untouched. +- **Populating the layouts**<br/> + By this stage, *the source file* is considered rendered and it will not be revisited. However, based on the file's + extension and consequently based on the front matter, it is determined whether to take the *output* string from + the preceding stage and place into layouts or not. Whereas output from Sass files or CoffeeScript files are *never* + placed into a layout, regular text output can go either ways based on whether a layout has been assigned via the front + matter.<br/><br/> + Placement into layouts work similar to how Russian dolls encase the smaller ones within itself or how an oyster + generates a pearl --- the converted output from the preceding stage forms the core and layout(s) are successively + *rendered* separately onto the core. diff --git a/docs/_docs/ruby-101.md b/docs/_docs/ruby-101.md new file mode 100644 index 0000000..264f808 --- /dev/null +++ b/docs/_docs/ruby-101.md @@ -0,0 +1,56 @@ +--- +title: Ruby 101 +permalink: /docs/ruby-101/ +--- + +Jekyll is written in Ruby. If you're new to Ruby, this page helps you learn some of the terminology. + +## Gems + +Gems are code you can include in Ruby projects. Gems package specific functionality. You can share gems across multiple projects or with other people. +Gems can perform actions like: + +* Converting a Ruby object to JSON +* Pagination +* Interacting with APIs such as GitHub + +Jekyll is a gem. Many Jekyll [plugins]({{ '/docs/plugins/' | relative_url }}) are also gems, including +[jekyll-feed](https://github.com/jekyll/jekyll-feed), +[jekyll-seo-tag](https://github.com/jekyll/jekyll-seo-tag) and +[jekyll-archives](https://github.com/jekyll/jekyll-archives). + +## Gemfile + +A `Gemfile` is a list of gems used by your site. Every Jekyll site has a Gemfile in the main folder. + +For a simple Jekyll site it might look something like this: + +```ruby +source "https://rubygems.org" + +gem "jekyll" + +group :jekyll_plugins do + gem "jekyll-feed" + gem "jekyll-seo-tag" +end +``` + +## Bundler + +[Bundler](https://rubygems.org/gems/bundler) is a gem that installs all gems in your `Gemfile`. + +While you don't have to use `Gemfile` and `bundler`, it is highly recommended as it ensures you're running the same version of Jekyll and its plugins across different environments. + +Install Bundler using `gem install bundler`. You only need to install it once, not every time you create a new Jekyll project. + +To install gems in your Gemfile using Bundler, run the following in the directory that has the Gemfile: + +``` +bundle install +bundle exec jekyll serve +``` + +To bypass Bundler if you aren't using a Gemfile, run `jekyll serve`. + +See [Using Jekyll with Bundler](/tutorials/using-jekyll-with-bundler/) for more information about Bundler in Jekyll and for instructions to get up and running quickly. diff --git a/docs/_docs/security.md b/docs/_docs/security.md new file mode 100644 index 0000000..631a163 --- /dev/null +++ b/docs/_docs/security.md @@ -0,0 +1,36 @@ +--- +title: Security Policy +permalink: "/docs/security/" +note: This file is autogenerated. Edit /.github/SECURITY.markdown instead. +--- + +## Supported Versions + +Security updates are applied to the latest MINOR version of Jekyll, and the version used by GitHub Pages, v3.9.x. + +| Version | Supported | +| ------- | ------------------ | +| 4.2.x | :white_check_mark: | +| 3.9.x | :white_check_mark: | +| < 3.9.x | :x: | + +## Reporting a Vulnerability + +Please report vulnerabilities by sending an email to security@jekyllrb.com with the following information: + +1. A description of the vulnerability +2. Reproduction steps and/or a sample site (share a private repo to the [Jekyll Security Team](docs/pages/team.md)) +3. Your contact information + +The Jekyll security team will respond to your submission and notify you whether it has been confirmed by the team. +Your confidentiality is kindly requested as we work on a fix. We will provide our patch to you to test and verify that the vulnerability has +been closed. + +If you have created a patch and would like to submit that to us as well, we will happily consider it though we cannot guarantee that we will +use it. If we use your patch, we will attribute authorship to you either as the commit author, or as a co-author. + +Once a fix is verified, we will release PATCH versions of the supported MINOR versions and assign a CVE to the vulnerability. You will receive +credit in our release post. + +Once the patched version has been released, we will no longer request you to maintain confidentiality and you may choose to share details on +how you found the vulnerability with the community. diff --git a/docs/_docs/static_files.md b/docs/_docs/static_files.md new file mode 100644 index 0000000..037b2e8 --- /dev/null +++ b/docs/_docs/static_files.md @@ -0,0 +1,96 @@ +--- +title: Static Files +permalink: /docs/static-files/ +--- +A static file is a file that does not contain any front matter. These +include images, PDFs, and other un-rendered content. + +They're accessible in Liquid via `site.static_files` and contain the +following metadata: + +<div class="mobile-side-scroller"> +<table> + <thead> + <tr> + <th>Variable</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td><p><code>file.path</code></p></td> + <td><p> + + The relative path to the file, e.g. <code>/assets/img/image.jpg</code> + + </p></td> + </tr> + <tr> + <td><p><code>file.modified_time</code></p></td> + <td><p> + + The `Time` the file was last modified, e.g. <code>2016-04-01 16:35:26 +0200</code> + + </p></td> + </tr> + <tr> + <td><p><code>file.name</code></p></td> + <td><p> + + The string name of the file e.g. <code>image.jpg</code> for <code>image.jpg</code> + + </p></td> + </tr> + <tr> + <td><p><code>file.basename</code></p></td> + <td><p> + + The string basename of the file e.g. <code>image</code> for <code>image.jpg</code> + + </p></td> + </tr> + <tr> + <td><p><code>file.extname</code></p></td> + <td><p> + + The extension name for the file, e.g. + <code>.jpg</code> for <code>image.jpg</code> + + </p></td> + </tr> + </tbody> +</table> +</div> + +Note that in the above table, `file` can be anything. It's an arbitrarily set variable used in your own logic (such as in a for loop). It isn't a global site or page variable. + +## Add front matter to static files + +Although you can't directly add front matter values to static files, you can set front matter values through the [defaults property](/docs/configuration/front-matter-defaults/) in your configuration file. When Jekyll builds the site, it will use the front matter values you set. + +Here's an example: + +In your `_config.yml` file, add the following values to the `defaults` property: + +```yaml +defaults: + - scope: + path: "assets/img" + values: + image: true +``` + +This assumes that your Jekyll site has a folder path of `assets/img` where you have images (static files) stored. When Jekyll builds the site, it will treat each image as if it had the front matter value of `image: true`. + +Suppose you want to list all your image assets as contained in `assets/img`. You could use this for loop to look in the `static_files` object and get all static files that have this front matter property: + +{% raw %} +```liquid +{% assign image_files = site.static_files | where: "image", true %} +{% for myimage in image_files %} + {{ myimage.path }} +{% endfor %} +``` +{% endraw %} + +When you build your site, the output will list the path to each file that meets this front matter condition. diff --git a/docs/_docs/step-by-step/01-setup.md b/docs/_docs/step-by-step/01-setup.md new file mode 100644 index 0000000..516f8ce --- /dev/null +++ b/docs/_docs/step-by-step/01-setup.md @@ -0,0 +1,101 @@ +--- +layout: step +title: Setup +menu_name: Step by Step Tutorial +position: 1 +--- +Welcome to Jekyll's step-by-step tutorial. This tutorial takes +you from having some front-end web development experience to building your +first Jekyll site from scratch without relying on the default gem-based theme. + +## Installation + +Jekyll is a Ruby gem. First, install Ruby on your machine. +Go to [Installation]({{ '/docs/installation/' | relative_url }}) and follow the +instructions for your operating system. + +With Ruby installed, install Jekyll from the terminal: + +```sh +gem install jekyll bundler +``` + +Create a new `Gemfile` to list your project's dependencies: + +```sh +bundle init +``` + +Edit the `Gemfile` in a text editor and add jekyll as a dependency: + +```ruby +gem "jekyll" +``` + +Run `bundle` to install jekyll for your project. + +You can now prefix all jekyll commands listed in this tutorial with `bundle exec` +to make sure you use the jekyll version defined in your `Gemfile`. + +## Create a site + +It's time to create a site! Create a new directory for your site and name +it whatever you want. Through the rest of this tutorial we'll refer to this +directory as **root**. + +You can also initialize a Git repository here. + +One of the great things about Jekyll is there's no database. All content and +site structure are files that a Git repository can version. Using a repository +is optional but is recommended. You can learn more +about using Git by reading the +[Git Handbook](https://guides.github.com/introduction/git-handbook/). + +Let's add your first file. Create `index.html` in **root** with the following +content: + +```html +<!DOCTYPE html> +<html> + <head> + <meta charset="utf-8"> + <title>Home</title> + </head> + <body> + <h1>Hello World!</h1> + </body> +</html> +``` + +## Build + +Since Jekyll is a static site generator, it has to build the site +before we can view it. Run either of the following commands to build your site: + +* `jekyll build` - Builds the site and outputs a static site to a directory +called `_site`. +* `jekyll serve` - Does `jekyll build` and runs it on a local web server at `http://localhost:4000`, rebuilding the site any time you make a change. + +{: .note .info} +When you're developing a site, use `jekyll serve`. To force the browser to refresh with every change, use `jekyll serve --livereload`. +If there's a conflict or you'd like Jekyll to serve your development site at a different URL, use the `--host` and `--port` arguments, +as described in the [serve command options]({{ '/docs/configuration/options/#serve-command-options' | relative_url }}). + +{: .note .warning} +The version of the site that `jekyll serve` builds in `_site` is not suited for deployment. Links and asset URLs in sites created +with `jekyll serve` will use `https://localhost:4000` or the value set with command-line configuration, instead of the values set +in [your site's configuration file]({{ '/docs/configuration/' | relative_url }}). To learn about how to build your site when it's +ready for deployment, read the [Deployment]({{ '/docs/step-by-step/10-deployment/' | relative_url }}) section of this tutorial. + + +Run `jekyll serve` and go to +<a href="http://localhost:4000" target="_blank" data-proofer-ignore>http://localhost:4000</a> in +your browser. You should see "Hello World!". + +At this point, you might be thinking, "So what?". The only thing that happened was that Jekyll copied an +HTML file from one place to another. + +Patience, young grasshopper, there's +still much to learn! + +Next. you'll learn about Liquid and templating. diff --git a/docs/_docs/step-by-step/02-liquid.md b/docs/_docs/step-by-step/02-liquid.md new file mode 100644 index 0000000..012a8c6 --- /dev/null +++ b/docs/_docs/step-by-step/02-liquid.md @@ -0,0 +1,101 @@ +--- +layout: step +title: Liquid +position: 2 +--- +Liquid is where Jekyll starts to get more interesting. It is a templating +language which has three main components: + * [objects](#objects) + * [tags](#tags) + * [filters](#filters) + +## Objects + +Objects tell Liquid to output predefined [variables](../../variables/) as content on a page. Use double curly braces for objects: {% raw %}`{{`{% endraw %} and {% raw %}`}}`{% endraw %}. + +For example, {% raw %}`{{ page.title }}`{% endraw %} displays the `page.title` variable. + +## Tags + +Tags define the logic and control flow for templates. Use curly +braces and percent signs for tags: {% raw %}`{%`{% endraw %} and +{% raw %}`%}`{% endraw %}. + +For example: + +{% raw %} +```liquid +{% if page.show_sidebar %} + <div class="sidebar"> + sidebar content + </div> +{% endif %} +``` +{% endraw %} + +This displays the sidebar if the value of the `show_sidebar` page variable is true. + +Learn more about the tags available in Jekyll [here](/docs/liquid/tags/). + +## Filters + +Filters change the output of a Liquid object. They are used within an output +and are separated by a `|`. + +For example: + +{% raw %} +```liquid +{{ "hi" | capitalize }} +``` +{% endraw %} + +This displays `Hi` instead of `hi`. + +[Learn more about the filters](/docs/liquid/filters/) available. + +## Use Liquid + +Now, use Liquid to make your `Hello World!` text from [Setup](../01-setup/) lowercase: + +{% raw %} +```liquid +... +<h1>{{ "Hello World!" | downcase }}</h1> +... +``` +{% endraw %} + +To make Jekyll process your changes, add [front matter](../03-front-matter/) to the top of the page: + +```yaml +--- +# front matter tells Jekyll to process Liquid +--- +``` + +Your HTML document should look like this: + +{% raw %} +```html +--- +--- + +<!DOCTYPE html> +<html> + <head> + <meta charset="utf-8"> + <title>Home</title> + </head> + <body> + <h1>{{ "Hello World!" | downcase }}</h1> + </body> +</html> +``` +{% endraw %} + +When you reload your browser, you should see `hello world!`. + +Much of Jekyll's power comes from combining Liquid with other features. Add frontmatter to pages to make Jekyll process the Liquid on those pages. + +Next, you'll learn more about frontmatter. diff --git a/docs/_docs/step-by-step/03-front-matter.md b/docs/_docs/step-by-step/03-front-matter.md new file mode 100644 index 0000000..17bb145 --- /dev/null +++ b/docs/_docs/step-by-step/03-front-matter.md @@ -0,0 +1,58 @@ +--- +layout: step +title: Front Matter +position: 3 +--- +Front matter is a snippet of [YAML](http://yaml.org/) placed between two +triple-dashed lines at the start of a file. + +You can use front matter to set variables for the page: + +```yaml +--- +my_number: 5 +--- +``` + +You can call front matter variables in Liquid using the `page` variable. For +example, to output the value of the `my_number` variable above: + +{% raw %} +```liquid +{{ page.my_number }} +``` +{% endraw %} + +## Use front matter + +Change the `<title>` on your site to use front matter: + +{% raw %} +```liquid +--- +title: Home +--- +<!doctype html> +<html> + <head> + <meta charset="utf-8"> + <title>{{ page.title }}</title> + </head> + <body> + <h1>{{ "Hello World!" | downcase }}</h1> + </body> +</html> +``` +{% endraw %} + +{: .note .info } +You _must_ include front matter on the page for Jekyll to process any Liquid tags on it. + +To make Jekyll process a page without defining variables in the front matter, use: + +```yaml +--- +--- +``` + +Next, you'll learn more about layouts and why your pages use more source code than plain HTML. \ No newline at end of file diff --git a/docs/_docs/step-by-step/04-layouts.md b/docs/_docs/step-by-step/04-layouts.md new file mode 100644 index 0000000..ae2dd16 --- /dev/null +++ b/docs/_docs/step-by-step/04-layouts.md @@ -0,0 +1,89 @@ +--- +layout: step +title: Layouts +position: 4 +--- +Jekyll supports [Markdown](https://daringfireball.net/projects/markdown/syntax) +in addition to HTML when building pages. Markdown is a great choice for pages with a simple +content structure (just paragraphs, headings and images), as it's less verbose +than raw HTML. + +Create a new Markdown file named `about.md` in your site's root folder. + +You could copy the contents of `index` and modify it for the About page. However, +this creates duplicate code that has to be customized for each new page you add +to your site. + +For example, adding a new stylesheet to your site would involve adding the link +to the stylesheet to the `<head>` of each page. For sites with many pages, this +is a waste of time. + +## Creating a layout + +Layouts are templates that can be used by any page in your site and wrap around page content. +They are stored in a directory called `_layouts`. + +Create the `_layouts` directory in your site's root folder and create a new `default.html` file with the following content: + +{% raw %} +```liquid +<!doctype html> +<html> + <head> + <meta charset="utf-8"> + <title>{{ page.title }}</title> + </head> + <body> + {{ content }} + </body> +</html> +``` +{% endraw %} + +This HTML is almost identical to `index.html` except there's +no front matter and the content of the page is replaced by a `content` +variable. + +`content` is a special variable that returns the rendered +content of the page on which it's called. + +## Use layouts + +To make `index.html` use your new layout, set the `layout` variable in the front +matter. The file should look like this: + +{% raw %} +```liquid +--- +layout: default +title: Home +--- +<h1>{{ "Hello World!" | downcase }}</h1> +``` +{% endraw %} + +When you reload the site, the output remains the same. + +Since the layout wraps around the content on the page, you can call front matter like `page` +in the layout file. When you apply the layout to a page, it uses the front matter on that page. + +## Build the About page + +Add the following to `about.md` to use your new layout in the About page: + +```markdown +--- +layout: default +title: About +--- +# About page + +This page tells you a little bit about me. +``` + +Open <a href="http://localhost:4000/about.html" target="_blank" data-proofer-ignore>http://localhost:4000/about.html</a> +in your browser and view your new page. + +Congratulations, you now have a two page website! + +Next, you'll learn about navigating from page to page in your site. \ No newline at end of file diff --git a/docs/_docs/step-by-step/05-includes.md b/docs/_docs/step-by-step/05-includes.md new file mode 100644 index 0000000..b0b8ca9 --- /dev/null +++ b/docs/_docs/step-by-step/05-includes.md @@ -0,0 +1,83 @@ +--- +layout: step +title: Includes +position: 5 +--- +The site is coming together; however, there's no way to navigate between +pages. Let's fix that. + +Navigation should be on every page so adding it to your layout is the correct +place to do this. Instead of adding it directly to the layout, let's use this +as an opportunity to learn about includes. + +## Include tag + +The `include` tag allows you to include content from another file stored +in an `_includes` folder. Includes are useful for having a single source for +source code that repeats around the site or for improving the readability. + +Navigation source code can get complex, so sometimes it's nice to move it into an +include. + +## Include usage + +Create a file for the navigation at `_includes/navigation.html` with the +following content: + +``` +<nav> + <a href="/">Home</a> + <a href="/about.html">About</a> +</nav> +``` + +Try using the include tag to add the navigation to `_layouts/default.html`: + +{% raw %} +```liquid +<!doctype html> +<html> + <head> + <meta charset="utf-8"> + <title>{{ page.title }}</title> + </head> + <body> + {% include navigation.html %} + {{ content }} + </body> +</html> +``` +{% endraw %} + +Open <a href="http://localhost:4000" target="_blank" data-proofer-ignore>http://localhost:4000</a> +in your browser and try switching between the pages. + +## Current page highlighting + +Let's take this a step further and highlight the current page in the navigation. + +`_includes/navigation.html` needs to know the URL of the page it's inserted into +so it can add styling. Jekyll has useful [variables](/docs/variables/) available, +one of which is `page.url`. + +Using `page.url` you can check if each link is the current page and color it red +if true: + +{% raw %} +```liquid +<nav> + <a href="/" {% if page.url == "/" %}style="color: red;"{% endif %}> + Home + </a> + <a href="/about.html" {% if page.url == "/about.html" %}style="color: red;"{% endif %}> + About + </a> +</nav> +``` +{% endraw %} + +Take a look at <a href="http://localhost:4000" target="_blank" data-proofer-ignore>http://localhost:4000</a> +and see your red link for the current page. + +There's still a lot of repetition here if you wanted to add a new item to the +navigation or change the highlight color. In the next step we'll address this. diff --git a/docs/_docs/step-by-step/06-data-files.md b/docs/_docs/step-by-step/06-data-files.md new file mode 100644 index 0000000..d87a40c --- /dev/null +++ b/docs/_docs/step-by-step/06-data-files.md @@ -0,0 +1,48 @@ +--- +layout: step +title: Data Files +position: 6 +--- +Jekyll supports loading data from YAML, JSON, and CSV files located in a `_data` +directory. Data files are a great way to separate content from source code to +make the site easier to maintain. + +In this step you'll store the contents of the navigation in a data file +and then iterate over it in the navigation include. + +## Data file usage + +[YAML](http://yaml.org/) is a format that's common in the Ruby ecosystem. You'll +use it to store an array of navigation items each with a name and link. + +Create a data file for the navigation at `_data/navigation.yml` with the +following: + +```yaml +- name: Home + link: / +- name: About + link: /about.html +``` + +Jekyll makes this data file available to you at `site.data.navigation`. Instead +of outputting each link in `_includes/navigation.html`, now you can iterate over +the data file instead: + +{% raw %} +```liquid +<nav> + {% for item in site.data.navigation %} + <a href="{{ item.link }}" {% if page.url == item.link %}style="color: red;"{% endif %}> + {{ item.name }} + </a> + {% endfor %} +</nav> +``` +{% endraw %} + +The output will be exactly the same. The difference is you’ve made it easier to +add new navigation items and change the HTML structure. + +What good is a site without CSS, JS and images? Let’s look at how to handle +assets in Jekyll. diff --git a/docs/_docs/step-by-step/07-assets.md b/docs/_docs/step-by-step/07-assets.md new file mode 100644 index 0000000..4ea8748 --- /dev/null +++ b/docs/_docs/step-by-step/07-assets.md @@ -0,0 +1,92 @@ +--- +layout: step +title: Assets +position: 7 +--- +Using CSS, JS, images and other assets is straightforward with Jekyll. Place +them in your site folder and they’ll copy across to the built site. + +Jekyll sites often use this structure to keep assets organized: + +``` +. +├── assets +│ ├── css +│ ├── images +│ └── js +... +``` +So, from your assets folder, create folders called css, images and js. +Additionally, directly under the root create another folder called '_sass', which you will need shortly. + +## Sass + +Inlining the styles used in `_includes/navigation.html`(adding or configuring within the same file) is not a best practice. +Instead, let's style the current page by defining our first class in a new css file instead. + +To do this, refer to the class (that you will configure in the next parts of this step) from within the navigation.html file by removing the code you added earlier (to color the current link red) and inserting the following code: + +{% raw %} +```liquid +<nav> + {% for item in site.data.navigation %} + <a href="{{ item.link }}" {% if page.url == item.link %}class="current"{% endif %}>{{ item.name }}</a> + {% endfor %} +</nav> +``` +{% endraw %} + +You could use a standard CSS file for styling, we're going to take it a step +further by using [Sass](https://sass-lang.com/). Sass is a fantastic extension +to CSS baked right into Jekyll. + +First create a Sass file at `assets/css/styles.scss` with the following content: + +```sass +--- +--- +@import "main"; +``` + +The empty front matter at the top tells Jekyll it needs to process the file. The +`@import "main"` tells Sass to look for a file called `main.scss` in the sass +directory (`_sass/`) by default which you already created directly under the root folder of your website). + +At this stage you'll just have a main css file. For larger projects, this is a +great way to keep your CSS organized. + +Create the current class mentioned above in order to color the current link green. Create a Sass file at `_sass/main.scss` with the following content: + +```sass +.current { + color: green; +} +``` + +You'll need to reference the stylesheet in your layout. + +Open `_layouts/default.html` and add the stylesheet to the `<head>`: + +{% raw %} +```liquid +<!doctype html> +<html> + <head> + <meta charset="utf-8"> + <title>{{ page.title }}</title> + <link rel="stylesheet" href="/assets/css/styles.css"> + </head> + <body> + {% include navigation.html %} + {{ content }} + </body> +</html> +``` +{% endraw %} + +The `styles.css` referenced here is generated by Jekyll from the `styles.scss` you created earlier in `assets/css/`. + +Load up <a href="http://localhost:4000" target="_blank" data-proofer-ignore>http://localhost:4000</a> +and check that the active link in the navigation is green. + +Next we're looking at one of Jekyll's most popular features, blogging. diff --git a/docs/_docs/step-by-step/08-blogging.md b/docs/_docs/step-by-step/08-blogging.md new file mode 100644 index 0000000..249d2c3 --- /dev/null +++ b/docs/_docs/step-by-step/08-blogging.md @@ -0,0 +1,145 @@ +--- +layout: step +title: Blogging +position: 8 +--- +You might be wondering how you can have a blog without a database. In true +Jekyll style, blogging is powered by text files only. + +## Posts + +Blog posts live in a folder called `_posts`. The filename for posts have a +special format: the publish date, then a title, followed by an extension. + +Create your first post at `_posts/2018-08-20-bananas.md` with the +following content: + +```markdown +--- +layout: post +author: jill +--- +A banana is an edible fruit – botanically a berry – produced by several kinds +of large herbaceous flowering plants in the genus Musa. + +In some countries, bananas used for cooking may be called "plantains", +distinguishing them from dessert bananas. The fruit is variable in size, color, +and firmness, but is usually elongated and curved, with soft flesh rich in +starch covered with a rind, which may be green, yellow, red, purple, or brown +when ripe. +``` + +This is like the `about.md` you created before except it has an author and +a different layout. `author` is a custom variable, it's not required and could +have been named something like `creator`. + +## Layout + +The `post` layout doesn't exist so you'll need to create it at +`_layouts/post.html` with the following content: + +{% raw %} +```liquid +--- +layout: default +--- +<h1>{{ page.title }}</h1> +<p>{{ page.date | date_to_string }} - {{ page.author }}</p> + +{{ content }} +``` +{% endraw %} + +This is an example of layout inheritance. The post layout outputs the title, +date, author and content body which is wrapped by the default layout. + +Also note the `date_to_string` filter, this formats a date into a nicer format. + +## List posts + +There's currently no way to navigate to the blog post. Typically a blog has a +page which lists all the posts, let's do that next. + +Jekyll makes posts available at `site.posts`. + +Create `blog.html` in your root (`/blog.html`) with the following content: + +{% raw %} +```liquid +--- +layout: default +title: Blog +--- +<h1>Latest Posts</h1> + +<ul> + {% for post in site.posts %} + <li> + <h2><a href="{{ post.url }}">{{ post.title }}</a></h2> + {{ post.excerpt }} + </li> + {% endfor %} +</ul> +``` +{% endraw %} + +There's a few things to note with this code: + +* `post.url` is automatically set by Jekyll to the output path of the post +* `post.title` is pulled from the post filename and can be overridden by +setting `title` in front matter +* `post.excerpt` is the first paragraph of content by default + +You also need a way to navigate to this page through the main navigation. Open +`_data/navigation.yml` and add an entry for the blog page: + +```yaml +- name: Home + link: / +- name: About + link: /about.html +- name: Blog + link: /blog.html +``` + +## More posts + +A blog isn't very exciting with a single post. Add a few more: + +`_posts/2018-08-21-apples.md`: + +```markdown +--- +layout: post +author: jill +--- +An apple is a sweet, edible fruit produced by an apple tree. + +Apple trees are cultivated worldwide, and are the most widely grown species in +the genus Malus. The tree originated in Central Asia, where its wild ancestor, +Malus sieversii, is still found today. Apples have been grown for thousands of +years in Asia and Europe, and were brought to North America by European +colonists. +``` + +`_posts/2018-08-22-kiwifruit.md`: + +```markdown +--- +layout: post +author: ted +--- +Kiwifruit (often abbreviated as kiwi), or Chinese gooseberry is the edible +berry of several species of woody vines in the genus Actinidia. + +The most common cultivar group of kiwifruit is oval, about the size of a large +hen's egg (5–8 cm (2.0–3.1 in) in length and 4.5–5.5 cm (1.8–2.2 in) in +diameter). It has a fibrous, dull greenish-brown skin and bright green or +golden flesh with rows of tiny, black, edible seeds. The fruit has a soft +texture, with a sweet and unique flavor. +``` + +Open <a href="http://localhost:4000" target="_blank" data-proofer-ignore>http://localhost:4000</a> +and have a look through your blog posts. + +Next we'll focus on creating a page for each post author. diff --git a/docs/_docs/step-by-step/09-collections.md b/docs/_docs/step-by-step/09-collections.md new file mode 100644 index 0000000..d7a33b1 --- /dev/null +++ b/docs/_docs/step-by-step/09-collections.md @@ -0,0 +1,256 @@ +--- +layout: step +title: Collections +position: 9 +--- +Let's look at fleshing out authors so each author has their own page with a +blurb and the posts they've published. + +To do this you'll use collections. Collections are similar to posts except the +content doesn't have to be grouped by date. + +## Configuration + +To set up a collection you need to tell Jekyll about it. Jekyll configuration +happens in a file called `_config.yml` (by default). + +Create `_config.yml` in the root with the following: + +```yaml +collections: + authors: +``` + +To (re)load the configuration, restart the jekyll server. Press `Ctrl`+`C` in your terminal to stop the server, and then `jekyll serve` to restart it. + +## Add authors + +Documents (the items in a collection) live in a folder in the root of the site +named `_*collection_name*`. In this case, `_authors`. + +Create a document for each author: + +`_authors/jill.md`: + +```markdown +--- +short_name: jill +name: Jill Smith +position: Chief Editor +--- +Jill is an avid fruit grower based in the south of France. +``` + +`_authors/ted.md`: + +```markdown +--- +short_name: ted +name: Ted Doe +position: Writer +--- +Ted has been eating fruit since he was baby. +``` + +## Staff page + +Let's add a page which lists all the authors on the site. Jekyll makes the +collection available at `site.authors`. + +Create `staff.html` and iterate over `site.authors` to output all the staff: + +{% raw %} +```liquid +--- +layout: default +title: Staff +--- +<h1>Staff</h1> + +<ul> + {% for author in site.authors %} + <li> + <h2>{{ author.name }}</h2> + <h3>{{ author.position }}</h3> + <p>{{ author.content | markdownify }}</p> + </li> + {% endfor %} +</ul> +``` +{% endraw %} + +Since the content is markdown, you need to run it through the +`markdownify` filter. This happens automatically when outputting using +{% raw %}`{{ content }}`{% endraw %} in a layout. + +You also need a way to navigate to this page through the main navigation. Open +`_data/navigation.yml` and add an entry for the staff page: + +```yaml +- name: Home + link: / +- name: About + link: /about.html +- name: Blog + link: /blog.html +- name: Staff + link: /staff.html +``` + +## Output a page + +By default, collections do not output a page for documents. In this case we +want each author to have their own page so let's tweak the collection +configuration. + +Open `_config.yml` and add `output: true` to the author collection +configuration: + +```yaml +collections: + authors: + output: true +``` + +Restart the jekyll server once more for the configuration changes to take effect. + +You can link to the output page using `author.url`. + +Add the link to the `staff.html` page: + +{% raw %} +```liquid +--- +layout: default +title: Staff +--- +<h1>Staff</h1> + +<ul> + {% for author in site.authors %} + <li> + <h2><a href="{{ author.url }}">{{ author.name }}</a></h2> + <h3>{{ author.position }}</h3> + <p>{{ author.content | markdownify }}</p> + </li> + {% endfor %} +</ul> +``` +{% endraw %} + +Just like posts you'll need to create a layout for authors. + +Create `_layouts/author.html` with the following content: + +{% raw %} +```liquid +--- +layout: default +--- +<h1>{{ page.name }}</h1> +<h2>{{ page.position }}</h2> + +{{ content }} +``` +{% endraw %} + +## Front matter defaults + +Now you need to configure the author documents to use the `author` layout. You +could do this in the front matter like we have previously but that's getting +repetitive. + +What you really want is all posts to automatically have the post +layout, authors to have author and everything else to use the default. + +You can achieve this by using [front matter defaults](/docs/configuration/front-matter-defaults/) +in `_config.yml`. You set a scope of what the default applies to, then the +default front matter you'd like. + +Add defaults for layouts to your `_config.yml`, + +```yaml +collections: + authors: + output: true + +defaults: + - scope: + path: "" + type: "authors" + values: + layout: "author" + - scope: + path: "" + type: "posts" + values: + layout: "post" + - scope: + path: "" + values: + layout: "default" +``` + +Now you can remove layout from the front matter of all pages and posts. Note +that any time you update `_config.yml` you'll need to restart Jekyll for the +changes to take effect. + +## List author's posts + +Let's list the posts an author has published on their page. To do +this you need to match the author `short_name` to the post `author`. You +use this to filter the posts by author. + +Iterate over this filtered list in `_layouts/author.html` to output the +author's posts: + +{% raw %} +```liquid +--- +layout: default +--- +<h1>{{ page.name }}</h1> +<h2>{{ page.position }}</h2> + +{{ content }} + +<h2>Posts</h2> +<ul> + {% assign filtered_posts = site.posts | where: 'author', page.short_name %} + {% for post in filtered_posts %} + <li><a href="{{ post.url }}">{{ post.title }}</a></li> + {% endfor %} +</ul> +``` +{% endraw %} + +## Link to authors page + +The posts have a reference to the author so let's link it to the author's page. +You can do this using a similar filtering technique in `_layouts/post.html`: + +{% raw %} +```liquid +--- +layout: default +--- +<h1>{{ page.title }}</h1> + +<p> + {{ page.date | date_to_string }} + {% assign author = site.authors | where: 'short_name', page.author | first %} + {% if author %} + - <a href="{{ author.url }}">{{ author.name }}</a> + {% endif %} +</p> + +{{ content }} +``` +{% endraw %} + +Open up <a href="http://localhost:4000" target="_blank" data-proofer-ignore>http://localhost:4000</a> and +have a look at the staff page and the author links on posts to check everything +is linked together correctly. + +In the next and final step of this tutorial, we'll add polish to the site and +get it ready for a production deployment. diff --git a/docs/_docs/step-by-step/10-deployment.md b/docs/_docs/step-by-step/10-deployment.md new file mode 100644 index 0000000..8071b8a --- /dev/null +++ b/docs/_docs/step-by-step/10-deployment.md @@ -0,0 +1,172 @@ +--- +layout: step +title: Deployment +position: 10 +--- +In this final step we'll get the site ready for production. + +## Gemfile + +It's good practice to have a [Gemfile](/docs/ruby-101/#gemfile) for your site. +This ensures the version of Jekyll and other gems remains consistent across +different environments. + +Create a `Gemfile` in the root. +The file should be called 'Gemfile' and should *not* have any extension. +You can create a Gemfile with Bundler and then add the `jekyll` gem: + +```sh +bundle init +bundle add jekyll +``` + +Your file should look something like: + +```ruby +# frozen_string_literal: true +source "https://rubygems.org" + +gem "jekyll" +``` + +Bundler installs the gems and creates a `Gemfile.lock` which locks the current +gem versions for a future `bundle install`. If you ever want to update your gem +versions you can run `bundle update`. + +When using a `Gemfile`, you'll run commands like `jekyll serve` with +`bundle exec` prefixed. So the full command is: + +```sh +bundle exec jekyll serve +``` + +This restricts your Ruby environment to only use gems set in your `Gemfile`. + +## Plugins + +Jekyll plugins allow you to create custom generated content specific to your +site. There's many [plugins](/docs/plugins/) available or you can even +write your own. + +There are three official plugins which are useful on almost any Jekyll site: + +* [jekyll-sitemap](https://github.com/jekyll/jekyll-sitemap) - Creates a sitemap +file to help search engines index content +* [jekyll-feed](https://github.com/jekyll/jekyll-feed) - Creates an RSS feed for +your posts +* [jekyll-seo-tag](https://github.com/jekyll/jekyll-seo-tag) - Adds meta tags to help +with SEO + +To use these first you need to add them to your `Gemfile`. If you put them +in a `jekyll_plugins` group they'll automatically be required into Jekyll: + +```ruby +source 'https://rubygems.org' + +gem 'jekyll' + +group :jekyll_plugins do + gem 'jekyll-sitemap' + gem 'jekyll-feed' + gem 'jekyll-seo-tag' +end +``` + +Then add these lines to your `_config.yml`: + +```yaml +plugins: + - jekyll-feed + - jekyll-sitemap + - jekyll-seo-tag +``` + +Now install them by running a `bundle update`. + +`jekyll-sitemap` doesn't need any setup, it will create your sitemap on build. + +For `jekyll-feed` and `jekyll-seo-tag` you need to add tags to +`_layouts/default.html`: + +{% raw %} +```liquid +<!doctype html> +<html> + <head> + <meta charset="utf-8"> + <title>{{ page.title }}</title> + <link rel="stylesheet" href="/assets/css/styles.css"> + {% feed_meta %} + {% seo %} + </head> + <body> + {% include navigation.html %} + {{ content }} + </body> +</html> +``` +{% endraw %} + +Restart your Jekyll server and check these tags are added to the `<head>`. + +## Environments + +Sometimes you might want to output something in production but not +in development. Analytics scripts are the most common example of this. + +To do this you can use [environments](/docs/configuration/environments/). You +can set the environment by using the `JEKYLL_ENV` environment variable when +running a command. For example: + +```sh +JEKYLL_ENV=production bundle exec jekyll build +``` + +By default `JEKYLL_ENV` is development. The `JEKYLL_ENV` is available to you +in liquid using `jekyll.environment`. So to only output the analytics script +on production you would do the following: + +{% raw %} +```liquid +{% if jekyll.environment == "production" %} + <script src="my-analytics-script.js"></script> +{% endif %} +``` +{% endraw %} + +## Deployment + +The final step is to get the site onto a production server. The most basic way +to do this is to run a production build: + +```sh +JEKYLL_ENV=production bundle exec jekyll build +``` + +And then copy the contents of `_site` to your server. + +<div class="note warning"> + <h5>Destination folders are cleaned on site builds</h5> + <p> + The contents of <code>_site</code> are automatically cleaned, by default, when + the site is built. Files or folders that are not created by your site's build + process will be removed. + </p> + <p> + Some files could be retained by specifying them within the <code>keep_files</code> + configuration directive. Other files could be retained by keeping them in your + assets directory. + </p> +</div> + +A better way is to automate this process using a [CI](/docs/deployment/automated/) +or [3rd party](/docs/deployment/third-party/). + +## Wrap up + +That brings us to the end of this step-by-step tutorial and the beginning of +your Jekyll journey! + +* Come say hi to the [community forums](https://talk.jekyllrb.com) +* Help us make Jekyll better by [contributing](/docs/contributing/) +* Keep building Jekyll sites! diff --git a/docs/_docs/structure.md b/docs/_docs/structure.md new file mode 100644 index 0000000..bdec1fd --- /dev/null +++ b/docs/_docs/structure.md @@ -0,0 +1,239 @@ +--- +title: Directory Structure +permalink: /docs/structure/ +--- +A basic Jekyll site usually looks something like this: + +``` +. +├── _config.yml +├── _data +│ └── members.yml +├── _drafts +│ ├── begin-with-the-crazy-ideas.md +│ └── on-simplicity-in-technology.md +├── _includes +│ ├── footer.html +│ └── header.html +├── _layouts +│ ├── default.html +│ └── post.html +├── _posts +│ ├── 2007-10-29-why-every-programmer-should-play-nethack.md +│ └── 2009-04-26-barcamp-boston-4-roundup.md +├── _sass +│ ├── _base.scss +│ └── _layout.scss +├── _site +├── .jekyll-cache +│ └── Jekyll +│ └── Cache +│ └── [...] +├── .jekyll-metadata +└── index.html # can also be an 'index.md' with valid front matter +``` + +<div class="note"> + <h5>Directory structure of Jekyll sites using gem-based themes</h5> + <p> + Since version {% include docs_version_badge.html version="3.2"%}, a new Jekyll project bootstrapped with <code>jekyll new</code> uses <a href="/docs/themes/">gem-based themes</a> to define the look of the site. This results in a lighter default directory structure: <code>_layouts</code>, <code>_includes</code> and <code>_sass</code> are stored in the theme-gem, by default. + </p> + <br /> + <p> + <a href="https://github.com/jekyll/minima">minima</a> is the current default theme, and <code>bundle info minima</code> will show you where minima theme's files are stored on your computer. + </p> +</div> + +An overview of what each of these does: + +<div class="mobile-side-scroller"> +<table> + <thead> + <tr> + <th>File / Directory</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td> + <p><code>_config.yml</code></p> + </td> + <td> + <p> + Stores <a href="/docs/configuration/">configuration</a> data. Many of + these options can be specified from the command line executable but + it’s easier to specify them here so you don’t have to remember them. + </p> + </td> + </tr> + <tr> + <td> + <p><code>_drafts</code></p> + </td> + <td> + <p> + Drafts are unpublished posts. The format of these files is without a + date: <code>title.MARKUP</code>. Learn how to <a href="/docs/posts/#drafts"> + work with drafts</a>. + </p> + </td> + </tr> + <tr> + <td> + <p><code>_includes</code></p> + </td> + <td> + <p> + These are the partials that can be mixed and matched by your layouts + and posts to facilitate reuse. The liquid tag + <code>{% raw %}{% include file.ext %}{% endraw %}</code> + can be used to include the partial in + <code>_includes/file.ext</code>. + </p> + </td> + </tr> + <tr> + <td> + <p><code>_layouts</code></p> + </td> + <td> + <p> + These are the templates that wrap posts. Layouts are chosen on a + post-by-post basis in the + <a href="/docs/front-matter/">front matter</a>, + which is described in the next section. The liquid tag + <code>{% raw %}{{ content }}{% endraw %}</code> + is used to inject content into the web page. + </p> + </td> + </tr> + <tr> + <td> + <p><code>_posts</code></p> + </td> + <td> + <p> + Your dynamic content, so to speak. The naming convention of these + files is important, and must follow the format: + <code>YEAR-MONTH-DAY-title.MARKUP</code>. + The <a href="/docs/permalinks/">permalinks</a> can be customized for + each post, but the date and markup language are determined solely by + the file name. + </p> + </td> + </tr> + <tr> + <td> + <p><code>_data</code></p> + </td> + <td> + <p> + Well-formatted site data should be placed here. The Jekyll engine + will autoload all data files (using either the <code>.yml</code>, + <code>.yaml</code>, <code>.json</code>, <code>.csv</code> or + <code>.tsv</code> formats and extensions) in this directory, + and they will be accessible via `site.data`. If there's a file + <code>members.yml</code> under the directory, then you can access + contents of the file through <code>site.data.members</code>. + </p> + </td> + </tr> + <tr> + <td> + <p><code>_sass</code></p> + </td> + <td> + <p> + These are sass partials that can be imported into your <code>main.scss</code> + which will then be processed into a single stylesheet + <code>main.css</code> that defines the styles to be used by your site. + Learn <a href="{{ '/docs/assets/' | relative_url }}">how to work with assets</a>. + </p> + </td> + </tr> + <tr> + <td> + <p><code>_site</code></p> + </td> + <td> + <p> + This is where the generated site will be placed (by default) once + Jekyll is done transforming it. It’s probably a good idea to add this + to your <code>.gitignore</code> file. + </p> + </td> + </tr> + <tr> + <td> + <p><code>.jekyll-cache</code></p> + </td> + <td> + <p> + Keeps a copy of the generated pages and markup (e.g.: markdown) for + faster serving. Created when using e.g.: <code>jekyll serve</code>. + Can be disabled with + <a href="/docs/configuration/options/">an option and/or flag</a>. + This directory will not be included in the generated site. It’s + probably a good idea to add this to your <code>.gitignore</code> + file. + </p> + </td> + </tr> + <tr> + <td> + <p><code>.jekyll-metadata</code></p> + </td> + <td> + <p> + This helps Jekyll keep track of which files have not been modified + since the site was last built, and which files will need to be + regenerated on the next build. Only created when using + <a href="/docs/configuration/incremental-regeneration/"> + incremental regeneration</a> (e.g.: with <code>jekyll serve -I</code>). + This file will not be included in the generated site. It’s probably + a good idea to add this to your <code>.gitignore</code> file. + </p> + </td> + </tr> + <tr> + <td> + <p><code>index.html</code> or <code>index.md</code> and other HTML, + Markdown files</p> + </td> + <td> + <p> + Provided that the file has a <a href="/docs/front-matter/">front + matter</a> section, it will be transformed by Jekyll. The same will + happen for any <code>.html</code>, <code>.markdown</code>, + <code>.md</code>, or <code>.textile</code> file in your site’s root + directory or directories not listed above. + </p> + </td> + </tr> + <tr> + <td> + <p>Other Files/Folders</p> + </td> + <td> + <p> + Except for the special cases listed above, every other directory and + file—such as <code>css</code> and <code>images</code> folders, + <code>favicon.ico</code> files, and so forth—will be copied verbatim + to the generated site. There are plenty of <a href="/showcase/">sites + already using Jekyll</a> if you’re curious to see how they’re laid + out. + </p> + </td> + </tr> + </tbody> +</table> +</div> + +Every file or directory beginning with the following characters: `.`, `_ `, `#` or `~` in the `source` directory will not be included in the `destination` folder. Such paths will have to be explicitly specified via the config file in the `include` directive to make sure they're copied over: + +```yaml +include: + - _pages + - .htaccess + ``` diff --git a/docs/_docs/support.md b/docs/_docs/support.md new file mode 100644 index 0000000..d223318 --- /dev/null +++ b/docs/_docs/support.md @@ -0,0 +1,24 @@ +--- +title: Support +permalink: "/docs/support/" +note: This file is autogenerated. Edit /.github/SUPPORT.markdown instead. +--- + +## Getting Help + +**Jekyll's issue tracker is not a support forum.** + +If you're looking for support for Jekyll, there are a lot of options: + +* Read [Jekyll Documentation](https://jekyllrb.com/docs/home/) +* If you have a question about using Jekyll, start a discussion on [Jekyll Forum](https://talk.jekyllrb.com/) or [StackOverflow](https://stackoverflow.com/questions/tagged/jekyll) +* Chat with Jekyllers — Join [our Gitter channel](https://gitter.im/jekyll/jekyll) or [our IRC channel on Freenode](irc:irc.freenode.net/jekyll) + +There are a bunch of helpful community members on these services that should be willing to point you in the right direction. + +## Report a bug + +* If you think you've found a bug within a Jekyll plugin, open an issue in that plugin's repository — First [look for the plugin on rubygems](https://rubygems.org/) then click on the `Homepage` link to access the plugin repository. +* If you think you've found a bug within Jekyll itself, [open an issue](https://github.com/jekyll/jekyll/issues/new). + +Happy Jekyllin'! diff --git a/docs/_docs/themes.md b/docs/_docs/themes.md new file mode 100644 index 0000000..61916cc --- /dev/null +++ b/docs/_docs/themes.md @@ -0,0 +1,397 @@ +--- +title: Themes +permalink: /docs/themes/ +--- + +Jekyll has an extensive theme system that allows you to leverage community-maintained templates and styles to customize your site's presentation. Jekyll themes specify plugins and package up assets, layouts, includes, and stylesheets in a way that can be overridden by your site's content. + +## Pick up a theme + +You can find and preview themes on different galleries: + +- [GitHub.com #jekyll-theme repos](https://github.com/topics/jekyll-theme) +- [jamstackthemes.dev](https://jamstackthemes.dev/ssg/jekyll/) +- [jekyllthemes.org](http://jekyllthemes.org/) +- [jekyllthemes.io](https://jekyllthemes.io/) +- [jekyll-themes.com](https://jekyll-themes.com/) + +See also: [resources](/resources/). + +## Understanding gem-based themes + +When you [create a new Jekyll site](/docs/) (by running the `jekyll new <PATH>` command), Jekyll installs a site that uses a gem-based theme called [Minima](https://github.com/jekyll/minima). + +With gem-based themes, some of the site's directories (such as the `assets`, `_data`, `_layouts`, `_includes`, and `_sass` directories) are stored in the theme's gem, hidden from your immediate view. Yet all of the necessary directories will be read and processed during Jekyll's build process. + +In the case of Minima, you see only the following files in your Jekyll site directory: + +``` +. +├── Gemfile +├── Gemfile.lock +├── _config.yml +├── _posts +│ └── 2016-12-04-welcome-to-jekyll.markdown +├── about.markdown +└── index.markdown +``` + +The `Gemfile` and `Gemfile.lock` files are used by Bundler to keep track of the required gems and gem versions you need to build your Jekyll site. + +Gem-based themes make it easier for theme developers to make updates available to anyone who has the theme gem. When there's an update, theme developers push the update to RubyGems. + +If you have the theme gem, you can (if you desire) run `bundle update` to update all gems in your project. Or you can run `bundle update <THEME>`, replacing `<THEME>` with the theme name, such as `minima`, to just update the theme gem. Any new files or updates the theme developer has made (such as to stylesheets or includes) will be pulled into your project automatically. + +The goal of gem-based themes is to allow you to get all the benefits of a robust, continually updated theme without having all the theme's files getting in your way and over-complicating what might be your primary focus: creating content. + +## Overriding theme defaults + +Jekyll themes set default data, layouts, includes, and stylesheets. However, you can override any of the theme defaults with your own site content. + +To replace layouts or includes in your theme, make a copy in your `_layouts` or `_includes` directory of the specific file you wish to modify, or create the file from scratch giving it the same name as the file you wish to override. + +For example, if your selected theme has a `page` layout, you can override the theme's layout by creating your own `page` layout in the `_layouts` directory (that is, `_layouts/page.html`). + +To locate a theme's files on your computer: + +1. Run `bundle info --path` followed by the name of the theme's gem, e.g., `bundle info --path minima` for Jekyll's default theme. + + This returns the location of the gem-based theme files. For example, the Minima theme's files might be located in `/usr/local/lib/ruby/gems/2.6.0/gems/minima-2.5.1` on macOS. + +2. Open the theme's directory in Finder or Explorer: + + ```sh + # On MacOS + open $(bundle info --path minima) + + # On Windows + # First get the gem's installation path: + # + # bundle info --path minima + # => C:/Ruby26-x64/lib/ruby/gems/{{ site.data.ruby.current_version }}/gems/minima-2.5.1 + # + # then invoke explorer with above path, substituting `/` with `\` + explorer C:\Ruby26-x64\lib\ruby\gems\{{ site.data.ruby.current_version}}\gems\minima-2.5.1 + + # On Linux + xdg-open $(bundle info --path minima) + ``` + + A Finder or Explorer window opens showing the theme's files and directories. The Minima theme gem contains these files: + + ``` + . + ├── LICENSE.txt + ├── README.md + ├── _includes + │ ├── disqus_comments.html + │ ├── footer.html + │ ├── google-analytics.html + │ ├── head.html + │ ├── header.html + │ ├── icon-github.html + │ ├── icon-github.svg + │ ├── icon-twitter.html + │ └── icon-twitter.svg + ├── _layouts + │ ├── default.html + │ ├── home.html + │ ├── page.html + │ └── post.html + ├── _sass + │ ├── minima + │ │ ├── _base.scss + │ │ ├── _layout.scss + │ │ └── _syntax-highlighting.scss + │ └── minima.scss + └── assets + └── main.scss + ``` + +With a clear understanding of the theme's files, you can now override any theme file by creating a similarly named file in your Jekyll site directory. + +Let's say, for a second example, you want to override Minima's footer. In your Jekyll site, create an `_includes` folder and add a file in it called `footer.html`. Jekyll will now use your site's `footer.html` file instead of the `footer.html` file from the Minima theme gem. + +To modify any stylesheet you must take the extra step of also copying the main sass file (`_sass/minima.scss` in the Minima theme) into the `_sass` directory in your site's source. + +Jekyll will look first to your site's content before looking to the theme's defaults for any requested file in the following folders: + +- `/assets` +- `/_data` +- `/_layouts` +- `/_includes` +- `/_sass` + +Note that making copies of theme files will prevent you from receiving any theme updates on those files. An alternative, to continue getting theme updates on all stylesheets, is to use higher specificity CSS selectors in your own additional, originally named CSS files. + +{: .note .info} +Refer to your selected theme's documentation and source repository for more information on which files you can override. + +### Themes with `_data` directory {%- include docs_version_badge.html version="4.3.0" -%} +{: #themes-with-data-directory } + +Starting with version 4.3.0, Jekyll also takes into account the `_data` directory of themes. This allows data to be distributed across themes. + +A typical example is text used within design elements. + +Imagine a theme provides the include file `testimonials.html`. This design element creates a new section on the page, and puts a h3 heading over the list of testimonials. + +A theme developer will probably formulate the heading in English and put it directly into the HTML source code. + +Consumers of the theme can copy the included file into their project and replace the heading there. + +With the consideration of the `_data` directory there is another solution for this standard task. + +Instead of entering the text directly into the design template, the designer adds a reference to a text catalog (e.g. `site.data.i18n.testimonials.header`) and create a file `_data/i18n/testimonials.yml` in the data directory of the theme. + +In this file the header is put under the key `header` and Jekyll takes care of the rest. + +For theme developers, this, at first sight, is of course a bigger effort than before. + +However, for the consumers of the theme, the customization is greatly simplified. + +Imagine the theme is used by a customer from Germany. In order for her to get the translated header for the testimonials design element in, she just has to create a data file in her project directory with the key `site.data.i18n.testimonials.header`, put the German translation or a header of her choice on top of it and the design element is already customized. + +She no longer has to copy the included file into her project directory, customize it there and, what weighs heaviest, waiver all updates of the theme, simply because the theme developer offered her the possibility to make changes to text modules centrally via text files. + +{: .note .warning} +Data files provide a high degree of flexibility. The place where theme developers put text modules may differ from that of the consumer of the theme which can cause unforeseen troubles! + +Related to above example the overriding key `site.data.i18n.testimonials.header` from the theme's `_data/i18n/testimonials.yml` file on the consumer site can be located in three different locations: + +- `_data/i18n.yml` with key `testimonials.header` +- `_data/i18n/testimonials.yml` with key `header` (which mirrors the layout of the given example) +- `_data/i18n/testimonials/header.yml` without any key, the headline can go straight into the file + +Theme developers should have this ambiguity in mind, when supporting consumers that feel lost in setting their text modules for the design elements the theme provides. + +{: .note .info} +When using the data feature ask yourself, is the key that you introduce something that changes the behaviour of the theme when present or not, or is it just data that's displayed anyway. If it's changing the behaviour of the theme it should go into `site.config` otherwise it's fine to be provided via `site.data`. + +Bundling data that modifies the behavior of a theme is considered an **anti-pattern** whose use is strongly discouraged. It is solely up to the author of the theme to ensure that every provided data can be easily overridden by the consumer of the theme if they desire to. + +## Converting gem-based themes to regular themes + +Suppose you want to get rid of the gem-based theme and convert it to a regular theme, where all files are present in your Jekyll site directory, with nothing stored in the theme gem. + +To do this, copy the files from the theme gem's directory into your Jekyll site directory. (For example, copy them to `/myblog` if you created your Jekyll site at `/myblog`. See the previous section for details.) + +Then you must tell Jekyll about the plugins that were referenced by the theme. You can find these plugins in the theme's gemspec file as runtime dependencies. If you were converting the Minima theme, for example, you might see: + +```ruby +spec.add_runtime_dependency "jekyll-feed", "~> 0.12" +spec.add_runtime_dependency "jekyll-seo-tag", "~> 2.6" +``` + +You should include these references in the `Gemfile` in one of two ways. + +You could list them individually in both `Gemfile` and `_config.yml`. + +```ruby +# ./Gemfile + +gem "jekyll-feed", "~> 0.12" +gem "jekyll-seo-tag", "~> 2.6" +``` + +```yaml +# ./_config.yml + +plugins: + - jekyll-feed + - jekyll-seo-tag +``` + +Or you could list them explicitly as Jekyll plugins in your Gemfile, and not update `_config.yml`, like this: + +```ruby +# ./Gemfile + +group :jekyll_plugins do + gem "jekyll-feed", "~> 0.12" + gem "jekyll-seo-tag", "~> 2.6" +end +``` + +Either way, don't forget to `bundle update`. + +If you're publishing on GitHub Pages you should update only your `_config.yml` as GitHub Pages doesn't load plugins via Bundler. + +Finally, remove references to the theme gem in `Gemfile` and configuration. For example, to remove `minima`: + +- Open `Gemfile` and remove `gem "minima", "~> 2.5"`. +- Open `_config.yml` and remove `theme: minima`. + +Now `bundle update` will no longer get updates for the theme gem. + +## Installing a gem-based theme {#installing-a-theme} + +The `jekyll new <PATH>` command isn't the only way to create a new Jekyll site with a gem-based theme. You can also find gem-based themes online and incorporate them into your Jekyll project. + +For example, search for [jekyll theme on RubyGems](https://rubygems.org/search?utf8=%E2%9C%93&query=jekyll-theme) to find other gem-based themes. (Note that not all themes are using `jekyll-theme` as a convention in the theme name.) + +To install a gem-based theme: + +1. Add the theme gem to your site's `Gemfile`: + + ```ruby + # ./Gemfile + + # This is an example, declare the theme gem you want to use here + gem "jekyll-theme-minimal" + ``` + + Or if you've started with the `jekyll new` command, replace `gem "minima", "~> 2.0"` with the gem you want, e.g: + + ```diff + # ./Gemfile + + - gem "minima", "~> 2.5" + + gem "jekyll-theme-minimal" + ``` + +2. Install the theme: + + ```sh + bundle install + ``` + +3. Add the following to your site's `_config.yml` to activate the theme: + + ```yaml + theme: jekyll-theme-minimal + ``` + +4. Build your site: + + ```sh + bundle exec jekyll serve + ``` + +{: .note .info} +You can have multiple themes listed in your site's `Gemfile`, but only one theme can be selected in your site's `_config.yml`. + +If you're publishing your Jekyll site on [GitHub Pages](https://pages.github.com/), note that GitHub Pages supports only [some gem-based themes](https://pages.github.com/themes/). GitHub Pages also supports [using any theme hosted on GitHub](https://help.github.com/articles/adding-a-jekyll-theme-to-your-github-pages-site/#adding-a-jekyll-theme-in-your-sites-_configyml-file) using the `remote_theme` configuration as if it were a gem-based theme. + +## Creating a gem-based theme + +If you're a Jekyll theme developer (rather than a consumer of themes), you can package up your theme in RubyGems and allow users to install it through Bundler. + +If you're unfamiliar with creating Ruby gems, don't worry. Jekyll will help you scaffold a new theme with the `new-theme` command. Run `jekyll new-theme` with the theme name as an argument. + +Here is an example: + +```sh +jekyll new-theme jekyll-theme-awesome + create /path/to/jekyll-theme-awesome/_layouts + create /path/to/jekyll-theme-awesome/_includes + create /path/to/jekyll-theme-awesome/_sass + create /path/to/jekyll-theme-awesome/_layouts/page.html + create /path/to/jekyll-theme-awesome/_layouts/post.html + create /path/to/jekyll-theme-awesome/_layouts/default.html + create /path/to/jekyll-theme-awesome/Gemfile + create /path/to/jekyll-theme-awesome/jekyll-theme-awesome.gemspec + create /path/to/jekyll-theme-awesome/README.md + create /path/to/jekyll-theme-awesome/LICENSE.txt + initialize /path/to/jekyll-theme-awesome/.git + create /path/to/jekyll-theme-awesome/.gitignore +Your new Jekyll theme, jekyll-theme-awesome, is ready for you in /path/to/jekyll-theme-awesome! +For help getting started, read /path/to/jekyll-theme-awesome/README.md. +``` + +Add your template files in the corresponding folders. Then complete the `.gemspec` and the README files according to your needs. + +### Layouts and includes + +Theme layouts and includes work just like they work in any Jekyll site. Place layouts in your theme's `/_layouts` folder, and place includes in your themes `/_includes` folder. + +For example, if your theme has a `/_layouts/page.html` file, and a page has `layout: page` in its front matter, Jekyll will first look to the site's `_layouts` folder for the `page` layout, and if none exists, will use your theme's `page` layout. + +### Assets + +Any file in `/assets` will be copied over to the user's site upon build unless they have a file with the same relative path. You can ship any kind of asset here: SCSS, an image, a webfont, etc. These files behave like pages and static files in Jekyll: + +- If the file has [front matter](/docs/front-matter/) at the top, it will be rendered. +- If the file does not have front matter, it will simply be copied over into the resulting site. + +This allows theme creators to ship a default `/assets/styles.scss` file which their layouts can depend on as `/assets/styles.css`. + +All files in `/assets` will be output into the compiled site in the `/assets` folder just as you'd expect from using Jekyll on your sites. + +### Stylesheets + +Your theme's stylesheets should be placed in your theme's `_sass` folder, again, just as you would when authoring a Jekyll site. + +``` +_sass +└── jekyll-theme-awesome.scss +``` + +Your theme's styles can be included in the user's stylesheet using the `@import` directive. + +{% raw %} +```css +@import "{{ site.theme }}"; +``` +{% endraw %} + +### Theme-gem dependencies {%- include docs_version_badge.html version="3.5.0" -%} + +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. + +### Pre-configuring Theme-gems {%- include docs_version_badge.html version="4.0" -%} + +Jekyll will read-in a `_config.yml` at the root of the theme-gem and merge its data into the site's existing configuration data. + +But unlike other entities loaded from within the theme, loading the config file comes with a few restrictions, as summarized below: + * Jekyll's default settings cannot be overridden by a theme-config. That *ball is still in the user's court.* + * The theme-config-file cannot be a symlink, irrespective of `safe mode` and whether the file pointed to by the symlink is a legitimate file within the theme-gem. + * The theme-config should be a set of key-value pairs. An empty config file, a config file that simply *lists items* under a key, or a config file with just a simple string of text will simply be ignored silently. Users will not get a warning or any log output regarding this discrepancy. + * Any settings defined by the theme-config can be overridden by the user. + +While this feature is to enable easier adoption of a theme, the restrictions ensure that a theme-config cannot affect the build in a concerning manner. Any plugins required by the theme will have to be listed manually by the user or provided by the theme's `gemspec` file. + +This feature will let the theme-gem to work with *theme-specific config variables* out-of-the-box. + +### Documenting your theme + +Your theme should include a `/README.md` file, which explains how site authors can install and use your theme. What layouts are included? What includes? Do they need to add anything special to their site's configuration file? + +### Adding a screenshot + +Themes are visual. Show users what your theme looks like by including a screenshot as `/screenshot.png` within your theme's repository where it can be retrieved programmatically. You can also include this screenshot within your theme's documentation. + +### Previewing your theme + +To preview your theme as you're authoring it, it may be helpful to add dummy content in, for example, `/index.html` and `/page.html` files. This will allow you to use the `jekyll build` and `jekyll serve` commands to preview your theme, just as you'd preview a Jekyll site. + +{: .note .info} +If you do preview your theme locally, be sure to add `/_site` to your theme's `.gitignore` file to prevent the compiled site from also being included when you distribute your theme. + +### Publishing your theme + +Themes are published via [RubyGems.org](https://rubygems.org). You will need a RubyGems account, which you can [create for free](https://rubygems.org/sign_up). + +1. First, you need to have it in a git repository: + + ```sh + git init # Only the first time + git add -A + git commit -m "Init commit" + ``` + +2. Next, package your theme, by running the following command, replacing `jekyll-theme-awesome` with the name of your theme: + + ```sh + gem build jekyll-theme-awesome.gemspec + ``` + +3. Finally, push your packaged theme up to the RubyGems service, by running the following command, again replacing `jekyll-theme-awesome` with the name of your theme: + + ```sh + gem push jekyll-theme-awesome-*.gem + ``` + +4. To release a new version of your theme, update the version number in the gemspec file, ( `jekyll-theme-awesome.gemspec` in this example ), and then repeat Steps 1 - 3 above. We recommend that you follow [Semantic Versioning](https://semver.org) while bumping your theme-version. diff --git a/docs/_docs/troubleshooting.md b/docs/_docs/troubleshooting.md new file mode 100644 index 0000000..105833f --- /dev/null +++ b/docs/_docs/troubleshooting.md @@ -0,0 +1,323 @@ +--- +title: Troubleshooting +permalink: /docs/troubleshooting/ +--- + +If you ever run into problems installing or using Jekyll, here are a few tips +that might be of help. If the problem you’re experiencing isn’t covered below, +**please [check out our other help resources](/help/)** as well. + +- [Installation Problems](#installation-problems) +- [Problems running Jekyll](#problems-running-jekyll) +- [Base-URL Problems](#base-url-problems) +- [Configuration problems](#configuration-problems) +- [Markup Problems](#markup-problems) +- [Production Problems](#production-problems) + +## Installation Problems + +If you encounter errors during gem installation, you may need to install +the header files for compiling extension modules for Ruby 2.x This +can be done on Ubuntu or Debian by running: + +```sh +sudo apt-get install ruby2.6-dev +``` + +On Red Hat, CentOS, and Fedora systems you can do this by running: + +```sh +sudo yum install ruby-devel +``` + +On Arch Linux you need to run: + +```sh +sudo pacman -S ruby-ffi +``` + +On Ubuntu if you get stuck after `bundle exec jekyll serve` and see error +messages like `Could not locate Gemfile` or `.bundle/ directory`, it's likely +because all requirements have not been fully met. Recent stock Ubuntu +distributions require the installation of both the `ruby` and `ruby-all-dev` +packages: + +```sh +sudo apt-get install ruby ruby-all-dev +``` + +On [NearlyFreeSpeech](https://www.nearlyfreespeech.net/) you need to run the +following commands before installing Jekyll: + +```sh +export GEM_HOME=/home/private/gems +export GEM_PATH=/home/private/gems:/usr/local/lib/ruby/gems/1.8/ +export PATH=$PATH:/home/private/gems/bin +export RB_USER_INSTALL='true' +``` + +To install RubyGems on Gentoo: + +```sh +sudo emerge -av dev-ruby/rubygems +``` + +On Windows, you may need to install [RubyInstaller +DevKit](https://wiki.github.com/oneclick/rubyinstaller/development-kit). + +On Android (with Termux) you can install all requirements by running: + +```sh +apt update && apt install libffi-dev clang ruby-dev make +``` + +On macOS, you may need to update RubyGems (using `sudo` only if necessary): + +```sh +gem update --system +``` + +If you still have issues, you can download and install new Command Line +Tools (such as `gcc`) using the following command: + +```sh +xcode-select --install +``` + +which may allow you to install native gems using this command (again, using +`sudo` only if necessary): + +```sh +gem install jekyll +``` + +Note that upgrading macOS does not automatically upgrade Xcode itself +(that can be done separately via the App Store), and having an out-of-date +Xcode.app can interfere with the command line tools downloaded above. If +you run into this issue, upgrade Xcode and install the upgraded Command +Line Tools. + +### Running Jekyll as Non-Superuser (no sudo!) +{: #no-sudo} + +On most flavors of Linux, macOS, and Bash on Ubuntu on Windows, it is +possible to run Jekyll as a non-superuser and without having to install +gems to system-wide locations by adding the following lines to the end +of your `.bashrc` file: + +```bash +# Ruby exports + +export GEM_HOME=$HOME/gems +export PATH=$HOME/gems/bin:$PATH +``` + +This tells `gem` to place its gems within the user's home folder, +not in a system-wide location, and adds the local `jekyll` command to the +user's `PATH` ahead of any system-wide paths. + +This is also useful for many shared webhosting services, where user accounts +have only limited privileges. Adding these exports to `.bashrc` before running +`gem install jekyll bundler` allows a complete non-`sudo` install of Jekyll. + +To activate the new exports, either close and restart Bash, logout and +log back into your shell account, or run `. .bashrc` in the +currently-running shell. + +If you see the following error when running the `jekyll new` command, +you can solve it by using the above-described procedure: + +```sh +jekyll new test + +Running bundle install in /home/user/test... + +Your user account is not allowed to install to the system RubyGems. +You can cancel this installation and run: + + bundle install --path vendor/bundle + +to install the gems into ./vendor/bundle/, or you can enter your password +and install the bundled gems to RubyGems using sudo. + +Password: +``` + +Once this is done, the `jekyll new` command should work properly for +your user account. + +### Jekyll & macOS + +With the introduction of System Integrity Protection in v10.11, several directories +that were previously writable are now considered system locations and are no +longer available. Given these changes, there are a couple of simple ways to get +up and running. One option is to change the location where the gem will be +installed (again, using `sudo` only if necessary): + +```sh +gem install -n /usr/local/bin jekyll +``` + +Alternatively, Homebrew can be installed and used to set up Ruby. This can be +done as follows: + +```sh +ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" +``` + +Once Homebrew is installed, the second step is to run: + +```sh +brew install ruby +``` + +Advanced users (with more complex needs) may find it helpful to choose one of a +number of Ruby version managers ([RVM][], [rbenv][], [chruby][], [etc][].) in +which to install Jekyll. + +[RVM]: https://rvm.io +[rbenv]: http://rbenv.org +[chruby]: https://github.com/postmodern/chruby +[etc]: https://github.com/rvm/rvm/blob/master/docs/alt.md + +If you elect to use one of the above methods to install Ruby, it might be +necessary to modify your `$PATH` variable using the following command: + +```sh +export PATH=/usr/local/bin:$PATH +``` + +GUI apps can modify the `$PATH` as follows: + +```sh +launchctl setenv PATH "/usr/local/bin:$PATH" +``` + +Either of these approaches are useful because `/usr/local` is considered a +"safe" location on systems which have SIP enabled, they avoid potential +conflicts with the version of Ruby included by Apple, and it keeps Jekyll and +its dependencies in a sandboxed environment. This also has the added +benefit of not requiring `sudo` when you want to add or remove a gem. + +### Could not find a JavaScript runtime. (ExecJS::RuntimeUnavailable) + +This error can occur during the installation of `jekyll-coffeescript` when +you don't have a proper JavaScript runtime. To solve this, either install +`execjs` and `therubyracer` gems, or install `nodejs`. Check out +[issue #2327](https://github.com/jekyll/jekyll/issues/2327) for more info. + +## Problems running Jekyll + +### macOS + +Jekyll is compatible with macOS with ARM64 architecture. +However, `bundle exec jekyll serve` may [fail with older version `ffi`](https://github.com/ffi/ffi/issues/870). + +You may need to run `bundle update` or update `ffi` to at least `1.14.2` manually. + +### Debian or Ubuntu + +On Debian or Ubuntu, you may need to add `/var/lib/gems/1.8/bin/` to your path +in order to have the `jekyll` executable be available in your Terminal. + +## Base-URL Problems + +If you are using base-url option like: + +```sh +jekyll serve --baseurl '/blog' +``` + +… then make sure that you access the site at: + +``` +http://localhost:4000/blog/index.html +``` + +It won’t work to just access: + +``` +http://localhost:4000/blog +``` + +## Configuration problems + +The order of precedence for conflicting [configuration settings](/docs/configuration/) +is as follows: + +1. Command-line flags +2. Configuration file settings +3. Defaults + +That is: defaults are overridden by options specified in `_config.yml`, +and flags specified at the command-line will override all other settings +specified elsewhere. + +**Note: From v3.3.0 onward, Jekyll does not process `node_modules` and certain subdirectories within `vendor`, by default. But, by having an `exclude:` array defined explicitly in the config file overrides this default setting, which results in some users to encounter an error in building the site, with the following error message:** + +``` + ERROR: YOUR SITE COULD NOT BE BUILT: + ------------------------------------ + Invalid date '<%= Time.now.strftime('%Y-%m-%d %H:%M:%S %z') %>': + Document 'vendor/bundle/gems/jekyll-3.4.3/lib/site_template/_posts/0000-00-00-welcome-to-jekyll.markdown.erb' + does not have a valid date in front matter. +``` + +Adding `vendor/bundle` to the `exclude:` list will solve this problem but will lead to having other sub-directories under `/vendor/` (and also `/node_modules/`, if present) be processed to the destination folder `_site`. + +The proper solution is to incorporate the default setting for `exclude:` rather than override it completely: + +For versions up to `v3.4.3`, the `exclude:` setting must look like following: + +```yaml +exclude: + - Gemfile + - Gemfile.lock + - node_modules + - vendor/bundle/ + - vendor/cache/ + - vendor/gems/ + - vendor/ruby/ + - any_additional_item # any user-specific listing goes at the end +``` + +From `v3.5` onward, `Gemfile` and `Gemfile.lock` are also excluded by default. So, in most cases there is no need to define another `exclude:` array in the config file. So an existing definition can either be modified as above, or removed completely, or commented out to enable easy edits in future. + +## Markup Problems + +The various markup engines that Jekyll uses may have some issues. This +page will document them to help others who may run into the same +problems. + +### Liquid + +Liquid version 2.0 seems to break the use of `{{ "{{" }}` in templates. +Unlike previous versions, using `{{ "{{" }}` in 2.0 triggers the following error: + +``` +'{{ "{{" }}' was not properly terminated with regexp: /\}\}/ (Liquid::SyntaxError) +``` + +### Excerpts + +Since v1.0.0, Jekyll has had automatically-generated post excerpts. Since +v1.1.0, Jekyll also passes these excerpts through Liquid, which can cause +strange errors where references don't exist or a tag hasn't been closed. If you +run into these errors, try setting `excerpt_separator: ""` in your +`_config.yml`, or set it to some nonsense string. + +## Production Problems + +If you run into an issue that a static file can't be found in your +production environment during build since v3.2.0 you should set your +[environment to `production`](/docs/configuration/environments/). +The issue is caused by trying to copy a non-existing symlink. + +<div class="note"> + <h5>Please report issues you encounter!</h5> + <p> + If you come across a bug, please <a href="{{ site.repository }}/issues/new">create an issue</a> + on GitHub describing the problem and any work-arounds you find so we can + document it here for others. + </p> +</div> diff --git a/docs/_docs/upgrading.md b/docs/_docs/upgrading.md new file mode 100644 index 0000000..3713800 --- /dev/null +++ b/docs/_docs/upgrading.md @@ -0,0 +1,30 @@ +--- +layout: docs +title: Upgrading +permalink: /docs/upgrading/ +--- + +Upgrading from an older version of Jekyll? Upgrading to a new major version of +Jekyll (e.g. from v2.x to v3.x) may cause some headaches. Take the following +guides to aid your upgrade: + +- [From 0.x to 1.x and 2.x](/docs/upgrading/0-to-2/) +- [From 2.x to 3.x](/docs/upgrading/2-to-3/) +- [From 3.x to 4.x](/docs/upgrading/3-to-4/) + +## Minor updates + +<div class="note"> + <h5>Stay Up to Date</h5> + <p>We recommend you update Jekyll as often as possible to benefit from + the latest bug fixes. + </p> +</div> + +If you followed our setup recommendations and installed [Bundler](http://bundler.io/), run `bundle update jekyll` or simply `bundle update` and all your gems will +update to the latest versions. + +If you don't have Bundler installed, run `gem update jekyll`. + +The procedure is similar [if you use the `github-pages` +gem](https://help.github.com/articles/setting-up-your-github-pages-site-locally-with-jekyll/#keeping-your-site-up-to-date-with-the-github-pages-gem). diff --git a/docs/_docs/upgrading/0-to-2.md b/docs/_docs/upgrading/0-to-2.md new file mode 100644 index 0000000..5ea8219 --- /dev/null +++ b/docs/_docs/upgrading/0-to-2.md @@ -0,0 +1,138 @@ +--- +title: Upgrading from 0.x to 2.x +permalink: /docs/upgrading/0-to-2/ +--- + +Upgrading from an older version of Jekyll? A few things have changed in 1.0 +and 2.0 that you'll want to know about. + +Before we dive in, go ahead and fetch the latest version of Jekyll: + +```sh +gem update jekyll +``` + +<div class="note feature"> + <h5 markdown="1">Diving in</h5> + <p markdown="1">Want to get a new Jekyll site up and running quickly? Simply + run <code>jekyll new SITENAME</code> to create a new folder with a bare bones + Jekyll site.</p> +</div> + +### The Jekyll Command + +For better clarity, Jekyll now accepts the commands `build` and `serve`. +Whereas before you might simply run the command `jekyll` to generate a site +and `jekyll --server` to view it locally, in v2.0 (and later) you should +use the subcommands `jekyll build` and `jekyll serve` to build and preview +your site. + +<div class="note info"> + <h5>Watching and Serving</h5> + <p markdown="1">With the new subcommands, the way sites are previewed locally + changed a bit. Instead of specifying `server: true` in the site's + configuration file, use `jekyll serve`. The same holds true for + `watch: true`. Instead, use the `--watch` flag with either `jekyll serve` + or `jekyll build`.</p> +</div> + +### Absolute Permalinks + +In Jekyll v1.0, we introduced absolute permalinks for pages in +subdirectories. Starting with v2.0, absolute permalinks are opt-out, +meaning Jekyll will default to using absolute permalinks instead of +relative permalinks. Relative permalink backwards-compatibility was removed in v3.0. + +<div class="note warning" id="absolute-permalinks-warning"> + <h5 markdown="1">Absolute permalinks will be required in v3.0 and on</h5> + <p markdown="1"> + Starting with Jekyll v3.0, relative permalinks functionality will be removed and thus unavailable for use. + </p> +</div> + +### Draft Posts + +Jekyll now lets you write draft posts, and allows you to easily preview how +they will look prior to publishing. To start a draft, create a folder +called `_drafts` in your site's source directory (e.g., alongside `_posts`), +and add a new markdown file to it. To preview your new post, run the +`jekyll serve` command with the `--drafts` flag. + +<div class="note info"> + <h5 markdown="1">Drafts don't have dates</h5> + <p markdown="1"> + Unlike posts, drafts don't have a date, since they haven't + been published yet. Rather than naming your draft something like + `2013-07-01-my-draft-post.md`, simply name the file what you'd like your + post to eventually be titled, here `my-draft-post.md`.</p> +</div> + +### Custom Config File + +Rather than passing individual flags via the command line, you can now pass +an entire custom Jekyll config file. This helps to distinguish between +environments, or lets you programmatically override user-specified +defaults. Add the `--config` flag to the `jekyll` command, followed +by the path to one or more config files (comma-delimited, no spaces). + +#### As a result, the following command line flags are now deprecated: + +* `--no-server` +* `--no-auto` (now `--no-watch`) +* `--auto` (now `--watch`) +* `--server` +* `--url=` +* `--maruku`, `--rdiscount`, and `--redcarpet` +* `--pygments` +* `--permalink=` +* `--paginate` + +<div class="note info"> + <h5>The config flag explicitly specifies your configuration file(s)</h5> + <p markdown="1">If you use the `--config` flag, Jekyll will ignore your + `_config.yml` file. Want to merge a custom configuration with the normal + configuration? No problem. Jekyll will accept more than one custom config + file via the command line. Config files cascade from right to left, such + that if I run `jekyll serve --config _config.yml,_config-dev.yml`, + the values in the config files on the right (`_config-dev.yml`) overwrite + those on the left (`_config.yml`) when both contain the same key.</p> +</div> + +### New Config File Options + +Jekyll 1.0 introduced several new config file options. Before you upgrade, +you should check to see if any of these are present in your pre-1.0 config +file, and if so, make sure that you're using them properly: + +* `excerpt_separator` +* `host` +* `include` +* `keep_files` +* `layouts` +* `show_drafts` +* `timezone` +* `url` + +### Baseurl + +Often, you'll want the ability to run a Jekyll site in multiple places, +such as previewing locally before pushing to GitHub Pages. Jekyll 1.0 makes +that easier with the new `--baseurl` flag. To take advantage of this +feature, first add the production `baseurl` to your site's `_config.yml` +file. Then, throughout the site, prefix relative URLs +with {% raw %}`{{ site.baseurl }}`{% endraw %}. +When you're ready to preview your site locally, pass along the `--baseurl` +flag with your local baseurl (most likely `/`) to `jekyll serve` and Jekyll +will swap in whatever you've passed along, ensuring all your links work as +you'd expect in both environments. + +<div class="note warning"> + <h5 markdown="1">All page and post URLs contain leading slashes</h5> + <p markdown="1">If you use the method described above, please remember + that the URLs for all posts and pages contain a leading slash. Therefore, + concatenating the site baseurl and the post/page url where + `site.baseurl = /` and `post.url = /2013/06/05/my-fun-post/` will + result in two leading slashes, which will break links. It is thus + suggested that prefixing with `site.baseurl` only be used when the + `baseurl` is something other than the default of `/`.</p> +</div> diff --git a/docs/_docs/upgrading/2-to-3.md b/docs/_docs/upgrading/2-to-3.md new file mode 100644 index 0000000..f034d49 --- /dev/null +++ b/docs/_docs/upgrading/2-to-3.md @@ -0,0 +1,142 @@ +--- +title: Upgrading from 2.x to 3.x +permalink: /docs/upgrading/2-to-3/ +--- + +Upgrading from an older version of Jekyll? A few things have changed in Jekyll 3 +that you'll want to know about. + +Before we dive in, go ahead and fetch the latest version of Jekyll: + +```sh +gem update jekyll +``` + +{: .note .warning} +Since version {% include docs_version_badge.html version="3.2"%}, Jekyll requires Ruby version >= 2.1. + +<div class="note feature"> + <h5>Diving in</h5> + <p>Want to get a new Jekyll site up and running quickly? Simply + run <code>jekyll new SITENAME</code> to create a new folder with a bare bones + Jekyll site.</p> +</div> + +### site.collections has changed + +In 2.x, your iterations over `site.collections` yielded an array with the collection +label and the collection object as the first and second items, respectively. In 3.x, +this complication has been removed and iterations now yield simply the collection object. +A simple conversion must be made in your templates: + +- `collection[0]` becomes `collection.label` +- `collection[1]` becomes `collection` + +When iterating over `site.collections`, ensure the above conversions are made. + +For `site.collections.myCollection` in Jekyll 2, you now do: + +{% raw %} +```liquid +{% assign myCollection = site.collections | where: "label", "myCollection" | first %} +``` +{% endraw %} + +This is a bit cumbersome at first, but is easier than a big `for` loop. + +### Textile support + +We dropped native support for Textile, from now on you have to install our [jekyll-textile-converter](https://github.com/jekyll/jekyll-textile-converter) plugin to work with Textile files. + +### Dropped dependencies + +We dropped a number of dependencies the Core Team felt were optional. As such, in 3.0, they must be explicitly installed and included if you use any of the features. They are: + +- jekyll-paginate – Jekyll's pagination solution from days past +- jekyll-coffeescript – processing of CoffeeScript +- jekyll-gist – the `gist` Liquid tag +- pygments.rb – the Pygments highlighter +- redcarpet – the Markdown processor +- toml – an alternative to YAML for configuration files +- classifier-reborn – for `site.related_posts` + +### Future posts + +A seeming feature regression in 2.x, the `--future` flag was automatically _enabled_. +The future flag allows post authors to give the post a date in the future and to have +it excluded from the build until the system time is equal or after the post time. +In Jekyll 3, this has been corrected. **Now, `--future` is disabled by default.** +This means you will need to include `--future` if you want your future-dated posts to +generate when running `jekyll build` or `jekyll serve`. + +<div class="note info"> + <h5>Future Posts on GitHub Pages</h5> + <p> + An exception to the above rule are GitHub Pages sites, where the <code>--future</code> flag remains <em>enabled</em> + by default to maintain historical consistency for those sites. + </p> +</div> + +### Layout metadata + +Introducing: `layout`. In Jekyll 2 and below, any metadata in the layout was merged onto +the `page` variable in Liquid. This caused a lot of confusion in the way the data was +merged and some unexpected behaviour. In Jekyll 3, all layout data is accessible via `layout` +in Liquid. For example, if your layout has `class: my-layout` in its front matter, +then the layout can access that via {% raw %}`{{ layout.class }}`{% endraw %}. + +### Syntax highlighter changed + +For the first time, the default syntax highlighter has changed for the +`highlight` tag and for backtick code blocks. Instead of [Pygments.rb](https://github.com/tmm1/pygments.rb), +it's now [Rouge](http://rouge.jneen.net/). If you were using the `highlight` tag with certain +options, such as `hl_lines`, they may not be available when using Rouge. To +go back to using Pygments, set `highlighter: pygments` in your +`_config.yml` file and run `gem install pygments.rb` or add +`gem 'pygments.rb'` to your project's `Gemfile`. + +### Relative Permalink support removed + +In Jekyll 3 and above, relative permalinks have been deprecated. If you +created your site using Jekyll 2 and below, you may receive the following +error when trying to **serve** or **build**: + +``` +Since v3.0, permalinks for pages in subfolders must be relative to the site +source directory, not the parent directory. Check +https://jekyllrb.com/docs/upgrading/ for more info. +``` + +This can be fixed by removing the following line from your `_config.yml` file: + +```yaml +relative_permalinks: true +``` + +### Permalinks no longer automatically add a trailing slash + +In Jekyll 2, any URL constructed from the `permalink:` field had a trailing slash (`/`) added to it automatically. Jekyll 3 no longer adds a trailing slash automatically to `permalink:` URLs. This can potentially result in old links to pages returning a 404 error. For example, suppose a page previously contained the YAML `permalink: /:year-:month-:day-:title` that resulted in the URL `example.com/2016-02-01-test/` (notice the trailing slash), Jekyll internally generates a folder named `2016-02-01-test`. In Jekyll 3, the same `permalink:` generate the file `2016-02-01-test.html` and the URL for the same page will be `example.com/2016-02-01-test`, and consequently any links to the old URL will result in a 404 error. In order to maintain the same URLs and avoid this problem, a trailing slash should be added to the `permalink:` field, for example `permalink: /:year-:month-:day-:title/`. + +### All my posts are gone! Where'd they go! + +Try adding `future: true` to your `_config.yml` file. Are they showing up now? If they are, then you were ensnared by an issue with the way Ruby parses times. Each of your posts is being read in a different timezone than you might expect and, when compared to the computer's current time, is "in the future." The fix for this is to add [a timezone offset](https://en.wikipedia.org/wiki/List_of_UTC_time_offsets) to each post (and make sure you remove `future: true` from your `_config.yml` file). If you're writing from California, for example, you would change this: + +```yaml +--- +date: 2016-02-06 19:32:10 +--- +``` + +to this (note the offset): + +```yaml +--- +date: 2016-02-06 19:32:10 -0800 +--- +``` + +### My categories have stopped working! + +If you organized your categories as `/_posts/code/2008-12-24-closures.md`, you will need to restructure your directories to put the categories _above_ the `_posts` directories, as follows: `/code/_posts/2008-12-24-closures.md`. + +_Did we miss something? Please click "Improve this page" above and add a section. Thanks!_ diff --git a/docs/_docs/upgrading/3-to-4.md b/docs/_docs/upgrading/3-to-4.md new file mode 100644 index 0000000..87ad8ac --- /dev/null +++ b/docs/_docs/upgrading/3-to-4.md @@ -0,0 +1,175 @@ +--- +title: Upgrading from 3.x to 4.x +permalink: /docs/upgrading/3-to-4/ +--- + +A few things have changed in Jekyll 4. + +Before we dive in, you need to have at least Ruby {{ site.data.ruby.min_version }} +installed. + +Run the following in your terminal to check + +```sh +ruby -v +{{ site.data.ruby.current_version_output }} +``` + +If you're using a supported Ruby version >= {{ site.data.ruby.min_version }}, go ahead +and fetch the latest version of Jekyll: + +```sh +gem update jekyll +``` + +<div class="note warning"> + <h5><code>post_url</code> Tag and Baseurl</h5> + <p> </p> + <p> + The <code>post_url</code> tag now incorporates the <code>relative_url</code> filter within itself + and therefore automatically prepends your site's <code>baseurl</code> to the post's <code>url</code> + value. + </p> + <p> + Please ensure that you change all instances of the <code>post_url</code> usage as following: + </p> + +{% highlight diff %} +{% raw %} +- {{ site.baseurl }}/{% post_url 2018-03-20-hello-world.markdown %} ++ {% post_url 2018-03-20-hello-world.markdown %} +{% endraw %} +{% endhighlight %} +</div> + +## Template rendering + +We've slightly altered the way Jekyll parses and renders your various templates +to improve the overall build times. Jekyll now parses a template once, caches it +internally and then renders the parsed template multiple times as required by +your pages and documents. + +The downside to this is that some of the community-authored plugins may not work +as they previously used to. + +## Static files in unrendered collections + +Collections other than `posts` can contain static assets along with Markdown files. +But if the collection has not been configured with metadata `output: true`, then +neither its *documents* nor its *static assets* will be output to the destination +directory. + +## For plugin authors + +* If your plugin depends on the following code: `site.liquid_renderer.file(path).parse(content)`, +note that the return value (`template`, an instance of *`Liquid::Template`*), from that line will +always be the **same object** for a given `path`. <br/> +The *`template`* instance is then rendered as previously, with respect to the `payload` passed to it. +You'll therefore have to ensure that *`payload`* is not memoized or cached in your plugin instance. + +* If its a requirement that `template` you get from the above step *be different* at all times, +you can invoke *`Liquid::Template`* directly: + + ```diff + - template = site.liquid_renderer.file(path).parse(content) + + template = Liquid::Template.parse(content) + ``` + +## Exclusion changes + +We've enhanced our default exclusion array. +It now looks like the following: + +```yaml +# default excludes +exclude: +- .sass-cache/ +- .jekyll-cache/ +- gemfiles/ +- Gemfile +- Gemfile.lock +- node_modules/ +- vendor/bundle/ +- vendor/cache/ +- vendor/gems/ +- vendor/ruby/ +``` + +What's new is that this array **does not get overridden by the `exclude` array +in the user's config file anymore**. The user's exclude entries simply get +**added** to the above default array (if the entry isn't already excluded). + +To forcibly "process" directories or files that have been excluded, list them +in the `include` array instead: + +```yaml +# overrides your excluded items configuration and the default include array ([".htaccess"]) +include: + - .htaccess + - node_modules/uglifier/index.js +``` + +The above configuration directs Jekyll to handle only +`node_modules/uglifier/index.js` while ignoring every other file in the +`node_modules` directory since that directory is "excluded" by default. + +Note that the default `include` array still gets overridden by the `include` +array in your config file. So, be sure to add `.htaccess` to the list if you +need that file to be present in the generated site. + +## Kramdown v2 + +Jekyll has dropped support for `kramdown-1.x` entirely. + +From [`v2.0` onwards](https://kramdown.gettalong.org/news.html#kramdown-200-released) +kramdown requires specific extensions to be additionally installed to use +certain features are desired outside of kramdown's core functionality. + +Out of all the extensions listed in the report linked above, gem +`kramdown-parser-gfm` is automatically installed along with Jekyll 4.0. The +remaining extensions will have to be manually installed by the user depending on +desired functionality, by listing the extension's gem-name in their `Gemfile`. + +Notes: + * `kramdown-converter-pdf` will be ignored by Jekyll Core. To have Jekyll convert Markdown to PDF + you'll have to depend on a plugin that subclasses `Jekyll::Converter` with the + [required methods]({% link _docs/plugins/converters.md %}). + + For example: + + ```ruby + module Jekyll + External.require_with_graceful_fail "kramdown-converter-pdf" + + class Markdown2PDF < Converter + safe true + priority :low + + def matches(ext) + # match only files that have an extension exactly ".markdown" + ext =~ /^\.markdown$/ + end + + def convert(content) + Kramdown::Document.new(content).to_pdf + end + + def output_ext + ".pdf" + end + end + end + ``` + + * Vendors that provide a versioned Jekyll Environment Image (e.g. Docker Image, GitHub Pages, etc) + will have to manually whitelist kramdown's extension gems in their distributions for Jekyll 4.0. + +## Deprecated Configuration Options + +Jekyll 4.0 has dropped support for all legacy configuration options that were deprecated over multiple +releases in the previous series. + +To that end, we shall no longer output a deprecation warning when we encounter a legacy config key nor +shall we gracefully assign their values to the newer counterparts. Depending on the key, it shall either +be ignored or raise an `InvalidConfigurationError` error if the key is still valid but the associated +value is not of the valid type. diff --git a/docs/_docs/usage.md b/docs/_docs/usage.md new file mode 100644 index 0000000..42cd6f2 --- /dev/null +++ b/docs/_docs/usage.md @@ -0,0 +1,33 @@ +--- +title: Command Line Usage +permalink: /docs/usage/ +--- + +The Jekyll gem makes a `jekyll` executable available to you in your terminal. + +The `jekyll` program has several commands but the structure is always: + +``` +jekyll command [argument] [option] [argument_to_option] + +Examples: + jekyll new site/ --blank + jekyll serve --config _alternative_config.yml +``` + +Typically you'll use `jekyll serve` while developing locally and `jekyll build` when you need to generate the site for production. + +For a full list of options and their argument, see [Build Command Options](/docs/configuration/options/#build-command-options). + +Here are some of the most common commands: + +* `jekyll new PATH` - Creates a new Jekyll site with default gem-based theme at specified path. The directories will be created as necessary. +* `jekyll new PATH --blank` - Creates a new blank Jekyll site scaffold at specified path. +* `jekyll build` or `jekyll b` - Performs a one off build your site to `./_site` (by default). +* `jekyll serve` or `jekyll s` - Builds your site any time a source file changes and serves it locally. +* `jekyll clean` - Removes all generated files: destination folder, metadata file, Sass and Jekyll caches. +* `jekyll help` - Shows help, optionally for a given subcommand, e.g. `jekyll help build`. +* `jekyll new-theme` - Creates a new Jekyll theme scaffold. +* `jekyll doctor` - Outputs any deprecation or configuration issues. + +To change Jekyll's default build behavior have a look through the [configuration options](/docs/configuration/). diff --git a/docs/_docs/variables.md b/docs/_docs/variables.md new file mode 100644 index 0000000..0bf9373 --- /dev/null +++ b/docs/_docs/variables.md @@ -0,0 +1,52 @@ +--- +title: Variables +permalink: /docs/variables/ +--- + +Jekyll traverses your site looking for files to process. Any files with +[front matter](/docs/front-matter/) are subject to processing. For each of these +files, Jekyll makes a variety of data available via [Liquid](/docs/liquid/). +The following is a reference of the available data. + +## Global Variables + +{% include docs_variables_table.html scope=site.data.jekyll_variables.global %} + +## Site Variables + +{% include docs_variables_table.html scope=site.data.jekyll_variables.site %} + +## Page Variables + +{% include docs_variables_table.html scope=site.data.jekyll_variables.page %} + +<div class="note"> + <h5>ProTip™: Use Custom Front Matter</h5> + <p> + Any custom front matter that you specify will be available under + <code>page</code>. For example, if you specify <code>custom_css: true</code> + in a page’s front matter, that value will be available as <code>page.custom_css</code>. + </p> + <p> + If you specify front matter in a layout, access that via <code>layout</code>. + For example, if you specify <code>class: full_page</code> in a layout’s front matter, + that value will be available as <code>layout.class</code> in the layout and its parents. + </p> +</div> + +## Theme Variables {%- include docs_version_badge.html version="4.3.0" -%} +{: #theme-variables } + +{% include docs_variables_table.html scope=site.data.jekyll_variables.theme %} + +## Paginator + +{% include docs_variables_table.html scope=site.data.jekyll_variables.paginator %} + +<div class="note info"> + <h5>Paginator variable availability</h5> + <p> + These are only available in index files, however they can be located in a subdirectory, + such as <code>/blog/index.html</code>. + </p> +</div> diff --git a/docs/_includes/analytics.html b/docs/_includes/analytics.html new file mode 100644 index 0000000..5d0e9c4 --- /dev/null +++ b/docs/_includes/analytics.html @@ -0,0 +1,12 @@ +{% if site.google_analytics_id -%} + <!-- Google Analytics (https://www.google.com/analytics) --> + <script> + !function(j,e,k,y,l,L){j.GoogleAnalyticsObject=y,j[y]||(j[y]=function(){ + (j[y].q=j[y].q||[]).push(arguments)}),j[y].l=+new Date,l=e.createElement(k), + L=e.getElementsByTagName(k)[0],l.src='https://www.google-analytics.com/analytics.js', + L.parentNode.insertBefore(l,L)}(window,document,'script','ga'); + + ga('create', '{{ site.google_analytics_id }}', 'jekyllrb.com'); + ga('send', 'pageview'); + </script> +{% endif -%} diff --git a/docs/_includes/anchor_links.html b/docs/_includes/anchor_links.html new file mode 100644 index 0000000..c584ce5 --- /dev/null +++ b/docs/_includes/anchor_links.html @@ -0,0 +1,33 @@ +<script> + var anchorForId = function (id) { + var anchor = document.createElement("a"); + anchor.className = "header-link"; + anchor.href = "#" + id; + anchor.innerHTML = "<span class=\"sr-only\">Permalink</span><i class=\"fa fa-link\"></i>"; + anchor.title = "Permalink"; + return anchor; + }; + + var linkifyAnchors = function (level, containingElement) { + var headers = containingElement.getElementsByTagName("h" + level); + for (var h = 0; h < headers.length; h++) { + var header = headers[h]; + + if (typeof header.id !== "undefined" && header.id !== "") { + header.appendChild(anchorForId(header.id)); + } + } + }; + + document.onreadystatechange = function () { + if (this.readyState === "complete") { + var contentBlock = document.getElementsByClassName("docs")[0] || document.getElementsByClassName("news")[0]; + if (!contentBlock) { + return; + } + for (var level = 1; level <= 6; level++) { + linkifyAnchors(level, contentBlock); + } + } + }; +</script> diff --git a/docs/_includes/docs_contents.html b/docs/_includes/docs_contents.html new file mode 100644 index 0000000..931500a --- /dev/null +++ b/docs/_includes/docs_contents.html @@ -0,0 +1,15 @@ +<div class="unit one-fifth hide-on-mobiles"> + <aside> + {% for section in site.data.docs_nav -%} + <h4>{{ section.title }}</h4> + <ul> + {%- for item in section.docs -%} + {%- assign p = site.docs | where: "url", item.link | first %} + <li {%- if page.url == p.url %} class="current" {%- endif -%}><a href="{{ p.url | relative_url }}"> + {{- p.menu_name | default: p.title -}} + </a></li> + {%- endfor %} + </ul> + {% endfor -%} + </aside> +</div> diff --git a/docs/_includes/docs_contents_mobile.html b/docs/_includes/docs_contents_mobile.html new file mode 100644 index 0000000..6574b6a --- /dev/null +++ b/docs/_includes/docs_contents_mobile.html @@ -0,0 +1,15 @@ +<div class="docs-nav-mobile unit whole show-on-mobiles"> + <select onchange="if (this.value) window.location.href=this.value"> + <option value="">Navigate the docs…</option> + {% for section in site.data.docs_nav -%} + <optgroup label="{{ section.title }}"> + {%- for item in section.docs -%} + {% assign p = site.docs | where: "url", item.link | first %} + <option value="{{ p.url | relative_url }}"> + {{- p.menu_name | default: p.title -}} + </option> + {%- endfor %} + </optgroup> + {% endfor -%} + </select> +</div> diff --git a/docs/_includes/docs_variables_table.html b/docs/_includes/docs_variables_table.html new file mode 100644 index 0000000..feafa71 --- /dev/null +++ b/docs/_includes/docs_variables_table.html @@ -0,0 +1,18 @@ +<div class="mobile-side-scroller"> +<table> + <thead> + <tr> + <th>Variable</th> + <th>Description</th> + </tr> + </thead> + <tbody> + {% for var in include.scope -%} + <tr> + <td><p><code>{{ var.name }}</code></p></td> + <td><p>{{- var.description -}}</p></td> + </tr> + {% endfor -%} + </tbody> +</table> +</div> diff --git a/docs/_includes/docs_version_badge.html b/docs/_includes/docs_version_badge.html new file mode 100644 index 0000000..3c35810 --- /dev/null +++ b/docs/_includes/docs_version_badge.html @@ -0,0 +1 @@ +<span class="version-badge" title="This feature is available starting version {{ include.version }}">{{ include.version }}</span> diff --git a/docs/_includes/footer.html b/docs/_includes/footer.html new file mode 100644 index 0000000..6c2a8b4 --- /dev/null +++ b/docs/_includes/footer.html @@ -0,0 +1,24 @@ +<footer> + <div class="grid"> + <div class="unit one-third center-on-mobiles"> + <p>Jekyll is lovingly maintained by the <a href="{{ '/team/' | relative_url }}">core team</a> of volunteers. </p> + <p>The contents of this website are <br />© {{ site.time | date: '%Y' }} under the terms of the <a href="{{ site.repository }}/blob/master/LICENSE">MIT License</a>.</p> + </div> + <div class="unit two-thirds align-right center-on-mobiles"> + <p> + Proudly hosted by + <a href="https://github.com"> + <img src="{{ 'img/footer-logo.png' | relative_url }}" width="100" height="30" alt="GitHub • Social coding"> + </a> + </p> + </div> + <div class="unit two-thirds align-right center-on-mobiles"> + <p> + Jekyll is funded thanks to its + <a href="https://github.com/jekyll/jekyll#sponsors"> + sponsors! + </a> + </p> + </div> + </div> +</footer> diff --git a/docs/_includes/header.html b/docs/_includes/header.html new file mode 100644 index 0000000..b03a850 --- /dev/null +++ b/docs/_includes/header.html @@ -0,0 +1,27 @@ +<header> + <div class="flexbox"> + <div class="center-on-mobiles"> + <h1> + <a href="{{ '/' | relative_url }}" class="logo"> + <span class="sr-only">Jekyll</span> + <img src="{{ 'img/logo-2x.png' | relative_url }}" width="140" height="65" alt="Jekyll Logo"> + </a> + </h1> + </div> + <nav class="main-nav hide-on-mobiles"> + {% include primary-nav-items.html -%} + </nav> + <div class="search hide-on-mobiles"> + {% include search/input.html -%} + </div> + <div class="meta hide-on-mobiles"> + <ul> + <li><a href="{{ site.repository }}/releases/tag/v{{ site.version }}">v{{ site.version }}</a></li> + <li><a href="{{ site.repository }}">GitHub</a></li> + </ul> + </div> + </div> + <nav class="mobile-nav show-on-mobiles"> + {% include mobile-nav-items.html -%} + </nav> +</header> diff --git a/docs/_includes/mobile-nav-items.html b/docs/_includes/mobile-nav-items.html new file mode 100644 index 0000000..5dd9b86 --- /dev/null +++ b/docs/_includes/mobile-nav-items.html @@ -0,0 +1,14 @@ +<ul> + {% for p in site.data.primary_nav -%} + {% if p.show_on_mobile -%} + <li + {%- if p.link == '/' -%} + {%- if page.url == '/' %} class="current" {% endif -%} + {% else -%} + {%- if page.url contains p.link %} class="current" {% endif -%} + {% endif -%} + ><a href="{{ p.link | relative_url }}">{{ p.title }}</a></li> + {% endif -%} + {% endfor -%} + <li><a href="{{ site.repository }}">GitHub</a></li> +</ul> diff --git a/docs/_includes/news_contents.html b/docs/_includes/news_contents.html new file mode 100644 index 0000000..ea0a558 --- /dev/null +++ b/docs/_includes/news_contents.html @@ -0,0 +1,33 @@ +<div class="unit one-fifth hide-on-mobiles"> + <aside> + <ul> + <li {%- if page.title == 'News' %} class="current" {%- endif %}> + <a href="{{ '/news/' | relative_url }}">All News</a> + </li> + <li {%- if page.title == 'Releases' %} class="current" {%- endif %}> + <a href="{{ '/news/releases/' | relative_url }}">Jekyll Releases</a> + </li> + </ul> + <h4>Recent Releases</h4> + <ul> + {% for post in site.categories.release limit:5 -%} + <li {% if page.title == post.title %} class="current"{% endif %}> + <a href="{{ post.url | relative_url }}">Version {{ post.version }}</a> + </li> + {% endfor -%} + <li> + <a href="{{ '/docs/history/' | relative_url }}">History »</a> + </li> + </ul> + <h4>Other News</h4> + <ul> + {% for post in site.posts -%} + {% unless post.categories contains 'release' -%} + <li {%- if page.title == post.title %} class="current" {%- endif %}> + <a href="{{ post.url | relative_url }}">{{ post.title }}</a> + </li> + {% endunless -%} + {% endfor -%} + </ul> + </aside> +</div> diff --git a/docs/_includes/news_contents_mobile.html b/docs/_includes/news_contents_mobile.html new file mode 100644 index 0000000..8497278 --- /dev/null +++ b/docs/_includes/news_contents_mobile.html @@ -0,0 +1,11 @@ +<div class="docs-nav-mobile unit whole show-on-mobiles"> + <select onchange="if (this.value) window.location.href=this.value"> + <option value="">Navigate the blog…</option> + <option value="{{ '/news/' | relative_url }}">Home</option> + <optgroup label="posts"> + {% for post in site.posts -%} + <option value="{{ post.url | relative_url }}">{{ post.title }}</option> + {% endfor -%} + </optgroup> + </select> +</div> diff --git a/docs/_includes/news_item.html b/docs/_includes/news_item.html new file mode 100644 index 0000000..51bc9cd --- /dev/null +++ b/docs/_includes/news_item.html @@ -0,0 +1,25 @@ +<article> + <h2> + <a href="{{ post.url | relative_url }}"> + {{- post.title -}} + </a> + </h2> + <span class="post-category"> + <span class="label"> + {{- post.categories | array_to_sentence_string -}} + </span> + </span> + <div class="post-meta"> + <span class="post-date"> + {{- post.date | date_to_string -}} + </span> + {% assign author = post.author -%} + <a href="https://github.com/{{ author }}" class="post-author"> + {% avatar user=author size=24 -%} + <span class="author-name">{{ author }}</span> + </a> + </div> + <div class="post-content"> + {{- post.content -}} + </div> +</article> diff --git a/docs/_includes/news_item_archive.html b/docs/_includes/news_item_archive.html new file mode 100644 index 0000000..bccf652 --- /dev/null +++ b/docs/_includes/news_item_archive.html @@ -0,0 +1,26 @@ +<article> + <div class="cell-left"> + <span class="post-category"> + <span class="label"> + {{- post.categories | array_to_sentence_string -}} + </span> + </span> + </div> + <div class="cell-right"> + <div class="post-meta"> + <h2 class="post-title"> + <a href="{{ post.url | relative_url }}"> + {{- post.title -}} + </a> + </h2> + <span class="post-date"> + {{- post.date | date_to_string -}} + </span> + {% assign author = post.author -%} + <a href="https://github.com/{{ author }}" class="post-author"> + {% avatar user=author size=24 -%} + <span class="author-name">{{ author }}</span> + </a> + </div> + </div> +</article> diff --git a/docs/_includes/primary-nav-items.html b/docs/_includes/primary-nav-items.html new file mode 100644 index 0000000..0635478 --- /dev/null +++ b/docs/_includes/primary-nav-items.html @@ -0,0 +1,11 @@ +<ul> + {% for p in site.data.primary_nav -%} + <li + {%- if p.link == '/' -%} + {% if page.url == p.link %} class="current" {%- endif -%} + {% else -%} + {% if page.url contains p.link %} class="current" {%- endif -%} + {% endif -%} + ><a href="{{ p.link | relative_url }}">{{ p.title }}</a></li> + {% endfor -%} +</ul> diff --git a/docs/_includes/search/input.html b/docs/_includes/search/input.html new file mode 100644 index 0000000..729f5ca --- /dev/null +++ b/docs/_includes/search/input.html @@ -0,0 +1 @@ +<input type="text" id="docsearch-input" placeholder="Search the docs…"> diff --git a/docs/_includes/search/script.html b/docs/_includes/search/script.html new file mode 100644 index 0000000..dca756d --- /dev/null +++ b/docs/_includes/search/script.html @@ -0,0 +1,9 @@ +<script src="https://cdn.jsdelivr.net/npm/docsearch.js@2/dist/cdn/docsearch.min.js"></script> +<script> docsearch({ +apiKey: '50fe39c839958dfad797000f33e2ec17', +indexName: 'jekyllrb', +inputSelector: '#docsearch-input', +enhancedSearchInput: true, +debug: false // Set debug to true if you want to inspect the dropdown +}); +</script> diff --git a/docs/_includes/section_nav_tutorials.html b/docs/_includes/section_nav_tutorials.html new file mode 100644 index 0000000..7a684b6 --- /dev/null +++ b/docs/_includes/section_nav_tutorials.html @@ -0,0 +1,39 @@ +{%- comment -%} +Map grabs the tutorials sections, giving us an array of arrays. Join, flattens all +the items to a comma delimited string. Split turns it into an array again. +{%- endcomment -%} +{%- assign tutorials = site.data.tutorials | map: 'tutorials' | join: ',' | split: ',' -%} + +{%- comment -%} +Because this is built for every page, lets find where we are in the ordered +document list by comparing url strings. Then if there's something previous or +next, lets build a link to it. +{%- endcomment -%} + +{% for tutorial in tutorials -%} + {% assign tutorial_url = tutorial | prepend:"/tutorials/" | append:"/" -%} + {% if tutorial_url == page.url -%} + <div class="section-nav"> + <div class="left align-right"> + {% if forloop.first -%} + <span class="prev disabled">Back</span> + {% else -%} + {% assign previous = forloop.index0 | minus: 1 -%} + {% assign previous_page = tutorials[previous] | prepend:"/tutorials/" | append:"/" -%} + <a href="{{ previous_page | relative_url }}" class="prev">Back</a> + {% endif -%} + </div> + <div class="right align-left"> + {% if forloop.last -%} + <span class="next disabled">Next</span> + {% else -%} + {% assign next = forloop.index0 | plus: 1 -%} + {% assign next_page = tutorials[next] | prepend:"/tutorials/" | append:"/" -%} + <a href="{{ next_page | relative_url }}" class="next">Next</a> + {% endif -%} + </div> + </div> + <div class="clear"></div> + {% break -%} + {% endif -%} +{% endfor -%} diff --git a/docs/_includes/step-index.html b/docs/_includes/step-index.html new file mode 100644 index 0000000..798fb7c --- /dev/null +++ b/docs/_includes/step-index.html @@ -0,0 +1,34 @@ +{% assign docs = site.docs | where_exp: "doc", "doc.url contains '/step-by-step/'" -%} + +{% for doc in docs -%} + {% if doc.url == page.url -%} + <div class="section-nav"> + <div class="left align-right"> + {% if forloop.first -%} + <span class="prev disabled">Back</span> + {% else -%} + {% assign previous = forloop.index0 | minus: 1 -%} + <a href="{{ docs[previous].url }}" class="prev">Back</a> + {% endif -%} + </div> + <div class="right align-left"> + {% if forloop.last -%} + <span class="next disabled">Next</span> + {% else -%} + {% assign next = forloop.index0 | plus: 1 -%} + <a href="{{ docs[next].url }}" class="next">Next</a> + {% endif -%} + </div> + </div> + <div class="clear"></div> + {% break -%} + {% endif -%} +{% endfor -%} + +<ol class="step-nav"> + {% for step in docs -%} + <li {%- if step.url == page.url %} class="current"{% endif %}><a href="{{ step.url }}"> + {{- step.title -}} + </a></li> + {% endfor -%} +</ol> diff --git a/docs/_includes/top.html b/docs/_includes/top.html new file mode 100644 index 0000000..c0e203c --- /dev/null +++ b/docs/_includes/top.html @@ -0,0 +1,19 @@ +<!DOCTYPE HTML> +<html lang="en-US"> +<head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width,initial-scale=1"> + {% feed_meta %} + <link type="application/atom+xml" rel="alternate" href="{{ 'feed/release.xml' | relative_url }}" title="Jekyll releases posts" /> + <link rel="alternate" type="application/atom+xml" title="Recent commits to Jekyll’s master branch" href="{{ site.repository }}/commits/master.atom"> + <link rel="preload" href="{{ 'fonts/lato-v14-latin-300.woff2' | relative_url }}" as="font" type="font/woff2" crossorigin /> + <link rel="preload" href="{{ 'fonts/lato-v14-latin-700.woff2' | relative_url }}" as="font" type="font/woff2" crossorigin /> + <link rel="preload" href="{{ 'css/screen.css' | relative_url }}" as="style"> + <link rel="stylesheet" href="{{ 'css/screen.css' | relative_url }}"> + <link rel="icon" type="image/x-icon" href="{{ 'favicon.ico' | relative_url }}"> + {% seo %} + <!--[if lt IE 9]> + <script src="/js/html5shiv.min.js"></script> + <script src="/js/respond.min.js"></script> + <![endif]--> +</head> diff --git a/docs/_includes/tutorials_contents.html b/docs/_includes/tutorials_contents.html new file mode 100644 index 0000000..76684ac --- /dev/null +++ b/docs/_includes/tutorials_contents.html @@ -0,0 +1,16 @@ +<div class="unit one-fifth hide-on-mobiles"> + <aside> + {% for section in site.data.tutorials -%} + <h4>{{ section.title }}</h4> + <ul> + {% for item in section.tutorials -%} + {% assign item_url = item | prepend:"/tutorials/" | append:"/" -%} + {% assign p = site.tutorials | where:"url", item_url | first -%} + <li {%- if item_url == page.url %} class="current" {%- endif %}><a href="{{ p.url | relative_url }}"> + {{- p.title -}} + </a></li> + {% endfor -%} + </ul> + {% endfor -%} + </aside> +</div> diff --git a/docs/_includes/tutorials_contents_mobile.html b/docs/_includes/tutorials_contents_mobile.html new file mode 100644 index 0000000..080788d --- /dev/null +++ b/docs/_includes/tutorials_contents_mobile.html @@ -0,0 +1,14 @@ +<div class="docs-nav-mobile unit whole show-on-mobiles"> + <select onchange="if (this.value) window.location.href=this.value"> + <option value="">Navigate the tutorials…</option> + {% for section in site.data.tutorials -%} + <optgroup label="{{ section.title }}"> + {% for item in section.tutorials -%} + {% assign item_url = item | prepend:"/tutorials/" | append:"/" -%} + {% assign tutorial = site.tutorials | where: "url", item_url | first -%} + <option value="{{ tutorial.url | relative_url }}">{{ tutorial.title }}</option> + {% endfor -%} + </optgroup> + {% endfor -%} + </select> +</div> diff --git a/docs/_layouts/default.html b/docs/_layouts/default.html new file mode 100644 index 0000000..9234c13 --- /dev/null +++ b/docs/_layouts/default.html @@ -0,0 +1,13 @@ +{%- include top.html -%} + +<body class="wrap"> + {%- include header.html -%} + + {{- content -}} + + {%- include footer.html -%} + {%- include anchor_links.html -%} + {%- include analytics.html -%} + {%- include search/script.html -%} +</body> +</html> diff --git a/docs/_layouts/docs.html b/docs/_layouts/docs.html new file mode 100644 index 0000000..f52e783 --- /dev/null +++ b/docs/_layouts/docs.html @@ -0,0 +1,21 @@ +--- +layout: default +--- + +<section class="docs"> + <div class="grid"> + {% include docs_contents_mobile.html -%} + <div class="unit four-fifths"> + <article> + <div class="improve right hide-on-mobiles"> + <a data-proofer-ignore href="https://github.com/jekyll/jekyll/edit/master/docs/{{ page.path }}"><i + class="fa fa-pencil"></i> Improve this page</a> + </div> + <h1>{{ page.title }}</h1> + {{ content }} + </article> + </div> + {% include docs_contents.html -%} + <div class="clear"></div> + </div> +</section> diff --git a/docs/_layouts/error.html b/docs/_layouts/error.html new file mode 100644 index 0000000..f5bc5c9 --- /dev/null +++ b/docs/_layouts/error.html @@ -0,0 +1,23 @@ +{% include top.html %} + +<body class="wrap"> + <header> + <div class="grid"> + <div class="unit whole align-center"> + <h1> + <a href="/"> + <span class="sr-only">Jekyll</span> + <img src="/img/logo-2x.png" width="249" height="115" alt="Jekyll Logo"> + </a> + </h1> + </div> + </div> + </header> + + {{ content }} + + {% include anchor_links.html %} + {% include analytics.html %} + +</body> +</html> diff --git a/docs/_layouts/news.html b/docs/_layouts/news.html new file mode 100644 index 0000000..7b776bc --- /dev/null +++ b/docs/_layouts/news.html @@ -0,0 +1,14 @@ +--- +layout: default +--- + +<section class="news"> + <div class="grid"> + {% include news_contents_mobile.html -%} + <div class="unit four-fifths"> + {{- content -}} + </div> + {% include news_contents.html -%} + <div class="clear"></div> + </div> +</section> diff --git a/docs/_layouts/news_item.html b/docs/_layouts/news_item.html new file mode 100644 index 0000000..5b0960e --- /dev/null +++ b/docs/_layouts/news_item.html @@ -0,0 +1,31 @@ +--- +layout: news +--- + +<article> + <h2> + {{ page.title }} + <a href="{{ page.url | relative_url }}" class="header-link" title="Permalink"> + <span class="sr-only">Permalink</span> + <i class="fa fa-link"></i> + </a> + </h2> + <span class="post-category"> + <span class="label"> + {{- page.categories | array_to_sentence_string -}} + </span> + </span> + <div class="post-meta"> + <span class="post-date"> + {{- page.date | date_to_string -}} + </span> + {% assign author = page.author -%} + <a href="https://github.com/{{ author }}" class="post-author"> + {% avatar user=author size=24 -%} + <span class="author-name">{{ author }}</span> + </a> + </div> + <div class="post-content"> + {{ content }} + </div> +</article> diff --git a/docs/_layouts/page.html b/docs/_layouts/page.html new file mode 100644 index 0000000..caa6186 --- /dev/null +++ b/docs/_layouts/page.html @@ -0,0 +1,20 @@ +--- +layout: default +--- + +<section class="standalone"> + <div class="grid"> + <div class="unit whole"> + <article> + {%- if page.permalink contains "resources" %} + <div class="improve right hide-on-mobiles"> + <a data-proofer-ignore href="https://github.com/jekyll/jekyll/edit/master/docs/{{ page.path }}"><i class="fa fa-pencil"></i> Improve this page</a> + </div> + {% endif -%} + <h1>{{ page.title }}</h1> + {{ content }} + </article> + </div> + <div class="clear"></div> + </div> +</section> diff --git a/docs/_layouts/step.html b/docs/_layouts/step.html new file mode 100644 index 0000000..3bf1101 --- /dev/null +++ b/docs/_layouts/step.html @@ -0,0 +1,21 @@ +--- +layout: default +--- +<section class="docs"> + <div class="grid"> + {% include docs_contents_mobile.html -%} + <div class="unit four-fifths"> + <article> + <div class="improve right hide-on-mobiles"> + <a data-proofer-ignore href="https://github.com/jekyll/jekyll/edit/master/docs/{{ page.path }}"><i class="fa fa-pencil"></i> Improve this page</a> + </div> + <h1>Step by Step Tutorial</h1> + <h2>{{ page.position }}. {{ page.title }}</h2> + {{ content }} + {% include step-index.html -%} + </article> + </div> + {% include docs_contents.html -%} + <div class="clear"></div> + </div> +</section> diff --git a/docs/_layouts/tutorials.html b/docs/_layouts/tutorials.html new file mode 100644 index 0000000..de2286d --- /dev/null +++ b/docs/_layouts/tutorials.html @@ -0,0 +1,43 @@ +--- +layout: default +--- + +<section class="docs"> + <div class="grid"> + {%- include tutorials_contents_mobile.html -%} + <div class="unit four-fifths"> + <article> + <header class="tutorial-header"> + <div class="improve right hide-on-mobiles"> + <a href="https://github.com/jekyll/jekyll/edit/master/docs/{{ page.path }}"><i + class="fa fa-pencil"></i> Improve this page</a> + </div> + <h1 class="tutorial-title">{{ page.title }}</h1> + {% assign author = page.author %} + {% if author %} + <div class="tutorial-meta"> + <span class="tutorial-date"> + {{ page.date | date_to_long_string }} + </span> + <a href="https://github.com/{{ author }}" class="tutorial-author"> + {% avatar user=author size=24 %} + <span class="author-name">{{ author }}</span> + </a> + </div> + {% endif %} + </header> + {{- content -}} + {% if page.plugin_disclaimer %} + <div class="disclaimer-ribbon"> + Disclaimer: The Jekyll Core Team does not endorse the Ruby code in this tutorial and is + not liable to resolve any bugs therein or issues arising otherwise from the use of the + provided example(s) in production builds. + </div> + {% endif %} + {%- include section_nav_tutorials.html -%} + </article> + </div> + {%- include tutorials_contents.html -%} + <div class="clear"></div> + </div> +</section> diff --git a/docs/_posts/2013-05-06-jekyll-1-0-0-released.markdown b/docs/_posts/2013-05-06-jekyll-1-0-0-released.markdown new file mode 100644 index 0000000..0b03b43 --- /dev/null +++ b/docs/_posts/2013-05-06-jekyll-1-0-0-released.markdown @@ -0,0 +1,22 @@ +--- +title: "Jekyll 1.0.0 Released" +date: "2013-05-06 02:12:52 +0200" +author: parkr +version: 1.0.0 +category: release +--- + +Hey! After many months of hard work by Jekyll's contributors, we're excited +to announce the first major release of the project in a long while. v1.0.0 is +finally here! While the list of improvements and bug fixes is [quite lengthy][history], +here are the highlights (thanks to [@benbalter](https://twitter.com/BenBalter) for the +examples and for compiling this list): + +- Support for the Gist tag for easily embedding Gists ([example](https://gist.github.com/benbalter/5555251)) +- Automatically generated post excerpts ([example](https://gist.github.com/benbalter/5555369)) +- Save and preview drafts before publishing ([example](https://gist.github.com/benbalter/5555992)) + +Take a look at the [Upgrading][] page in the docs for more detailed information. + +[history]: /docs/history/#v1-0-0 +[Upgrading]: /docs/upgrading/ diff --git a/docs/_posts/2013-05-08-jekyll-1-0-1-released.markdown b/docs/_posts/2013-05-08-jekyll-1-0-1-released.markdown new file mode 100644 index 0000000..a1f1c7e --- /dev/null +++ b/docs/_posts/2013-05-08-jekyll-1-0-1-released.markdown @@ -0,0 +1,26 @@ +--- +title: "Jekyll 1.0.1 Released" +date: "2013-05-08 23:46:11 +0200" +author: parkr +version: 1.0.1 +category: release +--- + +Hot on the trails of v1.0, v1.0.1 is out! Here are the highlights: + +* Add newer `language-` class name prefix to code blocks ([#1037][]) +* Commander error message now preferred over process abort with incorrect args ([#1040][]) +* Do not force use of toc_token when using generate_toc in RDiscount ([#1048][]) +* Make Redcarpet respect the pygments configuration option ([#1053][]) +* Fix the index build with LSI ([#1045][]) +* Don't print deprecation warning when no arguments are specified. ([#1041][]) +* Add missing `</div>` to site template used by `new` subcommand, fixed typos in code ([#1032][]) + +See the [History][] page for more information on this release. + +{% assign issue_numbers = "1037|1040|1048|1053|1045|1041|1032" | split: "|" %} +{% for issue in issue_numbers %} +[#{{ issue }}]: {{ site.repository }}/issues/{{ issue }} +{% endfor %} + +[History]: /docs/history/#v1-0-1 diff --git a/docs/_posts/2013-05-12-jekyll-1-0-2-released.markdown b/docs/_posts/2013-05-12-jekyll-1-0-2-released.markdown new file mode 100644 index 0000000..10dfa0f --- /dev/null +++ b/docs/_posts/2013-05-12-jekyll-1-0-2-released.markdown @@ -0,0 +1,27 @@ +--- +title: "Jekyll 1.0.2 Released" +date: "2013-05-12 14:45:00 +0200" +author: parkr +version: 1.0.2 +category: release +--- + +v1.0.2 has some key bugfixes that optionally restore some behaviour from pre-1.0 +releases, and fix some other annoying bugs: + +* Backwards-compatibilize relative permalinks ([#1081][]) +* Add `jekyll doctor` command to check site for any known compatibility problems ([#1081][]) +* Deprecate old config `server_port`, match to `port` if `port` isn't set ([#1084][]) +* Update pygments.rb and kramdown versions to 0.5.0 and 1.0.2, respectively ([#1061][], [#1067][]) +* Fix issue when post categories are numbers ([#1078][]) +* Add a `data-lang="<lang>"` attribute to Redcarpet code blocks ([#1066][]) +* Catching that Redcarpet gem isn't installed ([#1059][]) + +See the [History][] page for more information on this release. + +{% assign issue_numbers = "1059|1061|1066|1067|1078|1081|1084" | split: "|" %} +{% for issue in issue_numbers %} +[#{{ issue }}]: {{ site.repository }}/issues/{{ issue }} +{% endfor %} + +[History]: /docs/history/#v1-0-2 diff --git a/docs/_posts/2013-06-07-jekyll-1-0-3-released.markdown b/docs/_posts/2013-06-07-jekyll-1-0-3-released.markdown new file mode 100644 index 0000000..c81f1ce --- /dev/null +++ b/docs/_posts/2013-06-07-jekyll-1-0-3-released.markdown @@ -0,0 +1,24 @@ +--- +title: "Jekyll 1.0.3 Released" +date: "2013-06-07 21:02:13 +0200" +author: parkr +version: 1.0.3 +category: release +--- + +v1.0.3 contains some key enhancements and bug fixes: + +- Fail with non-zero exit code when MaRuKu errors ([#1190][]) or Liquid errors ([#1121][]) +- Add support for private gists to `gist` tag ([#1189][]) +- Add `--force` option to `jekyll new` ([#1115][]) +- Fix compatibility with `exclude` and `include` with pre-1.0 Jekyll ([#1114][]) +- Fix pagination issue regarding `File.basename` and `page:num` ([#1063][]) + +See the [History][] page for more information on this release. + +{% assign issue_numbers = "1190|1121|1189|1115|1114|1063" | split: "|" %} +{% for issue in issue_numbers %} +[#{{ issue }}]: {{ site.repository }}/issues/{{ issue }} +{% endfor %} + +[History]: /docs/history/#v1-0-3 diff --git a/docs/_posts/2013-07-14-jekyll-1-1-0-released.markdown b/docs/_posts/2013-07-14-jekyll-1-1-0-released.markdown new file mode 100644 index 0000000..8d8361a --- /dev/null +++ b/docs/_posts/2013-07-14-jekyll-1-1-0-released.markdown @@ -0,0 +1,26 @@ +--- +title: "Jekyll 1.1.0 Released" +date: "2013-07-14 19:38:02 +0200" +author: parkr +version: 1.1.0 +category: release +--- + +After a month of hard work, the Jekyll core team is excited to announce the release of +Jekyll v1.1.0! This latest release of Jekyll brings some really exciting new additions: + +- Add `docs` subcommand to read Jekyll's docs when offline. ([#1046][]) +- Support passing parameters to templates in `include` tag ([#1204][]) +- Add support for Liquid tags to post excerpts ([#1302][]) +- Fix pagination for subdirectories ([#1198][]) +- Provide better error reporting when generating sites ([#1253][]) +- Latest posts first in non-LSI `related_posts` ([#1271][]) + +See the [GitHub Release][] page for more a more detailed changelog for this release. + +{% assign issue_numbers = "1046|1204|1302|1198|1171|1118|1098|1215|1253|1271" | split: "|" %} +{% for issue in issue_numbers %} +[#{{ issue }}]: {{ site.repository }}/issues/{{ issue }} +{% endfor %} + +[GitHub Release]: {{ site.repository }}/releases/tag/v1.1.0 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 new file mode 100644 index 0000000..7256e08 --- /dev/null +++ b/docs/_posts/2013-07-24-jekyll-1-1-1-released.markdown @@ -0,0 +1,23 @@ +--- +title: "Jekyll 1.1.1 Released" +date: "2013-07-24 22:24:14 +0200" +author: parkr +version: 1.1.1 +category: 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)). + +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! + +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) + +See the [GitHub Release]({{ site.repository }}/releases/tag/v1.1.1) page for +more a more detailed changelog for this release. diff --git a/docs/_posts/2013-07-25-jekyll-1-0-4-released.markdown b/docs/_posts/2013-07-25-jekyll-1-0-4-released.markdown new file mode 100644 index 0000000..7823468 --- /dev/null +++ b/docs/_posts/2013-07-25-jekyll-1-0-4-released.markdown @@ -0,0 +1,19 @@ +--- +title: "Jekyll 1.0.4 Released" +date: "2013-07-25 09:08:38 +0200" +author: mattr- +version: 1.0.4 +category: release +--- + +Version 1.0.4 fixes a minor, but nonetheless important security vulnerability affecting several third-party Jekyll plugins. If your Jekyll site does not use plugins, you may, but are not required to upgrade at this time. + +Community and custom plugins extending the `Liquid::Drop` class may inadvertently disclose some system information such as directory structure or software configuration to users with access to the Liquid templating system. + +We recommend you upgrade to Jekyll v1.0.4 immediately if you use `Liquid::Drop` plugins on your Jekyll site. + +Many thanks for [Ben Balter](https://github.com/benbalter) for alerting us to the problem +and [submitting a patch][1349] so quickly. + +[230]: https://github.com/Shopify/liquid/pull/230 +[1349]: {{ site.repository }}/issues/1349 diff --git a/docs/_posts/2013-07-25-jekyll-1-1-2-released.markdown b/docs/_posts/2013-07-25-jekyll-1-1-2-released.markdown new file mode 100644 index 0000000..26c0345 --- /dev/null +++ b/docs/_posts/2013-07-25-jekyll-1-1-2-released.markdown @@ -0,0 +1,19 @@ +--- +title: "Jekyll 1.1.2 Released" +date: "2013-07-25 09:08:38 +0200" +author: parkr +version: 1.1.2 +category: release +--- + +Version 1.1.2 fixes a minor, but nonetheless important security vulnerability affecting several third-party Jekyll plugins. If your Jekyll site does not use plugins, you may, but are not required to upgrade at this time. + +Community and custom plugins extending the `Liquid::Drop` class may inadvertently disclose some system information such as directory structure or software configuration to users with access to the Liquid templating system. + +We recommend you upgrade to Jekyll v1.1.2 immediately if you use `Liquid::Drop` plugins on your Jekyll site. + +Many thanks for [Ben Balter](https://github.com/benbalter) for alerting us to the problem +and [submitting a patch][1349] so quickly. + +[230]: https://github.com/Shopify/liquid/pull/230 +[1349]: {{ site.repository }}/issues/1349 diff --git a/docs/_posts/2013-09-06-jekyll-1-2-0-released.markdown b/docs/_posts/2013-09-06-jekyll-1-2-0-released.markdown new file mode 100644 index 0000000..a227b4e --- /dev/null +++ b/docs/_posts/2013-09-06-jekyll-1-2-0-released.markdown @@ -0,0 +1,22 @@ +--- +title: "Jekyll 1.2.0 Released" +date: "2013-09-06 22:02:41 -0400" +author: parkr +version: 1.2.0 +category: release +--- + +After nearly a month and a half of hard work, the Jekyll team is happy to +announce the release of v1.2.0. It's chock full of bug fixes and some +enhancements that we think you'll love. + +Here are a few things we think you'll want to know about this release: + +* Run `jekyll serve --detach` to boot up a WEBrick server in the background. **Note:** you'll need to run `kill [server_pid]` to shut the server down. +* You can now **disable automatically-generated excerpts** if you set `excerpt_separator` to `""`. +* If you're moving around pages and post, you can now check for **URL conflicts** by running `jekyll doctor`. +* If you're a fan of the drafts feature, you'll be happy to know we've added `-D`, a shortened version of `--drafts`. +* Permalinks with special characters should now generate without errors. +* Expose the current Jekyll version as the `jekyll.version` Liquid variable. + +For a full run-down, visit our [change log](/docs/history/)! diff --git a/docs/_posts/2013-09-14-jekyll-1-2-1-released.markdown b/docs/_posts/2013-09-14-jekyll-1-2-1-released.markdown new file mode 100644 index 0000000..da2c74a --- /dev/null +++ b/docs/_posts/2013-09-14-jekyll-1-2-1-released.markdown @@ -0,0 +1,18 @@ +--- +title: 'Jekyll 1.2.1 Released' +date: 2013-09-14 20:46:50 -0400 +author: parkr +version: 1.2.1 +category: release +--- + +Quick turnover, anyone? A [recent incompatibility with Liquid +v2.5.2](https://github.com/jekyll/jekyll/pull/1525) produced a nasty bug in +which `include` tags were not rendered properly within `if` blocks. + +This release also includes a better handling of detached servers (prints pid and +the command for killing the process). **Note**: the `--detach` flag and +`--watch` flags are presently incompatible in 1.2.x. Fix for that coming soon! + +For a full list of the fixes in this release, check out [the change +log](/docs/history/)! diff --git a/docs/_posts/2013-10-28-jekyll-1-3-0-rc1-released.markdown b/docs/_posts/2013-10-28-jekyll-1-3-0-rc1-released.markdown new file mode 100644 index 0000000..c857699 --- /dev/null +++ b/docs/_posts/2013-10-28-jekyll-1-3-0-rc1-released.markdown @@ -0,0 +1,16 @@ +--- +title: 'Jekyll 1.3.0.rc1 Released' +date: 2013-10-28 20:14:39 -0500 +author: mattr- +version: 1.3.0.rc1 +category: release +--- + +Jekyll 1.3.0 is going to be a big release! In order to make sure we +didn't screw anything up too badly, we're making a release candidate +available for any early adopters who want to give the latest and +greatest code a spin without having to clone a repository from git. + +Please take this prerelease for a spin and [let us +know](https://github.com/jekyll/jekyll/issues/new) if you run into any +issues! diff --git a/docs/_posts/2013-11-04-jekyll-1-3-0-released.markdown b/docs/_posts/2013-11-04-jekyll-1-3-0-released.markdown new file mode 100644 index 0000000..14d1f08 --- /dev/null +++ b/docs/_posts/2013-11-04-jekyll-1-3-0-released.markdown @@ -0,0 +1,42 @@ +--- +title: 'Jekyll 1.3.0 Released' +date: 2013-11-04 21:46:02 -0600 +author: mattr- +version: 1.3.0 +category: release +--- + +It's been about six weeks since v1.2.0 and the Jekyll team is happy to +announce the arrival of v1.3.0. This is a **huge** release full of all +sorts of new features, bug fixes, and other things that you're sure to +love. + +Here are a few things we think you'll want to know about this release: + +* You can add [arbitrary data][] to the site by adding YAML files under a + site's `_data` directory. This will allow you to avoid + repetition in your templates and to set site specific options without + changing `_config.yml`. + +* You can now run `jekyll serve --detach` to boot up a WEBrick server in the + background. **Note:** you'll need to run `kill [server_pid]` to shut + the server down. When ran, you'll get a process id that you can use in + place of `[server_pid]` + +* You can now **disable automatically-generated excerpts** if you set + `excerpt_separator` to `""`. + +* If you're moving pages and posts, you can now check for **URL + conflicts** by running `jekyll doctor`. + +* If you're a fan of the drafts feature, you'll be happy to know we've + added `-D`, a shortened version of `--drafts`. + +* Permalinks with special characters should now generate without errors. + +* Expose the current Jekyll version as the `jekyll.version` Liquid + variable. + +For a full run-down, visit our [change log](/docs/history/)! + +[arbitrary data]: /docs/datafiles/ diff --git a/docs/_posts/2013-11-26-jekyll-1-3-1-released.markdown b/docs/_posts/2013-11-26-jekyll-1-3-1-released.markdown new file mode 100644 index 0000000..305a3b7 --- /dev/null +++ b/docs/_posts/2013-11-26-jekyll-1-3-1-released.markdown @@ -0,0 +1,20 @@ +--- +title: 'Jekyll 1.3.1 Released' +date: 2013-11-26 19:52:20 -0600 +author: mattr- +version: 1.3.1 +category: release +--- + +Just in time for the US holiday Thanksgiving, we're releasing version +1.3.1 of Jekyll to address some of the issues seen since the +release of 1.3.0. + +In addition to a couple of other smaller bug fixes, the biggest thing +we've fixed is an issue with the `--watch` option with Ruby 1.8.7. For a +full run-down, visit our [change log](/docs/history/)! + +Thanks to all the people who have contributed to this release! They are +(in alphabetical order): Abhi Yerra, Anatol Broder, Andreas Möller, Greg +Karékinian, Sam Rayner, Santeri Paavolainen, Shigeya Suzuki, Yihang Ho, +albertogg, andrewhavens, maul.esel, and thomasdao diff --git a/docs/_posts/2013-12-07-jekyll-1-4-0-released.markdown b/docs/_posts/2013-12-07-jekyll-1-4-0-released.markdown new file mode 100644 index 0000000..07aa719 --- /dev/null +++ b/docs/_posts/2013-12-07-jekyll-1-4-0-released.markdown @@ -0,0 +1,29 @@ +--- +title: 'Jekyll 1.4.0 Released' +date: 2013-12-07 13:55:28 -0600 +author: mattr- +version: 1.4.0 +category: release +--- + +About a month after the release of Jekyll v1.3.0, we are releasing +Jekyll v1.4.0. This release will be the last non-patch release to support Ruby +1.8.7 and our next release will be Jekyll 2.0.0. + +Here are a few things we think you'll want to know about this release: + +* TOML is now a supported markup language for config files. + +* Maruku has been updated to 0.7.0 which provides some new features and + a ton of bugfixes over the previous 0.6.x releases. + +* Non-`gem` Plugins are now sorted alphabetically by filename before they're + processed, which can provide a rudimentary way to establish a load order for + plugins. + +For a full run-down, visit our [change log](/docs/history/)! + +As always, Jekyll wouldn't be possible without the contributions from +others in the Jekyll community. We'd like to thank the following people +for contributing to this release: Anatol Broder, David Sawyer, Greg +Karékinian, Jordon Bedwell, Matthew Iversen, Persa Zula, and Yi Zeng. diff --git a/docs/_posts/2013-12-09-jekyll-1-4-1-released.markdown b/docs/_posts/2013-12-09-jekyll-1-4-1-released.markdown new file mode 100644 index 0000000..73a20a0 --- /dev/null +++ b/docs/_posts/2013-12-09-jekyll-1-4-1-released.markdown @@ -0,0 +1,17 @@ +--- +title: 'Jekyll 1.4.1 Released' +date: 2013-12-09 20:44:13 -0600 +author: mattr- +version: 1.4.1 +category: release +--- + +Another quick turnover, anyone? A [critical +bug]({{ site.repository }}/issues/1794) in the reading of +posts snuck itself into the 1.4.0 release. + +To address this issue, we're releasing v1.4.1 of Jekyll so that you can +keep on writing without any problems. + +As always, you can find the full list of fixes in this release in the +[change log](/docs/history/)! diff --git a/docs/_posts/2013-12-16-jekyll-1-4-2-released.markdown b/docs/_posts/2013-12-16-jekyll-1-4-2-released.markdown new file mode 100644 index 0000000..3ca96a5 --- /dev/null +++ b/docs/_posts/2013-12-16-jekyll-1-4-2-released.markdown @@ -0,0 +1,17 @@ +--- +title: 'Jekyll 1.4.2 Released' +date: 2013-12-16 19:48:13 -0500 +author: parkr +version: 1.4.2 +category: release +--- + +This release fixes [a regression][] where Maruku fenced code blocks were turned +off, instead of the previous default to on. We've added a new default +configuration to our `maruku` config key: `fenced_code_blocks` and set it to +default to `true`. + +If you do not wish to use Maruku fenced code blocks, you may turn this option +off in your site's configuration file. + +[a regression]: https://github.com/jekyll/jekyll/pull/1830 diff --git a/docs/_posts/2014-01-13-jekyll-1-4-3-released.markdown b/docs/_posts/2014-01-13-jekyll-1-4-3-released.markdown new file mode 100644 index 0000000..446c513 --- /dev/null +++ b/docs/_posts/2014-01-13-jekyll-1-4-3-released.markdown @@ -0,0 +1,25 @@ +--- +title: 'Jekyll 1.4.3 Released' +date: 2014-01-13 17:43:32 -0800 +author: benbalter +version: 1.4.3 +category: release +--- + +Jekyll 1.4.3 contains two **critical** security fixes. If you run Jekyll locally +and do not run Jekyll in "safe" mode (e.g. you do not build Jekyll sites on behalf +of others), you are not affected and are not required to update at this time. +([See pull request.]({{ site.repository }}/pull/1944)) + +Versions of Jekyll prior to 1.4.3 and greater than 1.2.0 may allow malicious +users to expose the content of files outside the source directory in the +generated output via improper symlink sanitization, potentially resulting in an +inadvertent information disclosure. + +Versions of Jekyll prior to 1.4.3 may also allow malicious users to write +arbitrary `.html` files outside of the destination folder via relative path +traversal, potentially overwriting otherwise-trusted content with arbitrary HTML +or Javascript depending on your server's configuration. + +*Maintainer's note: Many thanks to @gregose and @charliesome for discovering +these vulnerabilities, and to @BenBalter and @alindeman for writing the patch.* diff --git a/docs/_posts/2014-03-24-jekyll-1-5-0-released.markdown b/docs/_posts/2014-03-24-jekyll-1-5-0-released.markdown new file mode 100644 index 0000000..7192d8b --- /dev/null +++ b/docs/_posts/2014-03-24-jekyll-1-5-0-released.markdown @@ -0,0 +1,18 @@ +--- +title: 'Jekyll 1.5.0 Released' +date: 2014-03-24 20:37:59 -0400 +author: parkr +version: 1.5.0 +category: release +--- + +As work continues on Jekyll 2.0.0, we felt it was important to address two key +issues of Jekyll 1.4.3, namely the `safe_yaml` dependency below 1.0 and the +inability to use Jekyll 1.4.3 on Windows due to a [fun issue with path sanitizing][]. + +For a full changelog, check out our [history][] page. + +Now, back to work on 2.0.0! + +[fun issue with path sanitizing]: https://github.com/jekyll/jekyll/issues/1948 +[history]: /docs/history/#v1-5-0 diff --git a/docs/_posts/2014-03-27-jekyll-1-5-1-released.markdown b/docs/_posts/2014-03-27-jekyll-1-5-1-released.markdown new file mode 100644 index 0000000..71afc80 --- /dev/null +++ b/docs/_posts/2014-03-27-jekyll-1-5-1-released.markdown @@ -0,0 +1,25 @@ +--- +title: 'Jekyll 1.5.1 Released' +date: 2014-03-27 22:43:48 -0400 +author: parkr +version: 1.5.1 +category: release +--- + +The hawk-eyed [@gregose](https://github.com/gregose) spotted a bug in our +`Jekyll.sanitized_path` code: + +{% highlight ruby %} +> sanitized_path("/tmp/foobar/jail", "..c:/..c:/..c:/etc/passwd") +=> "/tmp/foobar/jail/../../../etc/passwd" +{% endhighlight %} + +Well, we can't have that! In 1.5.1, you'll instead see: + +{% highlight ruby %} +> sanitized_path("/tmp/foobar/jail", "..c:/..c:/..c:/etc/passwd") +=> "/tmp/foobar/jail/..c:/..c:/..c:/etc/passwd" +{% endhighlight %} + +Luckily not affecting 1.4.x, this fix will make 1.5.0 that much safer for +the masses. Thanks, Greg! diff --git a/docs/_posts/2014-05-06-jekyll-turns-2-0-0.markdown b/docs/_posts/2014-05-06-jekyll-turns-2-0-0.markdown new file mode 100644 index 0000000..f2064d7 --- /dev/null +++ b/docs/_posts/2014-05-06-jekyll-turns-2-0-0.markdown @@ -0,0 +1,30 @@ +--- +title: 'Jekyll turns 2.0.0' +author: parkr +version: 2.0.0 +category: release +--- + +A year ago to the day, [we released Jekyll 1.0.0][jekyll-1]. One year later, we present to you the next major version: Jekyll 2.0.0. + +Jam-packed with some [highly-requested features and bugfixes galore][changelog], this is the best Jekyll yet. Some notable changes: + +1. [Collections](/docs/collections/) - Collections allow you to define an unlimited number of custom document types (beyond just posts and pages) for different types of content you may want to author in Jekyll such as API documentation or a cookbook! +2. [Brand new site template](https://github.com/jekyll/jekyll/pull/2050#issuecomment-35938016) (thanks [@jglovier][]!) - Getting started with Jekyll just got a lot easier and a lot more beautiful. Just run `jekyll new <path>` and you're good to go. +3. [Native Sass & CoffeeScript support](/docs/assets/) - We love CSS and JavaScript as much as the next guy, but there will always be a special place in our hearts for Sass and CoffeeScript. We now offer native support for these file types — no more messing around with Rake or Grunt! +4. [Front Matter defaults](/docs/configuration/front-matter-defaults/) - If you've set `layout: post` more than once in your life, you'll love this new feature: set front matter defaults for a given directory or type. +5. [Custom markdown processors](/docs/configuration/markdown/) - Always wanted to use your favourite home-grown Markdown converter, but couldn't with Jekyll? Now you can. Simply specify `markdown: MyConverterClass` and you're on your way. +6. [Addition of `where` and `group_by` Liquid filters](/docs/liquid/filters/) - Simplifying your Liquid templates one filter at a time. The `where` filter selects from an array all items within which have a given value for a property. The `group_by` filter groups all items in an array which have the same value for a given property. +7. [Switch from Maruku to Kramdown as default markdown converter](https://github.com/jekyll/jekyll/pull/1988) - Maruku is dead. We've replaced it with the converter which has the closest feature parity: Kramdown! + +Check out our [changelog][] for a complete list of all (200+) changes. + +Many thanks to these 183 contributors for making Jekyll 2.0.0 happen: + +Parker Moore, Matt Rogers, maul.esel, Anatol Broder, Zach Gersh, Joel Glovier, Ben Balter, XhmikosR, Coby Chapple, John Piasetzki, Aidan Feldman, Robin Dupret, Pascal Borreli, Troy Swanson, Erik Michaels-Ober, albertogg, Lucas Jenss, Matt Rogers & Persa Zula, Eric Mill, Shigeya Suzuki, Jens Nazarenus, ddavison, Pat Hawks, Rob Wierzbowski, MURAOKA Taro, Casey Lang, Fabian Rodriguez, Greg Karékinian, Zlatan Vasović, Christopher Nicotera, Dmitry Chestnykh, Ryan Morrissey, Jordon, John Hughes, akira yamada, Matt Swanson, Jashank Jeremy, Matthew Iversen, Meeka, liufengyun, Anand Narayan, nitoyon, Geoff Shannon, Benjamin J. Balter, Juan Ignacio Donoso, David Briggs, Benjamin Esham, Slava Pavlutin, Assaf Gelber, Josh Brown, Nick Fagerlund, Davide Ficano, pilosus, Anthony Smith, André Arko, Mikael Konutgan, Matthew Scharley, Dan Tao, scribu, Mort Yao, m, Stephen McDonald, Marcus Stollsteimer, Thomas Torsney-Weir, Jordon Bedwell, Tom Preston-Werner, Lincoln Mullen, Philip Poots, Ivan Tse, Christopher Giroir, Valery Tolstov, Wlodek Bzyl, Xavier Noria, Yi Zeng, Persa Zula, Phil Leggetter, Pirogov Evgenij, Rafael Revi, Rob McGuire-Dale, Rob Muhlestein, Robin Mehner, Roland Warmerdam, Rusty Geldmacher, Sam Rayner, Santeri Paavolainen, Sebastian Morr, Stephan Groß, Steven Spasbo, Tobias Brunner, Tuomas Kareinen, Tyler Margison, Uwe Dauernheim, Yihang Ho, Zach Leatherman, Zequez, andrew morton, andrewhavens, imathis, jannypie, jaybe@jekyll, kk_Ataka, markets, redwallhp, schneems, szymzet, thomasdao, tomsugden, wǒis神仙, 张君君, Noah Slater, Abhi Yerra, Adam Heckler, Ahmed Hazem, Aigars Dzerviniks, Aleksey V. Zapparov, Andreas Möller, Andy Lindeman, Arlen Cuss, Aziz Shamim, Ben Baker-Smith, Ben Hanzl, Ben Hildred, Brian Kim, Brice, Carol Nichols, Chezou, Chris Jones, Christian Grobmeier, Christoph Hochstrasser, Christoph Schiessl, Clint Shryock, Colin Dean, Corey Ward, Damian Lettie, Daniel Schauenberg, David Ensinger, David Paschich, David Sawyer, David Silva Smith, Donald Perry, Doug Johnston, Edward Ball, Eric Dobson, Erik Dungan, Florent Guilleux, Francis, Frederic ROS, GSI2013, Garen Torikian, George Anderson, Giuseppe Capizzi, Ishibashi Hideto, Jarrod Birch, Jeff Kolesky, Jens Bissinger, Jens Krause, John Firebaugh, John Papandriopoulos, Josh Branchaud, Katy DeCorah, Lachlan Holden, Mark Prins, Markus Roth, Martin Charles, Matt Iversen, Matt Sheehan, Matt Swensen, Matthias Vogelgesang, Michael Parker, Miha Rekar, Nathan Youngman, Nick Quaranto, Nick Quinlan, Nick Schonning, Nicolas Alpi, Nicolás Reynolds, Nikkau, 4ensicLog, Octavian Damiean, Olov Lassus, PatrickC8t, Paul Annesley, and Paul Oppenheim. + +Happy developing! + +[changelog]: /docs/history/ +[@jglovier]: https://github.com/jglovier +[jekyll-1]: {% post_url 2013-05-06-jekyll-1-0-0-released %} diff --git a/docs/_posts/2014-05-08-jekyll-2-0-3-released.markdown b/docs/_posts/2014-05-08-jekyll-2-0-3-released.markdown new file mode 100644 index 0000000..d693c51 --- /dev/null +++ b/docs/_posts/2014-05-08-jekyll-2-0-3-released.markdown @@ -0,0 +1,17 @@ +--- +title: 'Jekyll 2.0.3 Released' +date: 2014-05-08 22:43:17 -0400 +author: parkr +version: 2.0.3 +category: release +--- + +Hey again! Just wanted to let you know we've released another version of Jekyll, jam-packed with bug fixes. + +A huge "thank you" is in order for all the folks who have submitted bug reports over the last 2 days — your input is what allows this project to continue. It's always a pain to deal with a MAJOR version bump release, but it's been pretty smooth so far and you have all been nice about the flaws you've found in the tool. Keep filing those reports so we can continue to make Jekyll even better! + +Thank you to the contributors that contributed code to 2.0.1, 2.0.2, and/or 2.0.3: + +Parker Moore, Yi Zeng, Gabe Ortiz, Aaron Broder, Alberto Grespan, gpxl, David Briggs, Kevin Ingersoll, and Troy Swanson. + +As always, check out the [changelog](/docs/history/) for more info. Happy Jekylling! diff --git a/docs/_posts/2014-06-04-jekyll-stickers-1-dollar-stickermule.markdown b/docs/_posts/2014-06-04-jekyll-stickers-1-dollar-stickermule.markdown new file mode 100644 index 0000000..6b9989c --- /dev/null +++ b/docs/_posts/2014-06-04-jekyll-stickers-1-dollar-stickermule.markdown @@ -0,0 +1,18 @@ +--- +title: 'Pick Up your $1 Jekyll Sticker' +date: 2014-06-04 15:46:53 -0400 +author: parkr +categories: [partners] +--- + + + +You may have heard that [@cobyism](https://github.com/cobyism)'s excellent +Jekyll logo has been made into a sticker. You may have sat idly by, wishing +that you could have a sticker honoring your beloved Jekyll. + +The StickerMule team says, *"Pine no longer!"* StickerMule has **[discounted the +price of Jekyll stickers down to $1 and are offering free (domestic) +shipping](https://www.stickermule.com/marketplace/825-jekyll-stickers)!** +Go grab one now on the StickerMule marketplace – they'll look +swell on your favourite hardware. diff --git a/docs/_posts/2014-06-28-jekyll-turns-21-i-mean-2-1-0.markdown b/docs/_posts/2014-06-28-jekyll-turns-21-i-mean-2-1-0.markdown new file mode 100644 index 0000000..7c9c2bb --- /dev/null +++ b/docs/_posts/2014-06-28-jekyll-turns-21-i-mean-2-1-0.markdown @@ -0,0 +1,30 @@ +--- +title: 'Jekyll Turns 21! Err... I mean 2.1.0.' +date: 2014-06-28 17:26:59 -0400 +author: parkr +version: 2.1.0 +category: release +--- + +Jekyll's finally [legal to drink in the States](https://en.wikipedia.org/wiki/Legal_drinking_age). +And he's done a lot of learning in the process! Here are some of the new +things to look forward to: + +- Uses the latest Liquid version (2.6.1) (#2495) +- Set front matter defaults for collections (#2419) +- Set a collection-specific URL template (#2418) +- `pygments.rb` 0.6.0! (#2504) +- `.json` files in `_data` (#2369) +- Allow subdirectories in `_data` (#2395) +- Add support for `hl_lines` in `highlight` tag (#2532) +- Post categories now merge with directory, front matter, and defaults (#2373) +- New `--skip_initial_build` flag for `jekyll serve` (#2477) +- A bajillion bug fixes and site updates! + +Let's go party! + +*Check out the [full changelog](/docs/history/#v2-1-0) for more.* + +Many thanks to these 37 contributors for the 2.1.0 release: + +Alberto Grespan, Alessandro Lorenzi, Alex Medearis, Alfred Xing, Anatol Broder, Ben, Ben Balter, Bud Parr, Chezou, Denilson Figueiredo de Sá, Denilson Sá, Ivan Tse, Jens Nazarenus, Jesse Shawl, Jordon Bedwell, Josh Davis, János Rusiczki, Marc Ransome, Mathieu Bruyen, Matt Rogers, Parker Moore, Pat Hawks, Paul Henry, Peter Rhoades, Philipp Rudloff, Quinn Shanahan, Renaud Martinet, Rob Murray, Rodrigo Dumont, Simon Sarris, Terry, Terry Schmidt, Tomer Cohen, XhmikosR, Yihang Ho, jaybe@jekyll, and mikecole. diff --git a/docs/_posts/2014-07-01-jekyll-2-1-1-released.markdown b/docs/_posts/2014-07-01-jekyll-2-1-1-released.markdown new file mode 100644 index 0000000..c1a0501 --- /dev/null +++ b/docs/_posts/2014-07-01-jekyll-2-1-1-released.markdown @@ -0,0 +1,29 @@ +--- +title: 'Jekyll 2.1.1 Released' +date: 2014-07-01 20:16:43 -0400 +author: parkr +version: 2.1.1 +category: release +--- + +This is a minor release for Jekyll 2.1.0. It fixes a couple bugs and +introduces fixes for a couple security-related issues. + +It covers two security vulnerabilities: + +1. One in the reading of data +2. One in the `layouts` setting + +They were identified in Jekyll 1.5.1 and has been confirmed as patched +in this version and the version used by GitHub Pages. If you are in the +business of building Jekyll sites, please ensure you upgrade to 2.1.1 as +soon as possible. + +For more, check out [`jekyll/jekyll#2563`](https://github.com/jekyll/jekyll/pull/2563). + +Additionally, the dependency on Maruku has been loosened and a bug was +fixed with document URLs. + +As always, check out the [full changelog](/docs/history/) for more info! + +Happy Jekylling! diff --git a/docs/_posts/2014-07-29-jekyll-2-2-0-released.markdown b/docs/_posts/2014-07-29-jekyll-2-2-0-released.markdown new file mode 100644 index 0000000..ce8e672 --- /dev/null +++ b/docs/_posts/2014-07-29-jekyll-2-2-0-released.markdown @@ -0,0 +1,18 @@ +--- +title: 'Jekyll 2.2.0 Released' +date: 2014-07-29 18:59:13 -0400 +author: parkr +version: 2.2.0 +category: release +--- + +Jekyll 2.2.0 contains a few key updates: + +1. A warning will now fire if you specify a layout in any of your pages or + posts that doesn't exist. +2. Certain Pygments options are now whitelisted in safe mode +3. Categories in a post's path are now respected (i.e. folders in `_posts` + will now work properly). + +As always, a full list of the updates are on the +[history page](/docs/history/#v2-2-0). Happy Jekylling! diff --git a/docs/_posts/2014-08-10-jekyll-2-3-0-released.markdown b/docs/_posts/2014-08-10-jekyll-2-3-0-released.markdown new file mode 100644 index 0000000..c40f850 --- /dev/null +++ b/docs/_posts/2014-08-10-jekyll-2-3-0-released.markdown @@ -0,0 +1,40 @@ +--- +title: 'Jekyll 2.3.0 Released' +date: 2014-08-10 20:38:34 -0400 +author: parkr +version: 2.3.0 +category: release +--- + +This latest release of Jekyll includes a slew of enhancements and bug +fixes. Some of the highlights: + +* Strange bug around spacing/indentation should be resolved. [It was a + curious bug indeed.](https://github.com/jekyll/jekyll/issues/2676) +* Pages, Posts, and Drafts can now be converted by multiple converters. +* Static files can now be safely included in collections. They'll be placed + in a `collection.files` array. `collection.docs` still holds exclusively + content with front matter. +* Sass files can once again be rendered by Liquid. However, neither Sass + nor CoffeeScript can ever have a layout. Bonus: `scssify` and `sassify` + Liquid filters. +* Partial variables allowed now in the path argument of `include` calls +* We added a `jekyll help` command. Pass it a subcommand to see more info + about that subcommand. Or don't, to see the help for `jekyll` itself. +* Lots of fixes to the site template we use for `jekyll new`, including + converting the CSS into SCSS. +* The `jsonify` filter will now call `#to_liquid` for you +* Lots, lots more! + +One change deserves special note. In [#2633][], subfolders *inside* a +`_posts` folder were processed and added as categories to the posts. It +turns out, this behaviour was unwanted by a large number of individuals, as +it is a handy way to organize posts. Ultimately, we decided to revert this +change in [#2705][], because it was a change in behaviour that was already +well-established (at least since Jekyll v0.7.0), and was convenient. + +[#2633]: {{ site.repository }}/issues/2633 +[#2705]: {{ site.repository }}/issues/2705 + +For more excellent CHANGELOG reading material, check out the [History +page](/docs/history/)! Happy Jekylling! diff --git a/docs/_posts/2014-09-09-jekyll-2-4-0-released.markdown b/docs/_posts/2014-09-09-jekyll-2-4-0-released.markdown new file mode 100644 index 0000000..4bcb3a7 --- /dev/null +++ b/docs/_posts/2014-09-09-jekyll-2-4-0-released.markdown @@ -0,0 +1,24 @@ +--- +title: 'A Wild Jekyll 2.4.0 Appeared!' +date: 2014-09-09 21:10:33 -0700 +author: parkr +version: 2.4.0 +category: release +--- + +Well, lookie here! A new release of Jekyll! v2.4.0 contains lots of goodies, including some brilliant new additions: + +- A new `relative_include` Liquid tag ([#2870]({{ site.repository }}/issues/2870)) +- Render Liquid in CoffeeScript files ([#2830]({{ site.repository }}/issues/2830)) +- Add 4 new array Liquid filters: `push`, `pop`, `shift`, and `unshift` ([#2895]({{ site.repository }}/pull/2895)) +- Auto-enable watch on 'serve' ([#2858]({{ site.repository }}/issues/2858)). No more `-w`! +- Add `:title` and `:name` to collection URL template fillers ([#2864]({{ site.repository }}/issues/2864) & [#2799]({{ site.repository }}/issues/2799)) +- Add support for CSV files in the `_data` directory ([#2761]({{ site.repository }}/issues/2761)) +- Add `inspect` liquid filter ([#2867]({{ site.repository }}/issues/2867)) +- Add a `slugify` Liquid filter ([#2880]({{ site.repository }}/issues/2880)) + +Some other wunderbar bug fixes in there as well. Check out the [full changelog](/docs/history/) for the whole scoop. + +As always, many thanks to our amazing contributors who made this release possible: Chris Frederick, Garen Torikian, James Smith, Ruslan Korolev, Joel Glovier, Michael Kühnel, Minn Soe, Pat Hawks, Peter deHaan, Shu Uesugi, TJ, Zhuochun, Alfred Xing, nitoyon, Anatol Broder, Faruk AYDIN, Frederic Hemberger, and Gordon Gao. Thank you!! + +Happy Jekylling! diff --git a/docs/_posts/2014-11-06-jekylls-midlife-crisis-jekyll-turns-2-5-0.markdown b/docs/_posts/2014-11-06-jekylls-midlife-crisis-jekyll-turns-2-5-0.markdown new file mode 100644 index 0000000..45056f9 --- /dev/null +++ b/docs/_posts/2014-11-06-jekylls-midlife-crisis-jekyll-turns-2-5-0.markdown @@ -0,0 +1,46 @@ +--- +title: "Jekyll's Mid-Life Crisis (Or, Jekyll turns 2.5.0)" +date: 2014-11-05 10:48:22 -0800 +author: parkr +version: 2.5.0 +category: release +--- + +A new day, a new release! Jekyll just turned 2.5.0 and has gained a lot of +wisdom along the way. This 2.5.0 release also comes just a few weeks after +Jekyll turned 6 years old! In fashion, we're celebrating this huge +milestone with a pretty big release. What's changed in 2.5.0? Here are some +highlights: + +* Require plugins in the `:jekyll_plugins` Gemfile group (turned off with an environment variable) +* Front matter permalinks can now contain placeholders like `:name`. Check out all the placeholders on the [Permalinks docs page](/docs/permalinks/). +* The `jsonify` filter now deep-converts arrays to liquid. +* Shorted `build` and `serve` commands with `b` and `s` aliases, respectively +* WEBrick will now list your directory if it can't find an index file. +* Any enumerable can be used with the `where` filter. +* Performance optimizations thanks to @tmm1's [stackprof](https://github.com/tmm1/stackprof) +* Fix for Rouge's Redcarpet interface +* Security auditors will love this: path sanitation has now been centralized. +* Specify a log level with `JEKYLL_LOG_LEVEL`: debug, info, warn, or error. + +...and a whole bunch of other fixes and enhancements you can read more +about in [the changelog!](/docs/history/) + +As always, if you run into issues, please [check the issues]({{ site.repository }}/issues) +and [create an issue if one doesn't exist for the bug you encountered]({{ site.repository }}/issues/new). +If you just need some help, the extraordinary jekyll help team is here for +you! + +*When was the [first commit to Jekyll](https://github.com/jekyll/jekyll/commit/d189e05d236769c1e5594af9db4d6eacb86fc16e)? +All the way back on October 19, 2008. It features interesting historical +tidbits, such as the old name for Jekyll was "autoblog", and was first +released via Rubyforge. What a difference 6 years has made!* + +Thanks to the following contributors for making this release possible: + +Parker Moore, XhmikosR, Alfred Xing, Ruslan Korolev, Pat Hawks, +chrisfinazzo, Mike Kruk, Tanguy Krotoff, Matt Hickford, Philipp Rudloff, +Rob Murray, Sean Collins, Seth Warburton, Tom Thorogood, Vasily Vasinov, +Veres Lajos, feivel, mitaa, nitoyon, snrbrnjna, tmthrgd, Bret Comnes, +Charles Baynham, Christian Mayer, Dan Croak, Frederic Hemberger, Glauco +Custódio, Igor Kapkov, and Kevin Ndung'u! diff --git a/docs/_posts/2014-11-08-jekyll-2-5-1-released.markdown b/docs/_posts/2014-11-08-jekyll-2-5-1-released.markdown new file mode 100644 index 0000000..6d08142 --- /dev/null +++ b/docs/_posts/2014-11-08-jekyll-2-5-1-released.markdown @@ -0,0 +1,28 @@ +--- +title: 'Jekyll 2.5.1 Released' +date: 2014-11-09 09:47:52 -0800 +author: parkr +version: 2.5.1 +category: release +--- + +Hot on the heels of v2.5.0, this release brings relief to our Windows +users. It includes a fix for a 2.5.0 path sanitation change that has been +confirmed to work on Windows. + +To our Windows users: while we don't officially support Windows, we don't +wish to impede your normal use of Jekyll at all. Our lack of full support +for Windows is due to our lack of a Windows machine for development testing +(no one on the core team has a Windows machine upon which to test new +release candidates), not due to any malice or willful oversight. If you +come to us with an issue, we are more than happy to work through it with +you to come to a solution that works for all platforms. Along those lines, +we have created a [**Windows Test Force**][] (WTF) which is a group of Jekyll +users dedicated to making sure all future releases work on Windows *before* +they're released so we don't have this issue again. A special thanks goes +out to the initial WTF team members, XhmikosR, Julian Thilo, Pedro Rogério, +and Alfred Xing. + +Happy Jekylling! + +[**Windows Test Force**]: https://github.com/jekyll/jekyll/issues/3069 diff --git a/docs/_posts/2014-11-12-jekyll-2-5-2-released.markdown b/docs/_posts/2014-11-12-jekyll-2-5-2-released.markdown new file mode 100644 index 0000000..bd3d5ee --- /dev/null +++ b/docs/_posts/2014-11-12-jekyll-2-5-2-released.markdown @@ -0,0 +1,17 @@ +--- +title: 'Jekyll 2.5.2 Released' +date: 2014-11-12 18:49:08 -0800 +author: parkr +version: 2.5.2 +category: release +--- + +A very minor release, 2.5.2 fixes a bug with path sanitation that 2.5.1 +introduced. It also improves the `post_url` tag such that it checks the +posts' name (e.g. `2014-03-03-my-cool-post`) instead of a compiled time and +name. This fixes issues where posts are created and the day changes based +on timezone discrepancies. + +[Full history here.](/docs/history/) + +Happy Jekylling! diff --git a/docs/_posts/2014-12-17-alfredxing-welcome-to-jekyll-core.md b/docs/_posts/2014-12-17-alfredxing-welcome-to-jekyll-core.md new file mode 100644 index 0000000..51f5502 --- /dev/null +++ b/docs/_posts/2014-12-17-alfredxing-welcome-to-jekyll-core.md @@ -0,0 +1,26 @@ +--- +title: 'Alfred Xing has joined the Jekyll core team' +date: 2014-12-17 11:16:21 -0800 +author: parkr +version: alfredxing +categories: [team] +--- + +We're excited to announce that [@alfredxing][] has joined the @jekyll/core +team! + +He hails from Vancouver, BC, Canada, where he is studying Economics and +Computer Science at the [University of British Columbia][]. Alfred popped up in +the issues a few months ago with terrific insights, focus, and humility. +Performance buffs may be pleased to hear incremental regeneration will be +released in a future version of Jekyll -- a significant piece of the +feature written by Alfred. + +Please join me in welcoming Alfred to the Jekyll core team. We're excited +he's agreed to lend his talents to this project. The future is an exciting +place! + +Happy Jekylling! + +[@alfredxing]: https://github.com/alfredxing +[University of British Columbia]: http://www.ubc.ca diff --git a/docs/_posts/2014-12-22-jekyll-2-5-3-released.markdown b/docs/_posts/2014-12-22-jekyll-2-5-3-released.markdown new file mode 100644 index 0000000..00ddca6 --- /dev/null +++ b/docs/_posts/2014-12-22-jekyll-2-5-3-released.markdown @@ -0,0 +1,19 @@ +--- +title: 'Jekyll Release for the Holidays! v2.5.3 Out' +date: 2014-12-22 09:03:30 -0500 +author: parkr +version: 2.5.3 +category: release +--- + +Happy Holidays, everyone. + +Jekyll v2.5.3 is a quick patch release, containing some minor fixes. See the +[full history](/docs/history/) for more info. If you notice any problems, +please [let us know]({{ site.repository }}). + +This release also marks the start of Jekyll 3 development. I wrote about it +over on my personal blog: [Jekyll 3 — The Road Ahead](https://byparker.com/blog/2014/jekyll-3-the-road-ahead/). +Feel free to chime in over on GitHub. + +Happy Jekylling! diff --git a/docs/_posts/2015-01-20-jekyll-meet-and-greet.markdown b/docs/_posts/2015-01-20-jekyll-meet-and-greet.markdown new file mode 100644 index 0000000..eee8d5e --- /dev/null +++ b/docs/_posts/2015-01-20-jekyll-meet-and-greet.markdown @@ -0,0 +1,19 @@ +--- +title: "Jekyll Meet & Greet at GitHub HQ" +date: "2015-01-20 19:23:12 -0800" +author: parkr +categories: [meetup] +--- + +Hey! Our friends at GitHub have agreed to host a Jekyll meet & greet on +**February 5, 2015 at 7pm**. The event will be hosted at +[GitHub's Headquarters](https://goo.gl/maps/Bmy7i) +here in San Francisco, CA. Pizza & beer will be available for those interested, +and there will be much time to sit and chat about all things Jekyll. This would +be an especially good time to get help with bugs you've encountered or to talk +over a potential feature with the core team in attendance. + +A special thanks to [@gjtorikian](https://github.com/gjtorikian) for making this +all possible! You rock. + +We look forward to meeting all you fine folks. Cheers! diff --git a/docs/_posts/2015-01-24-jekyll-3-0-0-beta1-released.markdown b/docs/_posts/2015-01-24-jekyll-3-0-0-beta1-released.markdown new file mode 100644 index 0000000..7612876 --- /dev/null +++ b/docs/_posts/2015-01-24-jekyll-3-0-0-beta1-released.markdown @@ -0,0 +1,39 @@ +--- +title: 'Jekyll 3.0.0.beta1 Released' +date: 2015-01-24 00:42:31 -0800 +author: parkr +version: 3.0.0.beta1 +category: release +--- + +Hey! + +Exciting news! First beta for Jekyll 3 is out. Check out the [sizable +changelog](https://github.com/jekyll/jekyll/blob/v3.0.0.beta1/History.markdown#head) +to get a feel for what changes are afoot. Key features: + +1. **Speed.** Jekyll now features incremental regeneration and greatly + improved problematic code that caused slow-downs. +2. Gobs of bugfixes and customization. +3. Uniformity and sanity to Jekyll extensions of Liquid. + +To install just run: + +{% highlight shell %} +$ gem install jekyll --pre +{% endhighlight %} + +Future versions will include [some awesome new +features](https://github.com/jekyll/jekyll/issues/3324) that we haven't +built yet. If you see one you want to tackle, submit a PR & you'll be +featured in the Jekyll 3.0 release post as a contributor to that epic +release. + +Please file bugs as you encounter them, being sure to include your version +of Ruby, the Jekyll version, and (if possible) a link to your site so we +can reproduce. + +If you think there's room for improvement in the UX, also do let us know. +We're always looking to make Jekyll easier to use! + +Happy Jekylling! diff --git a/docs/_posts/2015-02-26-introducing-jekyll-talk.markdown b/docs/_posts/2015-02-26-introducing-jekyll-talk.markdown new file mode 100644 index 0000000..b486ef6 --- /dev/null +++ b/docs/_posts/2015-02-26-introducing-jekyll-talk.markdown @@ -0,0 +1,14 @@ +--- +title: 'Join the Discussion at Jekyll Talk' +date: 2015-02-26 21:06:51 -0800 +author: alfredxing +categories: [community] +--- + +We're super excited to announce the launch of [Jekyll Talk](https://talk.jekyllrb.com), a Discourse forum for anything related to Jekyll! + +The forum was set up by [@envygeeks](https://github.com/envygeeks) to build a community more accessible to Jekyll users and more suitable for general discussion. + +There's already been a lot of interesting topics, including a site showcase and a poll for Jekyll 3.0 priorities. + +Come join the fun! diff --git a/docs/_posts/2015-10-26-jekyll-3-0-released.markdown b/docs/_posts/2015-10-26-jekyll-3-0-released.markdown new file mode 100644 index 0000000..36bf46a --- /dev/null +++ b/docs/_posts/2015-10-26-jekyll-3-0-released.markdown @@ -0,0 +1,34 @@ +--- +title: 'Jekyll 3.0 Released' +date: 2015-10-26 15:37:30 -0700 +author: parkr +version: 3.0 +category: release +--- + +The much-anticipated Jekyll 3.0 has been released! Key changes: + +- Incremental regeneration (experimental, enable with `--incremental`) +- Liquid profiler (add `--profile` to a build or serve) +- Hook plugin API (no more monkey-patching!) +- Dependencies reduced from 14 to 8, none contain C extensions. We're hoping to reduce this even more in the future. +- Changed version support: no support for Ruby 1.9.3, added basic JRuby support. Better Windows support. +- Extension-less URLs +- `site.collections` is an array of collections, thus: + - `collection[0]` becomes `collection.label` + - `collection[1]` becomes `collection` +- Default highlighter is now Rouge instead of Pygments +- Lots of performance improvements +- ... and lots more! + +We also added a [Code of Conduct](/docs/conduct/) to encourage a happier, nicer community where contributions and discussion is protected from negative behaviour. + +A huge shout-out to the amazing Jekyll Core Team members Jordon Bedwell, Alfred Xing, and Matt Rogers for all their hard work in making Jekyll 3 the best release yet. + +We also added [Jekyll Talk](https://talk.jekyllrb.com), managed solely by Jordon, which offers a modern forum experience for Jekyllers across the globe to talk and learn about Jekyll! + +As always, check out the [full history](/docs/history/#v3-0-0) for more details. + +Our contributors are the core of what makes Jekyll great! Many thanks to the 132 contributors who made this release possible (in alphabetical order): AJ Acevedo, Adam Richeimer, Alan Scherger, Alfred Xing, Anatol Broder, Andrew Dunning, Anna Debenham, Anton, Arne Gockeln, Arthur Hammer, Arthur Neves, BRAVO, Ben Balter, Bernardo Dias, BigBlueHat, Brandon Mathis, Bruce Smith, Cai⚡️, Carlos Matallín, ChaYoung You, Christian Vuerings, Cory Simmons, David Herman, David Silva Smith, David Smith, David Wales, David Williamson, DigitalSparky, Dimitri König, Dominik, Eduardo Boucas, Eduardo Bouças, Eduardo Bouças, Erlend Sogge Heggen, Eugene Pirogov, Ezmyrelda Andrade, Fabian Rodriguez, Fabian Tamp, Fabio Niephaus, Falko Richter, Florian Weingarten, Fonso, Garen Torikian, Guillaume LARIVIERE, Günter Kits, I´m a robot, Jason Ly, Jedd Ahyoung, Jensen Kuras, Jesse Pinho, Jesse W, Jim Meyer, Joel Glovier, Johan Bové, Joop Aué, Jordan Thornquest, Jordon Bedwell, Joseph Anderson, Julien Bourdeau, Justin Weiss, Kamil Dziemianowicz, Kevin Locke, Kevin Ushey, Leonard, Lukas, Mads Ohm Larsen, Malo Skrylevo, Marcus Stollsteimer, Mark Phelps, Mark Tareshawty, Martijn den Hoedt, Martin Jorn Rogalla, Martin Rogalla, Matt Rogers, Matt Sheehan, Matthias Nuessler, Max, Max Beizer, Max White, Merlos, Michael Giuffrida, Michael Tu, Mike Bland, Mike Callan, MonsieurV, Nate Berkopec, Neil Faccly, Nic West, Nicholas Burlett, Nicolas Hoizey, Parker Moore, Pascal Borreli, Pat Hawks, Paul Rayner, Pedro Euko, Peter Robins, Philipp Rudloff, Philippe Loctaux, Rafael Picanço, Renaud Martinet, Robert Papp, Ryan Burnette, Ryan Tomayko, Seb, Seth Warburton, Shannon, Stephen Crosby, Stuart Kent, Suriyaa Kudo, Sylvester Keil, Tanguy Krotoff, Toddy69, Tom Johnson, Tony Eichelberger, Tunghsiao Liu, Veres Lajos, Vitaly Repin, Will Norris, William Entriken, XhmikosR, chrisfinazzo, eksperimental, hartmel, jaybe@jekyll, kaatt, nightsense, nitoyon, robschia, schneems, sonnym, takuti, and tasken. + +Happy Jekylling! diff --git a/docs/_posts/2015-11-17-jekyll-3-0-1-released.markdown b/docs/_posts/2015-11-17-jekyll-3-0-1-released.markdown new file mode 100644 index 0000000..f3e6483 --- /dev/null +++ b/docs/_posts/2015-11-17-jekyll-3-0-1-released.markdown @@ -0,0 +1,24 @@ +--- +title: 'Jekyll 3.0.1 Released' +date: 2015-11-17 22:04:39 -0800 +author: parkr +version: 3.0.1 +category: release +--- + +Hey, folks! Bunch of bug fixes here. Notables: + +* Only superdirectories of `_posts` will be categories. +* `:title` in permalink templates are now properly cased as before +* `.jekyll-metadata` being erroneously written when not using incremental build. +* Failure in liquid will now always fail the `jekyll` process. +* All hooks should now be properly registered & documented + +And a bunch more changes which you can see over in the +[changelog](/docs/history/). + +Thanks to the 17 developers who contributed code and documentation to this +patch release: Alfred Xing, Christian Trosell, Jordan Thornquest, Jordon +Bedwell, Larry Fox, Lawrence Murray, Lewis Cowles, Matt Rogers, Nicole +White, Parker Moore, Paul Robert Lloyd, Sarah Kuehnle, Vincent Wochnik, +Will Norris, XhmikosR, chrisfinazzo, and rebornix. diff --git a/docs/_posts/2016-01-20-jekyll-3-0-2-released.markdown b/docs/_posts/2016-01-20-jekyll-3-0-2-released.markdown new file mode 100644 index 0000000..34c3aff --- /dev/null +++ b/docs/_posts/2016-01-20-jekyll-3-0-2-released.markdown @@ -0,0 +1,18 @@ +--- +title: 'Jekyll 3.0.2 Released' +date: 2016-01-20 14:08:18 -0800 +author: parkr +version: 3.0.2 +category: release +--- + +A crucial bug was found in v3.0.1 which caused invalid post dates to go +unnoticed in the build chain until the error that popped up was unhelpful. +v3.0.2 [throws errors as you'd expect](https://github.com/jekyll/jekyll/issues/4375) +when there is a post like `_posts/2016-22-01-future.md` or a post has an +invalid date like `date: "tuesday"` in their front matter. + +This should make the experience of working with Jekyll just a little +better. + +Happy Jekylling! diff --git a/docs/_posts/2016-01-24-jekyll-3-1-0-released.markdown b/docs/_posts/2016-01-24-jekyll-3-1-0-released.markdown new file mode 100644 index 0000000..216a4f2 --- /dev/null +++ b/docs/_posts/2016-01-24-jekyll-3-1-0-released.markdown @@ -0,0 +1,49 @@ +--- +title: 'Jekyll 3.1.0 Released' +date: 2016-01-24 13:16:12 -0800 +author: parkr +version: 3.1.0 +category: release +--- + +Happy weekend! To make your weekend all the better, we have just released +v3.1.0 of Jekyll. + +There are _lots_ of great performance improvements, including a huge one +which is to use Liquid drops instead of hashes. Much of the slowness in +Jekyll is due to Jekyll making lots of objects it doesn't need to make. +By making these objects only as they're needed, we can speed up Jekyll +considerably! + +Some other highlights: + +* Fix: `permalink`s with non-HTML extensions will not be honored +* Fix: `jekyll clean` now accepts build flags like `--source`. +* Enhancement: `include` tags can now accept multiple liquid variables +* Feature: adds new `sample` liquid tag which gets random element from an array +* Fix: Jekyll will read in files with front matter that has extraneous +spaces after the first line +* Enhancement: extract the `title` attribute from the filename for +collection items without a date +* Fix: gracefully handle empty configuration files + +... and [a whole bunch more](/docs/history/#v3-1-0)! + +Please [file a bug]({{ site.repository }}/issues/new?title=Jekyll+3.1.0+Issue:) +if you encounter any issues! As always, [Jekyll Talk](https://talk.jekyllrb.com) +is the best place to get help if you're encountering a problem. + +Special thanks to all our amazing contributors who helped make v3.1.0 a +possibility: + +Alex J Best, Alexander Köplinger, Alfred Xing, Alistair Calder, Atul +Bhosale, Ben Orenstein, Chi Trung Nguyen, Conor O'Callaghan, Craig P. +Motlin, Dan K, David Burela, David Litvak Bruno, Decider UI, Ducksan Cho, +Florian Thomas, James Wen, Jordon Bedwell, Joseph Wynn, Kakoma, Liam +Bowers, Mike Neumegen, Nick Quaranto, Nielsen Ramon, Olivér Falvai, Pat +Hawks, Paul Robert Lloyd, Pedro Euko, Peter Suschlik, Sam Volin, Samuel +Wright, Sasha Friedenberg, Tim Cuthbertson, Vincent Wochnik, William +Entriken, Zshawn Syed, chrisfinazzo, ducksan cho, leethomas, +midnightSuyama, musoke, and rebornix + +Happy Jekylling! diff --git a/docs/_posts/2016-01-28-jekyll-3-1-1-released.markdown b/docs/_posts/2016-01-28-jekyll-3-1-1-released.markdown new file mode 100644 index 0000000..925c9ff --- /dev/null +++ b/docs/_posts/2016-01-28-jekyll-3-1-1-released.markdown @@ -0,0 +1,32 @@ +--- +title: 'Jekyll 3.1.1 Released' +date: 2016-01-28 17:21:50 -0800 +author: parkr +version: 3.1.1 +category: release +--- + +This release squashes a few bugs :bug: :bug: :bug: noticed by a few +wonderful Jekyll users: + +* If your `permalink` ended with a `/`, your URL didn't have any extension, +even if you wanted one +* We now strip the BOM by default per Ruby's `IO.open`. +* `page.dir` will not always end in a slash. + +We also updated our [Code of Conduct](/docs/conduct/) to the latest version of +the Contributor Covenant. The update includes language to ensure that the +reporter of the incident remains confidential to non-maintainers and that +all complaints will result in an appropriate response. I care deeply about +Jekyll's community and will do everything in my power to ensure it is a +welcoming community. Feel free to reach out to me directly if you feel +there is a way we can improve the community for everyone! If you're +interested in more details, [there is a diff for +that](https://github.com/ContributorCovenant/contributor_covenant/blob/v1_4/diffs/1_3_vs_1_4.patch). + +See links to the PR's on [the history page](/docs/history/#v3-1-1). + +Thanks to Jordon Bedwell, chrisfinazzo, Kroum Tzanev, David Celis, and +Alfred Xing for their commits on this latest release! :sparkles: + +Happy Jekylling! diff --git a/docs/_posts/2016-02-08-jekyll-3-0-3-released.markdown b/docs/_posts/2016-02-08-jekyll-3-0-3-released.markdown new file mode 100644 index 0000000..05c9cc2 --- /dev/null +++ b/docs/_posts/2016-02-08-jekyll-3-0-3-released.markdown @@ -0,0 +1,31 @@ +--- +title: 'Jekyll 3.0.3 Released' +date: 2016-02-08 10:39:08 -0800 +author: parkr +version: 3.0.3 +category: release +--- + +[GitHub Pages upgraded to Jekyll 3.0.2][1] last week. With a testbed of +over a million sites, this really put Jekyll 3 through the wringer. This +release addresses a handful of bugs that were surfaced as a result. The +fixes: + +* Fix problem where outputting to a folder would have two extensions +* Handle tildes (`~`) in filenames properly +* Fix issue when comparing documents without dates +* Include line numbers in liquid error output + +Read more on the [changelog](/docs/history/#v3-0-3) with links to the +related patches. + +Please keep [submitting bugs][2] as you find them! Please do take a look +[in our various help resources](/help/) before filing a bug and use [our +forum][3] for asking questions and getting help on a specific problem +you're having. + +Happy Jekylling! + +[1]: https://github.com/blog/2100-github-pages-now-faster-and-simpler-with-jekyll-3-0 +[2]: {{ site.repository }}/issues +[3]: https://talk.jekyllrb.com/ diff --git a/docs/_posts/2016-02-19-jekyll-3-1-2-released.markdown b/docs/_posts/2016-02-19-jekyll-3-1-2-released.markdown new file mode 100644 index 0000000..df8bfd3 --- /dev/null +++ b/docs/_posts/2016-02-19-jekyll-3-1-2-released.markdown @@ -0,0 +1,19 @@ +--- +title: 'Jekyll 3.1.2 Released!' +date: 2016-02-19 15:24:00 -0800 +author: parkr +version: 3.1.2 +category: release +--- + +Happy Friday from sunny California! Today, we're excited to announce the release of Jekyll v3.1.2, which comes with some crucial bug fixes: + +* If a syntax error is encountered by Liquid, it will now print the line number. +* A nasty war between symbols and strings in our configuration hash caused kramdown syntax highlighting to break. That has been resolved; you stand victorious! +* A tilde at the beginning of a filename will no longer crash Jekyll. +* The `titleize` filter mistakenly dropped words that were already capitalized. Fixed! +* Permalinks which end in a slash will now always output as a folder with an `index.html` inside. + +Nitty-gritty details, like always, are available in the [history](/docs/history/). + +Thanks to those who contributed to this release: Alfred Xing, atomicules, bojanland, Brenton Horne, Carlos Garcés, Cash Costello, Chris, chrisfinazzo, Daniel Schildt, Dean Attali, Florian Thomas, Jordon Bedwell, Juuso Mikkonen, Katya Demidova, lonnen, Manabu Sakai, Michael Lee, Michael Lyons, Mitesh Shah, Nicolas Hoizey, Parker Moore, Pat Hawks, Prayag Verma, Robert Martin, Suriyaa Kudo, and toshi. diff --git a/docs/_posts/2016-03-10-making-it-easier-to-contribute-to-jekyll.md b/docs/_posts/2016-03-10-making-it-easier-to-contribute-to-jekyll.md new file mode 100644 index 0000000..9d80604 --- /dev/null +++ b/docs/_posts/2016-03-10-making-it-easier-to-contribute-to-jekyll.md @@ -0,0 +1,16 @@ +--- +title: Making it easier to contribute to Jekyll +description: We've made it easier to contribute to Jekyll by updating our contributing documentation and introducing Jekyll Affinity Teams, teams dedicated to specific aspects of the project. +author: benbalter +categories: [community] +--- + +Jekyll is an open source project, built one contribution at a time by community members just like you. These community contributions can come in many forms beyond just writing code, from reporting an issue or suggesting a new feature to improving documentation or providing feedback on proposed changes. + +If you've been looking to get involved with the Jekyll community, but didn't know, we've recently made it easier to contribute to Jekyll in two ways: + +First, we've completely rewritten [the project's contributing guidelines](https://jekyllrb.com/docs/contributing/), outlining [the various ways you can contribute](https://jekyllrb.com/docs/contributing/#ways-to-contribute), and including better instructions for [submitting proposed changes via GitHub.com](https://jekyllrb.com/docs/contributing/#submitting-a-pull-request-via-githubcom) or for [submitting your first code improvement](https://jekyllrb.com/docs/contributing/#code-contributions). And if you have any feedback, we'd love to hear it! Simply click the "improve this page" button in the top right corner of the contributing documentation. + +Second, this week, we created six community interest groups, we're calling [Jekyll affinity teams](https://teams.jekyllrb.com). If you're interested in a particular aspect of the project (or just want to learn more), you can join any one of these teams (or two, or three), to participate in discussions about potential bugs and proposed improvements. And the best part is there's no commitment. If you just want to listen, or if at any point you want to leave (or switch teams), that's totally fine. We won't say a thing. To learn more about the various affinity teams, or to join one (please do!), just head on over to [teams.jekyllrb.com](https://teams.jekyllrb.com/). + +We hope these changes will make it easier for you to make your first (or second, or third) contribution to Jekyll today. Thanks for helping to make Jekyll awesome! diff --git a/docs/_posts/2016-04-19-jekyll-3-0-4-released.markdown b/docs/_posts/2016-04-19-jekyll-3-0-4-released.markdown new file mode 100644 index 0000000..218ec07 --- /dev/null +++ b/docs/_posts/2016-04-19-jekyll-3-0-4-released.markdown @@ -0,0 +1,23 @@ +--- +title: 'Jekyll 3.0.4 Released' +date: 2016-04-19 10:26:12 -0700 +author: parkr +version: 3.0.4 +category: release +--- + +v3.0.4 is a patch release which fixes the follow two issues: + +- Front matter defaults may not have worked for collection documents and posts due to a problem where they were looked up by their URL rather than their path relative to the site source +- Configuration for the posts permalink might be borked when a user specified a value for `collections.posts.permalink` directly. This forced the use of `permalink` at the top level, which also affected pages. To configure a permalink _just for posts_, you can do so with: + +{% highlight yaml %} +collections: + posts: + output: true + permalink: /blog/:year/:title/ +{% endhighlight %} + +Both of these issues have been resolved. For more information, check out [the full history](/docs/history/#v3-0-4). + +Happy Jekylling! diff --git a/docs/_posts/2016-04-19-jekyll-3-1-3-released.markdown b/docs/_posts/2016-04-19-jekyll-3-1-3-released.markdown new file mode 100644 index 0000000..1735f59 --- /dev/null +++ b/docs/_posts/2016-04-19-jekyll-3-1-3-released.markdown @@ -0,0 +1,16 @@ +--- +title: 'Jekyll 3.1.3 Released' +date: 2016-04-19 10:26:16 -0700 +author: parkr +version: 3.1.3 +category: release +--- + +v3.1.3 is a patch release which fixes the follow two issues: + +- Front matter defaults may not have worked for collection documents and posts due to a problem where they were looked up by their URL rather than their path relative to the site source +- Running `jekyll serve` with SSL enabled was broken due to a bad configuration. + +Both of these issues have been resolved. For more information, check out [the full history](/docs/history/#v3-1-3). + +Happy Jekylling! diff --git a/docs/_posts/2016-04-26-jekyll-3-0-5-released.markdown b/docs/_posts/2016-04-26-jekyll-3-0-5-released.markdown new file mode 100644 index 0000000..8dc23be --- /dev/null +++ b/docs/_posts/2016-04-26-jekyll-3-0-5-released.markdown @@ -0,0 +1,24 @@ +--- +title: 'Jekyll 3.0.5 Released' +date: 2016-04-26 17:40:44 -0700 +author: parkr +version: 3.0.5 +category: release +--- + +This version fixes a bug affecting only v3.0.4 where autoregeneration was +*always* disabled when running Jekyll locally. This feature is a huge +reason why Jekyll (or any static site generator, for that matter) is a joy +to use. Sorry for the regression! + +If you're using GitHub Pages, [you can follow the progress of the upgrade +on the github/pages-gem repo](https://github.com/github/pages-gem/pull/285). + +As always, our [history doc](/docs/history/#v3-0-5) has links to the pull +requests and issues associated with the release for your perusal. + +We're looking forward to the upcoming release of v3.2 which [has some +excellent goodies](https://github.com/jekyll/jekyll/blob/master/History.markdown#head) +we think you'll love. + +Happy Jekylling! diff --git a/docs/_posts/2016-05-18-jekyll-3-1-4-released.markdown b/docs/_posts/2016-05-18-jekyll-3-1-4-released.markdown new file mode 100644 index 0000000..e1ad119 --- /dev/null +++ b/docs/_posts/2016-05-18-jekyll-3-1-4-released.markdown @@ -0,0 +1,25 @@ +--- +title: 'Jekyll 3.1.4 "Stability Sam" Released' +date: 2016-05-18 16:50:37 -0700 +author: parkr +version: 3.1.4 +category: release +--- + +Hey Jekyllites! + +Today, we released v3.1.4 in an effort to bring more stability to the v3.1.x series. This bugfix release consists of: + +* A fix for `layout` in Liquid where values would carry over from one document to the next +* A fix for `layout` in Liquid where a parent layout (e.g. `default` or `base`) would overwrite the metadata of the child layout (e.g. `post` or `special`). +* A fix where `page.excerpt` referencing its excerpt would cause an infinite loop of recursive horror. +* We added `Configuration.from` and the great permalink fix from [v3.0.4](/news/2016/04/19/jekyll-3-0-4-released/) to the v3.1.x series +* `site.collections` in Liquid is now sorted alphabetically by label, so `docs` shows up before `posts` reliably. + +The fixes for `layout` may not be seamless for everyone, but we believe they will be the "right thing to do" going forward. + +We are always striving to make Jekyll more straight-forward to use. Please do open an issue if you believe an aspect of Jekyll's user experience isn't up to par. + +For a full history of our changes, [see the changelog](/docs/history/#v3-1-4). + +As always, Happy Jekylling! diff --git a/docs/_posts/2016-05-18-jekyll-3-1-5-released.markdown b/docs/_posts/2016-05-18-jekyll-3-1-5-released.markdown new file mode 100644 index 0000000..0dbdd52 --- /dev/null +++ b/docs/_posts/2016-05-18-jekyll-3-1-5-released.markdown @@ -0,0 +1,16 @@ +--- +title: 'Jekyll 3.1.5 Released' +date: 2016-05-18 21:35:27 -0700 +author: parkr +version: 3.1.5 +category: release +--- + +There's always at least one bug, right? :) + +Hot on the trails of [v3.1.4](/news/2016/05/18/jekyll-3-1-4-released/), we +bring you v3.1.5! It fixes one bug around requiring the `ExcerptDrop`, +which only affects Linux. For the gory details, see [the pull +request for the fix](https://github.com/jekyll/jekyll/pull/4912). + +Happy Jekylling! diff --git a/docs/_posts/2016-05-19-jekyll-3-1-6-released.markdown b/docs/_posts/2016-05-19-jekyll-3-1-6-released.markdown new file mode 100644 index 0000000..a12c14e --- /dev/null +++ b/docs/_posts/2016-05-19-jekyll-3-1-6-released.markdown @@ -0,0 +1,18 @@ +--- +title: 'Jekyll 3.1.6 Released' +date: 2016-05-19 12:48:14 -0700 +author: parkr +version: 3.1.6 +category: release +--- + +Upon releasing 3.1.5 and kicking the tires, we noticed a glaring bug: our +beloved `jsonify` filter doesn't work! With that, our work was cut out for +us and we decided a 3.1.6 was necessary. This release restores sanity to +our object-to-JSON generation in Liquid and we hope you enjoy. + +For the gory details, see [the pull +request](https://github.com/jekyll/jekyll/pull/4914) or [the +changelog](/docs/history/#v3-1-6). + +Happy Jekylling! diff --git a/docs/_posts/2016-06-03-update-on-jekyll-s-google-summer-of-code-projects.markdown b/docs/_posts/2016-06-03-update-on-jekyll-s-google-summer-of-code-projects.markdown new file mode 100644 index 0000000..c3d01cd --- /dev/null +++ b/docs/_posts/2016-06-03-update-on-jekyll-s-google-summer-of-code-projects.markdown @@ -0,0 +1,18 @@ +--- +title: "Jekyll's Google Summer of Code Project: The CMS You Always Wanted" +date: "2016-06-03 13:21:02 -0700" +author: parkr +categories: [community] +--- + +This year, Jekyll applied to be a part of [Google Summer of Code](https://summerofcode.withgoogle.com/how-it-works/). Students were able to propose any project related to Jekyll. With a gracious sponsorship from GitHub and the participation of myself, @benbalter and @jldec, Jekyll was able to accept two students for the 2016 season, @mertkahyaoglu and @rush-skills. + +These students are working on a project that fills a huge need for the community: _a graphical solution for managing your site's content._ Current plans include a fully-integrated admin which spins up when you run jekyll serve and provides a friendly web interface for creating and editing your content. The server and web interface will speak a common HTTP interface so either piece could be switched out for, e.g. a server which writes directly to a repository on GitHub. + +The strength of text files as the storage medium for content has been part of Jekyll's success. [Our homepage](/) lauds the absence of a traditional SQL database when using Jekyll – your content should be what demands your time, not pesky database downtime. Unfortunately, understanding of the structure of a Jekyll site takes some work, enough that for some users, it's prohibitive to using Jekyll to accomplish their publishing goals. + +Mert and Ankur both applied to take on this challenge and agreed to split the project, one taking on the web interface and the other taking on the backend. We're very excited to see a fully-functional CMS for Jekyll at the end of the summer produced by these excellent community members, and we hope you'll join us in cheering them on and sharing our gratitude for all their hard work. + +Thanks, as always, for being part of such a wonderful community that made this all possible. I'm honored to work with each of you to create something folks all around the globe find a joy to use. I look forward to our continued work to move Jekyll forward. + +As always, Happy Jekylling! diff --git a/docs/_posts/2016-07-26-jekyll-3-2-0-released.markdown b/docs/_posts/2016-07-26-jekyll-3-2-0-released.markdown new file mode 100644 index 0000000..8644002 --- /dev/null +++ b/docs/_posts/2016-07-26-jekyll-3-2-0-released.markdown @@ -0,0 +1,124 @@ +--- +title: 'Jekyll turns 3.2' +date: 2016-07-26 15:06:49 -0700 +author: parkr +version: 3.2.0 +category: release +--- + +Happy Day! Jekyll v3.2.0 is out, and packed full of goodies. + +Our flagship feature for this release has been **themes**. _Themes?!_, you +say? Yes, proper, versionable, releasable, first-class themes. We're pretty +stoked about it and we hope you like building and using them. For now, it +only supports layouts, includes, and sass, but we have plans to include +static assets like images and CSS/JS in a future release. [Read more about +it in the docs.](/docs/themes/) Our site template generated by `jekyll new` +now dogfoods this feature, using the [minima](https://github.com/jekyll/minima) theme. + +Some other notable changes: + +- Symlinks are allowed as long as they target a file in the site source +- Explicit support for Ruby 2.0.x was dropped +- Added an `:after_init` Hook +- Added a `where_exp` filter to provide more powerful filtering +- Added a `link` liquid tag which can be used to generate URLs for any +post or document based on its path relative to the site source +- ... and lots more! + +As always, there is [a full list of changes](/docs/history/#v3-2-0) for +your perusal. + +Every release is made possible by the countless hours of hard work that our +fellow community members put into sending patches, filing thoughtful +patches, and so on. These release took the work of over 80 people: + +- Aaron Sky +- Adam Hollett +- ajhit406 +- Aki +- Alex Hanselka +- Alex Hoyau +- Alex Ivkin +- Alex Kitchens +- Alex Plescan +- Alex Wood +- Anatoliy Yastreb +- Andrew Artajos +- Andrew Munsell +- AndrewCz +- Ankush Menat +- Anthony Smith +- Ben Balter +- Brian Jones +- Brint O'Hearn +- Chayoung You +- Chris Wells +- chrisfinazzo +- Clark Winkelmann +- Dan Allen +- David Von Lehman +- David Zhang +- Derek Gottlieb +- Enes Gönültaş +- EricH +- Erick Sasse +- Eugênio Cabral +- Florian Thomas +- Frank Taillandier +- Henry Goodman +- Henry Wright +- Hugo Duksis +- Hugo Giraudel +- Jack Reed +- Jamie Bilinski +- Jeff Kolesky +- Jens Willmer +- Jordon Bedwell +- Josh Waller +- Joshua Barnett +- Keegan Mullaney +- Kevin Miller +- Krzysztof Jurewicz +- Loren Rogers +- Marcos Brito +- Marcus Stollsteimer +- Matt Rogers +- Michaël Guitton +- Mike Linksvayer +- Mike Neumegen +- Nathan Hazout +- Nick +- No +- nscyclone +- Parker Moore +- Pat Hawks +- Pierre Fenoll +- Praveen Kumar +- Rares Vernica +- Saleem Rashid +- Sam Dutton +- Shengbin Meng +- Shinn Kondo +- Shinnosuke Kondo +- skim +- Sondre Nilsen +- Spencer A. Bywater +- Stephen Checkoway +- Suriyaa Kudo +- surrim +- TheLucasMoore +- Thomas Wood +- Tim Wisniewski +- Tom Fejfar +- Tony Garnock-Jones +- Vincent Wochnik +- XhmikosR +- Yanis Vieilly +- Yordis Prieto +- Zack Spencer + +We are so grateful to all of you for helping to put together a terrific +release. Thank you! + +Happy Jekylling! diff --git a/docs/_posts/2016-08-02-jekyll-3-2-1-released.markdown b/docs/_posts/2016-08-02-jekyll-3-2-1-released.markdown new file mode 100644 index 0000000..d5af2a4 --- /dev/null +++ b/docs/_posts/2016-08-02-jekyll-3-2-1-released.markdown @@ -0,0 +1,23 @@ +--- +title: 'Jekyll 3.2.1 Released with Fix for Windows' +date: 2016-08-02 13:20:11 -0700 +author: parkr +version: 3.2.1 +category: release +--- + +Well, 3.2.0 has been a success, but with one fatal flaw: it doesn't work on +Windows! Sorry, Windows users. Hot on the trail of 3.2.0, this release +should squash that :bug:. Sorry about that! + +This release also fixes an issue when using [gem-based themes](/docs/themes/) +where the theme was rejected if it existed behind a symlink. This is a +common setup for the various ruby version managers, and for Ruby installed +via Homebrew. Props to @benbalter for fixing that up. + +Thanks to the contributors for this release: Adam Petrie, Ben Balter, +Daniel Chapman, DirtyF, Gary Ewan Park, Jordon Bedwell, and Parker Moore. + +As always, you can see our full changelog on [the History page](/docs/history/). + +Happy Jekylling! diff --git a/docs/_posts/2016-08-24-jekyll-admin-initial-release.markdown b/docs/_posts/2016-08-24-jekyll-admin-initial-release.markdown new file mode 100644 index 0000000..e256a5c --- /dev/null +++ b/docs/_posts/2016-08-24-jekyll-admin-initial-release.markdown @@ -0,0 +1,17 @@ +--- +title: "Jekyll Admin Initial Release" +date: "2016-08-25 09:50:00 +0300" +author: mertkahyaoglu +categories: [community] +--- + +[Jekyll's Google Summer of Code Project](https://jekyllrb.com/news/2016/06/03/update-on-jekyll-s-google-summer-of-code-projects/) has concluded. After three months of hard (but fun) work with my mentors @benbalter, @jldec, and @parkr, I'm proud to announce [Jekyll Admin](https://github.com/jekyll/jekyll-admin)'s [initial release](https://github.com/jekyll/jekyll-admin/releases/tag/v0.1.0). Jekyll admin is a Jekyll plugin that provides users with a traditional CMS-style graphical interface to author content and administer Jekyll sites. You can start to use it right away by following [these instructions](https://github.com/jekyll/jekyll-admin#installation). + +As a Google Summer of Code student, I feel very lucky to be part of a project that the community has been wanting for such a long time. The three-month Google Summer of Code period was a great journey. It was a lot of fun developing the project and seeing how it could help the community, and going forward, we are really excited to see where the project goes with the help of the amazing Jekyll community. + +I would like to thank my mentors who embraced me as their teammate and guided me throughout the process. They have put a lot of work and time to mentor me and helped me with everything. It was a great pleasure to work with them. I also would like to thank the wonderful Jekyll community for making Jekyll what it is today. It was amazing to see the community contribute to the project and give their feedback +prior to its release. I'm sure that they will support Jekyll Admin as much as they can and move Jekyll even further. + +Please let us know what you think about [Jekyll Admin](https://github.com/jekyll/jekyll-admin) and feel free to [contribute](https://github.com/jekyll/jekyll-admin/blob/master/.github/CONTRIBUTING.md). Your feedback and contributions are greatly appreciated. + +Happy (graphical) Jekylling! diff --git a/docs/_posts/2016-10-06-jekyll-3-3-is-here.md b/docs/_posts/2016-10-06-jekyll-3-3-is-here.md new file mode 100644 index 0000000..458f896 --- /dev/null +++ b/docs/_posts/2016-10-06-jekyll-3-3-is-here.md @@ -0,0 +1,109 @@ +--- +title: 'Jekyll 3.3 is here with better theme support, new URL filters, and tons more' +date: 2016-10-06 11:10:38 -0700 +author: parkr +version: 3.3.0 +category: release +--- + +There are tons of great new quality-of-life features you can use in 3.3. +Three key things you might want to try: + +### 1. Themes can now ship static & dynamic assets in an `/assets` directory + +In Jekyll 3.2, we shipped the ability to use a theme that was packaged as a +[gem](http://guides.rubygems.org/). 3.2 included support for includes, +layouts, and sass partials. In 3.3, we're adding assets to that list. + +In an effort to make theme management a bit easier, any files you put into +`/assets` in your theme will be read in as though they were part of the +user's site. This means you can ship SCSS and CoffeeScript, images and +webfonts, and so on -- anything you'd consider a part of the +*presentation*. Same rules apply here as in a Jekyll site: if it has YAML +front matter, it will be converted and rendered. No front matter, and +it will simply be copied over like a static asset. + +Note that if a user has a file of the same path, the theme content will not +be included in the site, i.e. a user's `/assets/main.scss` will be read and +processed if present instead of a theme's `/assets/main.scss`. + +See our [documentation on the subject]({{ "/docs/themes/#assets" | relative_url }}) +for more info. + +### 2. `relative_url` and `absolute_url` filters + +Want a clean way to prepend the `baseurl` or `url` in your config? These +new filters have you covered. When working locally, if you set your +`baseurl` to match your deployment environment, say `baseurl: "/myproject"`, +then `relative_url` will ensure that this baseurl is prepended to anything +you pass it: + +{% raw %} +```liquid +{{ "/docs/assets/" | relative_url }} => /myproject/docs/assets +``` +{% endraw %} + +By default, `baseurl` is set to `""` and therefore yields (never set to +`"/"`): + +{% raw %} +```liquid +{{ "/docs/assets/" | relative_url }} => /docs/assets +``` +{% endraw %} + +A result of `relative_url` will safely always produce a URL which is +relative to the domain root. A similar principle applies to `absolute_url`. +It prepends your `baseurl` and `url` values, making absolute URLs all the +easier to make: + +{% raw %} +```liquid +{{ "/docs/assets/" | absolute_url }} => https://jekyllrb.com/myproject/docs/assets +``` +{% endraw %} + +### 3. `site.url` is set by the development server + +When you run `jekyll serve` locally, it starts a web server, usually at +`http://localhost:4000`, that you use to preview your site during +development. If you are using the new `absolute_url` filter, or using +`site.url` anywhere, you have probably had to create a development config +which resets the `url` value to point to `http://localhost:4000`. + +No longer! When you run `jekyll serve`, Jekyll will build your site with +the value of the `host`, `port`, and SSL-related options. This defaults to +`url: http://localhost:4000`. When you are developing locally, `site.url` +will yield `http://localhost:4000`. + +This happens by default when running Jekyll locally. It will not be set if +you set `JEKYLL_ENV=production` and run `jekyll serve`. If `JEKYLL_ENV` is +any value except `development` (its default value), Jekyll will not +overwrite the value of `url` in your config. And again, this only applies +to serving, not to building. + +## A *lot* more! + +There are dozens of bug fixes and minor improvements to make your Jekyll +experience better than ever. With every Jekyll release, we strive to bring +greater stability and reliability to your everyday development workflow. + +As always, thanks to our many contributors who contributed countless hours +of their free time to making this release happen: + +Anatoliy Yastreb, Anthony Gaudino, Antonio, Ashwin Maroli, Ben Balter, +Charles Horn, Chris Finazzo, Daniel Chapman, David Zhang, Eduardo +Bouças, Edward Thomson, Eloy Espinaco, Florian Thomas, Frank Taillandier, +Gerardo, Heng Kwokfu, Heng, K. (Stephen), Jeff Kolesky, Jonathan Thornton, +Jordon Bedwell, Jussi Kinnula, Júnior Messias, Kyle O'Brien, Manmeet Gill, +Mark H. Wilkinson, Marko Locher, Mertcan GÖKGÖZ, Michal Švácha, Mike +Kasberg, Nadjib Amar, Nicolas Hoizey, Nicolas Porcel, Parker Moore, Pat +Hawks, Patrick Marsceill, Stephen Checkoway, Stuart Kent, XhmikosR, Zlatan +Vasović, mertkahyaoglu, shingo-nakanishi, and vohedge. + +[Full release notes]({{ "/docs/history/#v3-3-0" | relative_url }}) are available +for your perusal. If you notice any issues, please don't hesitate to file a +bug report. + +Happy Jekylling! diff --git a/docs/_posts/2016-11-14-jekyll-3-3-1-released.markdown b/docs/_posts/2016-11-14-jekyll-3-3-1-released.markdown new file mode 100644 index 0000000..6c286ff --- /dev/null +++ b/docs/_posts/2016-11-14-jekyll-3-3-1-released.markdown @@ -0,0 +1,18 @@ +--- +title: 'Jekyll 3.3.1 Released' +date: 2016-11-14 14:29:59 -0800 +author: parkr +version: 3.3.1 +category: release +--- + +Hello! We have a bugfix release of Jekyll hot off the presses for you. Key +fixes to call out: + +1. Only warn about auto-regeneration issues on Windows instead of disabling +2. Exclude very specific `vendor/` subdirectories instead of all of `vendor/` +3. Allow permalink templates to have plaintext underscores + +..and lots more! Check out the [full history for more](/docs/history/#v3-3-1). + +Happy Jekylling! diff --git a/docs/_posts/2017-01-18-jekyll-3-4-0-released.markdown b/docs/_posts/2017-01-18-jekyll-3-4-0-released.markdown new file mode 100644 index 0000000..bf1db93 --- /dev/null +++ b/docs/_posts/2017-01-18-jekyll-3-4-0-released.markdown @@ -0,0 +1,42 @@ +--- +title: 'Jekyll turns 3.4.0' +date: 2017-01-18 14:19:13 -0500 +author: parkr +version: 3.4.0 +category: release +--- + +Hey there! We have a quick update of Jekyll for you to enjoy this January. +Packed full of bug fixes as usual, thanks to the tireless efforts of our +exceptional Jekyll community. Three changes to call out: + +1. If you're a big fan of [`where_by_exp`](/docs/liquid/filters/), you'll be an +even bigger fan of [`group_by_exp`](/docs/liquid/filters/). +2. Using a custom timezone in Jekyll on Windows? Yeah, sorry that hasn't ever worked + properly. We made it possible to accurately [set the timezone using IANA + timezone codes](/docs/installation/windows/#time-zone-management). +3. Documentation has been improved, notably on themes, includes and permalinks. + +And [lots and lots more!](/docs/history/#v3-4-0) + +This update was made possible by the dedicated efforts of our excellent +contributors: Ajay Karwal, Alexey Rogachev, Ashwin Maroli, +BlueberryFoxtrot, Chase, Chayoung You, Dean Attali, Dmitrii Evdokimov, Don +Denton, Eldritch Cheese, Fabrice Laporte, Florian Thomas, Frank +Taillandier, Hugo, Ivan Dmitrievsky, Joel Meyer-Hamme, Josh Habdas, Kenton +Hansen, Kevin Wojniak, Kurt Anderson, Longwelwind, Max Chadwick, Nicolas +Hoizey, Nursen, Parker Moore, Pat Hawks, Purplecarrot, Ricardo N Feliciano, +Rob Crocombe, Roger Ogden, Skylar Challand, Thiago Arrais, Tim Banks, Tom +Johnson, Tunghsiao Liu, XhmikosR, Zlatan Vasović, alexmalik, brainscript, +kimbaudi, muratayusuke, penny, and yoostk. + +As always, if you encounter bugs, please do [search the issues]({{ site.repository }}/issues) +and [file an issue]({{ site.repository }}/issues/new) if you aren't able to +find a resolution. We also have [our Jekyll Talk +forum](https://talk.jekyllrb.com) for those of you with general questions +about how to accomplish certain tasks with Jekyll. + +We have some exciting updates in store for v3.5, and we're hard at work on +those already. + +Happy Jekylling! diff --git a/docs/_posts/2017-03-02-jekyll-3-4-1-released.markdown b/docs/_posts/2017-03-02-jekyll-3-4-1-released.markdown new file mode 100644 index 0000000..213e57a --- /dev/null +++ b/docs/_posts/2017-03-02-jekyll-3-4-1-released.markdown @@ -0,0 +1,106 @@ +--- +title: 'Jekyll 3.4.1, or "Unintended Consequences"' +date: 2017-03-02 14:20:26 -0500 +author: parkr +version: 3.4.1 +category: release +--- + +Conformity is a confounding thing. + +We write tests to ensure that a piece of functionality that works today +will work tomorrow, as further modifications are made to the codebase. This +is a principle of modern software development: every change must have a +test to guard against regressions to the functionality implemented by that +change. + +And yet, occasionally, our very best efforts to test functionality will be +thwarted. This is because of how our code produces unintended +functionality, which naturally goes untested. + +In our documentation, we tell users to name their posts with the following +format: + +```text +YYYY-MM-DD-title.extension +``` + +That format specifies exactly four numbers for the year, e.g. 2017, two +letters for the month, e.g. 03, and two letters for the day, e.g. 02. To +match this, we had the following regular expression: + +```ruby +%r!^(?:.+/)*(\d+-\d+-\d+)-(.*)(\.[^.]+)$! +``` + +You might already see the punchline. While our documentation specifies the +exact number of numbers that is required for each section of the date, our +regular expression does not enforce this precision. What happens if a user +doesn't conform to our documentation? + +We recently [received a bug report](https://github.com/jekyll/jekyll/issues/5603) +that detailed how the following file was considered a post: + +```text +84093135-42842323-42000001-b890-136270f7e5f1.md +``` + +Of course! It matches the above regular expression, but doesn't satisfy +other requirements about those numbers being a valid date (unless you're +living in a world that has 43 million months, and 42 million (and one) +days). So, we [modified the regular expression to match our +documentation](https://github.com/jekyll/jekyll/pull/5609): + +```ruby +%r!^(?:.+/)*(\d{4}-\d{2}-\d{2})-(.*)(\.[^.]+)$! +``` + +Our tests all passed and we were properly excluding this crazy date with 43 +million months and days. This change shipped in Jekyll v3.4.0 and all was +well. + +Well, not so much. + +A very common way to specify the month of February is `2`. This is true for +all single-digit months and days of the month. Notice anything about our +first regular expression versus our second? The second regular expression +imposes a **minimum**, as well as maximum, number of digits. This change +made Jekyll ignore dates with single-digit days and months. + +The first eight years of Jekyll's existence had allowed single-digit days +and months due to an imprecise regular expression. For some people, their +entire blog was missing, and there were no errors that told them why. + +After receiving a few bug reports, it became clear what had happened. +Unintended functionality of the last eight years had been broken. Thus, +v3.4.0 was broken for a non-negligible number of sites. With a test site +in-hand from @andrewbanchich, I tracked it down to this regular expression +and [reintroduced](https://github.com/jekyll/jekyll/pull/5920) a proper +minimum number of digits for each segment: + +```ruby +%r!^(?:.+/)*(\d{2,4}-\d{1,2}-\d{1,2})-(.*)(\.[^.]+)$! +``` + +And, I wrote a test. + +This change was quickly backported to v3.4.0 and here we are: releasing +v3.4.1. It will fix the problem for all users who were using single-digit +months and days. + +With this, I encourage all of you to look at your code for *unintended* +functionality and make a judgement call: if it's allowed, *should it be*? +If it should be allowed, make it *intended* functionality and test it! I +know I'll be looking at my code with much greater scrutiny going forward, +looking for unintended consequences. + +Many thanks to our Jekyll affinity team captains who helped out, including +@pathawks, @pnn, and @DirtyF. Thanks, too, to @ashmaroli for reviewing my +change with an eye for consistency and precision. This was certainly a team +effort. + +We hope Jekyll v3.4.1 brings your variable-digit dates back to their +previous glory. We certainly won't let that unintended functionality be +unintended any longer. + +As always, Happy Jekylling! diff --git a/docs/_posts/2017-03-09-jekyll-3-4-2-released.markdown b/docs/_posts/2017-03-09-jekyll-3-4-2-released.markdown new file mode 100644 index 0000000..775be8c --- /dev/null +++ b/docs/_posts/2017-03-09-jekyll-3-4-2-released.markdown @@ -0,0 +1,51 @@ +--- +title: 'Jekyll 3.4.2 Released' +date: 2017-03-09 15:41:57 -0500 +author: parkr +version: 3.4.2 +category: release +--- + +Another one-PR patch update, though without the same [lessons as for the +previous release]({% link _posts/2017-03-02-jekyll-3-4-1-released.markdown %}). + +This release includes a beneficial change for a number of plugins: +**static files now respect front matter defaults**. + +You might be asking yourself: "why would static files, files that are +static files explicitly because they *don't* have front matter, want +to respect front matter?" That's a great question. Let me illustrate +with an example. + +Let's look at `jekyll-sitemap`. This plugin generates a list of documents, +pages, and static files, and some metadata for them in an XML file for a +Google/Yahoo/Bing/DuckDuckGo crawler to consume. If you don't want a given +file in this list, you set `sitemap: false` in front matter. But +what about static files, which don't have front matter? Before this +release, they could not be excluded because they had no properties in YAML +other than [the ones we explicitly assigned](https://github.com/jekyll/jekyll/blob/v3.4.1/lib/jekyll/static_file.rb#L98-L106). +So if you had a PDF you didn't want to be in your sitemap, you couldn't use +`jekyll-sitemap`. + +With this release, you can now set [front matter +defaults](/docs/configuration/front-matter-defaults/) for static files: + +```yaml +defaults: + - + scope: + path: "pdfs/" + values: + sitemap: false +``` + +Now, for every file in the Liquid `site.static_files` loop which is in the +folder `pdfs/`, you'll see `sitemap` equal to `false`. + +Many thanks to @benbalter for coming up with the solution and ensuring +sitemaps everywhere are filled with just the right content. + +As always, if you notice any bugs, please search the issues and file one if +you can't find another related to your issue. + +Happy Jekylling! diff --git a/docs/_posts/2017-03-21-jekyll-3-4-3-released.markdown b/docs/_posts/2017-03-21-jekyll-3-4-3-released.markdown new file mode 100644 index 0000000..7373961 --- /dev/null +++ b/docs/_posts/2017-03-21-jekyll-3-4-3-released.markdown @@ -0,0 +1,49 @@ +--- +title: 'Jekyll 3.4.3 Released' +date: 2017-03-21 08:52:53 -0500 +author: pathawks +version: 3.4.3 +category: release +--- + +Another one-PR patch update as we continue our quest to destroy all bugs. A +fairly technical debriefing follows, but the TLDR is that we have updated the +`uri_escape` filter to more closely follow the pre-v3.4.0 behavior. + +In [v3.4.0]({% link _posts/2017-01-18-jekyll-3-4-0-released.markdown %}), we +moved away from using the deprecated +[`URI.escape`](https://ruby-doc.org/stdlib-2.3.0/libdoc/uri/rdoc/URI/Escape.html#method-i-encode) +in favor of +[`Addressable::URI.encode`](http://www.rubydoc.info/gems/addressable/Addressable/URI#encode-class_method). +This is what powers our [`uri_escape` +filter](https://jekyllrb.com/docs/templates/). + +While this transition was mostly a smooth one, the two methods are not +identical. While `URI.escape` was happy to escape any string, +`Addressable::URI.encode` first turns the string into an `Addressable::URI` +object, and will then escape each component of that object. In most cases, this +difference was insignificant, but there were a few cases where this caused some +unintended regressions when encoding colons. + +While **Addressable** can understand that something like `"/example :page"` is a +relative URI, without the slash it cannot figure out how to turn +`"example :page"` into an `Addressable::URI` object. `URI.escape` had no such +objection. This lead to the following Liquid code working fine in Jekyll 3.3.x +but breaking in 3.4.0: + +{% raw %} +```liquid +{{ "example :page" | uri_escape }} +``` +{% endraw %} + +This was not an intended consequence of switching to **Addressable**. + +Fortunately, the solution was not complicated. **Addressable** has a method +[`Addressable::URI.normalize_component`](http://www.rubydoc.info/gems/addressable/Addressable/URI#normalize_component-class_method) +which will simply escape the characters in a string, much like `URI.escape`. + +Thanks to @cameronmcefee and @FriesFlorian for reporting +[this issue](https://github.com/jekyll/jekyll/issues/5954). + +Happy Jekylling! diff --git a/docs/_posts/2017-06-14-jekyll-3-5-0-released.markdown b/docs/_posts/2017-06-14-jekyll-3-5-0-released.markdown new file mode 100644 index 0000000..7bb2a82 --- /dev/null +++ b/docs/_posts/2017-06-14-jekyll-3-5-0-released.markdown @@ -0,0 +1,38 @@ +--- +title: 'Jekyll turns 3.5, oh my!' +date: 2017-06-15 17:32:32 -0400 +author: parkr +version: 3.5.0 +category: release +--- + +Good news! Nearly 400 commits later, Jekyll 3.5.0 has been released into +the wild. Some new shiny things you might want to test out: + +- Jekyll now uses Liquid 4, the latest! It comes with whitespace control, new filters `concat` and `compact`, loop performance improvements and [many fixes](https://github.com/Shopify/liquid/blob/master/History.md#400--2016-12-14--branch-4-0-stable) +- Themes can specify runtime dependencies (in their gemspecs) and we'll require those. This makes it easier for theme writers to use plugins. +- Speaking of themes, we'll properly handle the discrepancy between a convertible file in the local site and a static file in the theme. Overriding a file locally now doesn't matter if it's convertible or static. +- Pages, posts, and other documents can now access layout variables via `{% raw %}{{ layout }}{% endraw %}`. +- The `gems` key in the `_config.yml` is now `plugins`. This is backwards-compatible, as Jekyll will gracefully upgrade `gems` to `plugins` if you use the former. +- Filters like `sort` now allow you to sort based on a subvalue, e.g. `{% raw %}{% assign sorted = site.posts | sort: "image.alt_text" %}{% endraw %}`. +- You can now create tab-separated data files. +- Using `layout: none` will now produce a file with no layout. Equivalent to `layout: null`, with the exception that `none` is a truthy value and won't be overwritten by front matter defaults. +- No more pesky errors if your URL contains a colon (sorry about those!) +- We now automatically exclude the `Gemfile` from the site manifest when compiling your site. No more `_site/Gemfile`! +- We fixed a bug where abbreviated post dates were ignored, e.g. `_posts/2016-4-4-april-fourth.md`. + +And [so much more!](/docs/history/) + +There was a huge amount of effort put into this release by our maintainers, +especially @pathawks, @DirtyF, and @pup. Huge thanks to them for ushering +this release along and keeping the contributions flowing! Jekyll wouldn't +work without the tireless dedication of our team captains & maintainers. +Thank you, all! + +A huge thanks as well to our contributors to this release: Adam Hollett, Aleksander Kuś, Alfred Myers, Anatoliy Yastreb, Antonio Argote, Ashton Hellwig, Ashwin Maroli, Ben Balter, BlueberryFoxtrot, Brent Yi, Chris Finazzo, Christoph Päper, Christopher League, Chun Fei Lung, Colin, David Zhang, Eric Leong, Finn Ellis, Florian Thomas, Frank Taillandier, Hendrik Schneider, Henry Kobin, Ivan Storck, Jakub Klímek, Jan Pobořil, Jeff Puckett, Jonathan Hooper, Kaligule, Kevin Funk, Krzysztof Szafranek, Liu Cheng, Lukasz Brodowski, Marc Bruins, Marcelo Canina, Martin Desrumaux, Mer, Nate, Oreonax, Parker Moore, Pat Hawks, Pedro Lamas, Phil Nash, Ricardo N Feliciano, Ricky Han, Roger Sheen, Ryan Lue, Ryan Streur, Shane Neuville, Sven Meyer, Tom Johnson, William Entriken, Yury V. Zaytsev, Zarino Zappia, dyang, jekylltools, sean delaney, zenHeart + +Please file any bugs with detailed replication instructions if you find any +bugs. Better yet, submit a patch if you find the bug in the code and know +how to fix it! :heart: + +Happy Jekylling! :tada: diff --git a/docs/_posts/2017-07-17-jekyll-3-5-1-released.markdown b/docs/_posts/2017-07-17-jekyll-3-5-1-released.markdown new file mode 100644 index 0000000..96b76ed --- /dev/null +++ b/docs/_posts/2017-07-17-jekyll-3-5-1-released.markdown @@ -0,0 +1,19 @@ +--- +title: 'Jekyll 3.5.1 Released' +date: 2017-07-17 12:40:37 -0400 +author: parkr +version: 3.5.1 +category: release +--- + +We've released a few bugfixes in the form of v3.5.1 today: + +- Some plugins stopped functioning properly due to a NoMethodError for `registers` on NilClass. That's been fixed. +- A bug in `relative_url` when `baseurl` is `nil` caused URL's to come out wrong. Squashed. +- Static files' liquid representations should now have all the keys you were expecting when serialized into JSON. + +We apologize for the breakages! We're working diligently to improve how we test our plugins with Jekyll core to prevent breakages in the future. + +More details in [the history](/docs/history/#v3-5-1). Many thanks to all the contributors to Jekyll v3.5.1: Adam Voss, ashmaroli, Ben Balter, Coby Chapple, Doug Beney, Fadhil, Florian Thomas, Frank Taillandier, James, jaybe, Joshua Byrd, Kevin Plattret, & Robert Jäschke. + +Happy Jekylling! diff --git a/docs/_posts/2017-08-12-jekyll-3-5-2-released.markdown b/docs/_posts/2017-08-12-jekyll-3-5-2-released.markdown new file mode 100644 index 0000000..84deb6f --- /dev/null +++ b/docs/_posts/2017-08-12-jekyll-3-5-2-released.markdown @@ -0,0 +1,23 @@ +--- +title: 'Jekyll 3.5.2 Released' +date: 2017-08-12 16:31:40 -0400 +author: parkr +version: 3.5.2 +category: release +--- + +3.5.2 is out with 6 great bug fixes, most notably one which should dramatically speed up generation of your site! In testing #6266, jekyllrb.com generation when from 18 seconds down to 8! Here is the full line-up of fixes: + + * Backport #6266 for v3.5.x: Memoize the return value of `Document#url` (#6301) + * Backport #6247 for v3.5.x: kramdown: symbolize keys in-place (#6303) + * Backport #6281 for v3.5.x: Fix `Drop#key?` so it can handle a nil argument (#6288) + * Backport #6280 for v3.5.x: Guard against type error in `absolute_url` (#6287) + * Backport #6273 for v3.5.x: delegate `StaticFile#to_json` to `StaticFile#to_liquid` (#6302) + * Backport #6226 for v3.5.x: `Reader#read_directories`: guard against an entry not being a directory (#6304 + +A [full history](/docs/history/#v3-5-2) is available for your perusal. As always, please file bugs if you encounter them! Opening a pull request with a failing test for your expected behaviour is the easiest way for us to address the issue since we have a reproducible example to test again. Short of that, please fill out our issue template to the best of your ability and we'll try to get to it quickly! + +Many thanks to our contributors without whom this release could not be +possible: Ben Balter & Kyle Zhao. + +Happy Jekylling! diff --git a/docs/_posts/2017-09-21-jekyll-3-6-0-released.markdown b/docs/_posts/2017-09-21-jekyll-3-6-0-released.markdown new file mode 100644 index 0000000..3a36efb --- /dev/null +++ b/docs/_posts/2017-09-21-jekyll-3-6-0-released.markdown @@ -0,0 +1,17 @@ +--- +title: 'Jekyll turns 3.6!' +date: 2017-09-21 16:38:20 -0400 +author: parkr +version: 3.6.0 +category: release +--- + +Another much-anticipated release of Jekyll. This release comes with it Rouge 2 support, but note you can continue to use Rouge 1 if you'd prefer. We also now require Ruby 2.1.0 as 2.0.x is no longer supported by the Ruby team. + +Otherwise, it's a massive bug-fix release! A few bugs were found and squashed with our `Drop` implementation. We're using the Schwartzian transform to speed up our custom sorting (thanks, Perl community!). We now protect against images that are named like posts and we generally worked on guarding our code to enforce requirements, instead of assuming the input was as expected. + +Please let us know if you find any bugs! You can see [the full history here](/docs/history/#v3-6-0). + +Many thanks to our contributors who helped make this release possible: Aleksander Kuś, André Jaenisch, Antonio Argote, ashmaroli, Ben Balter, Bogdan, Bradley Meck, David Zhang, Florian Thomas, Frank Taillandier, Jordon Bedwell, Joshua Byrd, Kyle Zhao, lymaconsulting, Maciej Bembenista, Matt Sturgeon, Natanael Arndt, Ohad Schneider, Pat Hawks, Pedro Lamas, and Sid Verma. + +As always, Happy Jekylling! diff --git a/docs/_posts/2017-10-19-diversity-open-source.markdown b/docs/_posts/2017-10-19-diversity-open-source.markdown new file mode 100644 index 0000000..a5cde65 --- /dev/null +++ b/docs/_posts/2017-10-19-diversity-open-source.markdown @@ -0,0 +1,44 @@ +--- +title: "Diversity in Open Source, and Jekyll's role in it" +date: 2017-10-19 21:33:00 +0200 +author: oe +categories: [community] +--- + +Open Source has a problem with diversity. GitHub recently conducted a [survey](http://opensourcesurvey.org/2017) which revealed that 95% of the respondents were identifying as male. This is even worse than in the tech industry overall, where the percentage is only about 76%. Every other week, there seems to be another case of a maintainer engaging in targeted harassment against minorities. People somehow deem it completely okay to let these things slide, though. + +Fortunately, there's a couple of things we can do to make it easier and more comfortable for people that have never contributed to any open source project before, to contribute to our projects. + +## Add a Code of Conduct, and enforce it + +This might seem like one of the easiest steps to do, but it actually requires a lot of dedication to pull through with. Basically, a Code of Conduct is a document detailing what is and what isn't acceptable behavior in your project. A good Code of Conduct also details enforcement procedures, that means how the person violating the Code of Conduct gets dealt with. This is the point at which I've seen a looooot of projects fail. It's easy enough to copy-paste a Code of Conduct into your project, but it's more important to be clear on how to enforce it. Inconsistent —or worse, nonexistent— enforcement is just going to scare off newcomers even more! + +The most widely adopted Code of Conduct is the [Contributor Covenant](https://www.contributor-covenant.org/). It's a very good catch-all document, but it is a bit light in the enforcement section, so I'd recommend to flesh it out by yourself, be it by means of adding contact information or expanding the enforcement rules. + +No matter which Code of Conduct you pick, the most important thing is to actually _read it for yourself_. The worst thing in open source is a maintainer that doesn't know when they've violated their own Code of Conduct. + +## Document your contributing workflow + +The problem that puts people off the most is incomplete or missing documentation, as revealed through GitHub's [open source survey](http://opensourcesurvey.org/2017). A very popular myth in programming is that good code explains itself, which might be true, but only for the person writing it. It's important, especially once you put your project out there for the world to see, to document not only your code, but also the process by which you maintain it. Otherwise, it's going to be extremely hard for newcomers to even figure out where to begin contributing to your project. + +Jekyll has [an entire section of its docs](/docs/contributing/) dedicated to information on how to contribute for this very reason. Every documentation page has a link to directly edit and improve it on GitHub. It's also important to realize that not all contributions are code. It can be documentation, it can be reviewing pull requests, but it can also just be weighing into issues, and all of this should be recognized in the same way. At Jekyll, out of 397 total merged pull requests in the last year, __204__ were documentation pull requests! + +## Create newcomer-friendly issues + +For most people new to open source, the biggest hurdle is creating their first pull request. That's why initiatives such as [YourFirstPR](https://twitter.com/yourfirstpr) and [First Timers Only](http://www.firsttimersonly.com/) were started. Recently, [a GitHub bot that automatically creates first-timer friendly issues](https://github.com/hoodiehq/first-timers-bot) was launched, which makes it very easy for maintainers to convert otherwise small or trivial changes into viable pull requests that can be taken on by newcomers! So we decided to give it a shot, and we've created a couple of very easy `first timers only` issues: + +- [Issue #6437](https://github.com/jekyll/jekyll/issues/6437) +- [Issue #6438](https://github.com/jekyll/jekyll/issues/6438) +- [Issue #6439](https://github.com/jekyll/jekyll/issues/6439) + +(There's also an up-to-date listing of all of our `first timers only` issues [here](https://github.com/jekyll/jekyll/issues?q=is%3Aissue+is%3Aopen+label%3Afirst-time-only)) + +These issues are designed to be taken on only by someone who has had little to no exposure to contributing to open source before, and additionally, project maintainers offer support in case a question arises. + +Jekyll is a very big and popular open source project, and we hope that with these special issues, we can help people who haven't contributed to open source before to catch a footing in these unsteady waters. + +## Be nice + +I know this is a cliche and a overused phrase, but really, it works if you pull through with it. Come to terms with the fact that some people aren't as fast or reliable as you might want to think. Don't get angry when a contributor takes a day longer than you might like them to. Treat new contributors to your project with respect, but also with hospitality. Think twice before you send that comment with slurs in it. + +I've been contributing to open source for about 4 years now, and I've had my fair share of horrible, horrible experiences. But Jekyll has historically been a project that has always valued the people contributing to it over the code itself, and I hope we can keep it that way. I also hope that other project maintainers read this and take inspiration from this post. Every project should be more diverse. diff --git a/docs/_posts/2017-10-21-jekyll-3-6-2-released.markdown b/docs/_posts/2017-10-21-jekyll-3-6-2-released.markdown new file mode 100644 index 0000000..ad6d37a --- /dev/null +++ b/docs/_posts/2017-10-21-jekyll-3-6-2-released.markdown @@ -0,0 +1,42 @@ +--- +title: 'Jekyll 3.6.2 Released' +date: 2017-10-21 21:31:40 +0200 +author: dirtyf +version: 3.6.2 +category: release +--- + +3.6.2 is out, it's a tiny patch release and we invite you to run `bundle update` +if you want to avoid possible build problems with: + +* some UTF-8 and UTF-16 encoded files, +* fully numeric layout names (we convert those to string for you now). + +Other changes include updates to our documentation, like this [complete +video series by Giraffe Academy](/tutorials/video-walkthroughs/) aimed at +complete beginners. A big thanks to Mike for this. + +And if you're wondering what happened to version 3.6.1, it was just our new +release maintainer getting familiar with the release process. 😄 + +We try to release patch releases as quickly as possible and we're already +working on the next minor version 3.7.0 that will allow you to store all your +collections in a single directory. Stay tuned. + +Theme developers are invited to test the brand new +[`jekyll-remote-theme`](https://github.com/benbalter/jekyll-remote-theme) plugin +and give their feedback to @benbalter. This plugin allows you to use any +GitHub hosted theme as a remote theme! + +Once again, many thanks to our contributors who helped make this release possible: +ashmaroli, bellvat, Frank Taillandier, i-give-up, Jan Piotrowski, Maximiliano +Kotvinsky, Oliver Steele and Pat Hawks. For some it was their [first +contribution to open-source]({% link +_posts/2017-10-19-diversity-open-source.markdown %}) 👏 + +As it's been nine years this week that Tom Preston-Werner started this project, +I also wanna seize this opportunity to thank [all of the 732 contributors](https://github.com/jekyll/jekyll/graphs/contributors) who +helped make it possible for Jekyll to power millions of websites around the world +today. + +Happy Birthday Jekyll! 🎂 diff --git a/docs/_posts/2018-01-02-jekyll-3-7-0-released.md b/docs/_posts/2018-01-02-jekyll-3-7-0-released.md new file mode 100644 index 0000000..c83ce55 --- /dev/null +++ b/docs/_posts/2018-01-02-jekyll-3-7-0-released.md @@ -0,0 +1,37 @@ +--- +title: "Jekyll 3.7.0 Released" +description: "Jekyll 3.7.0 brings LiveReload, a directory for your collections and much more…" +date: 2018-01-02 11:21:40 +0100 +author: DirtyF +version: 3.7.0 +category: release +--- + +We're happy to release a new minor for the new year. +Here are a few of the latest additions from our contributors: + + * LiveReload is available as an option during development: with `jekyll serve --livereload` no more manual page refresh. A big thanks to @awood for this feature and to @andreyvit, LiveReload author. + * New `collections_dir` configuration option allows you to store all your [collections](/docs/collections/) in a single folder. Your source root folder should now look cleaner :sparkles: . + * If you're using a [gem-based theme](/docs/themes/) in coordination with the `--incremental` option, you should notice some significant speed during the regeneration process, we did see build time went down **from 12s to 2s** with @mmistakes [minimal-mistakes theme](https://github.com/mmistakes/minimal-mistakes) during our tests. + * Jekyll will now check to determine whether host machine has internet connection. + * A new `latin` option is available to better [handle URLs slugs](/docs/liquid/filters/#options-for-the-slugify-filter). + * And of course many bug fixes and updates to our documentation — which you can now search thanks to our friends @Algolia. + * [Full history is here](/docs/history/#v3-7-0). + +This release wouldn't have been possible without all the following people: + +Aaron Borden, Alex Tsui, Alex Wood, Alexey Pelykh, Andrew Dassonville, Angelika Tyborska, Ankit Singhaniya, Ashwin Maroli, bellvat, Brandon Dusseau, Chris Finazzo, Doug Beney, Dr. Wolfram Schroers, Edward Shen, Florian Thomas, Frank Taillandier, Gert-jan Theunissen, Goulven Champenois, János Rusiczki, Jed Fox, Johannes Müller, Jon Anning, Jonathan Hooper, Jordon Bedwell, Junko Suzuki, Kacper Duras, Kenton Hansen, Kewin Dousse, Matt Rogers, Maximiliano Kotvinsky, mrHoliday, Olivia, Parker Moore, Pat Hawks, Sebastian Kulig, Vishesh Ruparelia, Xiaoiver and Yashu Mittal. + +A big thanks to everyone! + +Oh, one last thing… + +### :pray: upgrade your Ruby + +Prepare for the next major update, as next major version Jekyll 4.0 will drop support for Ruby 2.1 and 2.2. + +> Ruby 2.2 is now under the state of the security maintenance phase, until the end of the March of 2018. After the date, maintenance of Ruby 2.2 will be ended. We recommend you start planning migration to newer versions of Ruby, such as 2.4 or 2.3. — [Ruby Core Team](https://www.ruby-lang.org/en/news/2017/12/14/ruby-2-2-9-released/) + +We strongly encourage you to upgrade to at least Ruby 2.4.x [like our friends at GitHub Pages](https://pages.github.com/versions/) or even go with [Ruby 2.5](https://www.ruby-lang.org/en/news/2017/12/25/ruby-2-5-0-released/). + +Happy new year to all from the Jekyll team! diff --git a/docs/_posts/2018-01-25-jekyll-3-7-2-released.md b/docs/_posts/2018-01-25-jekyll-3-7-2-released.md new file mode 100644 index 0000000..04d6bbc --- /dev/null +++ b/docs/_posts/2018-01-25-jekyll-3-7-2-released.md @@ -0,0 +1,67 @@ +--- +title: "Jekyll 3.7.2 Released" +date: 2018-01-25 22:22:22 +0530 +author: ashmaroli +version: 3.7.2 +category: release +--- + +Close on the heels of shipping 3.7.0, we were informed of a couple of +regressions due to the changes made in that release. In due time, Team Jekyll +set out to address those issues as early as possible. + +Days later here we're, announcing 3.7.2 (sorry for skipping 3.7.1, +RubyGems didn't want to play nice) that fixes numerous issues! :tada: +The highlights being: + + * A major regression in 3.7.0 was that when a Front Matter Default was + configured with a `scope["path"]` set to a directory, Jekyll would scan + that directory for any subfolders and files, for each document in that + `path`. + Though this is intended, it increases build times in proportion to the size + of the directory. + + We addressed this by having Jekyll scan the directory path only if the user + explicitly configures the `scope["path"]` using wildcards. + + Read our [documentation](/docs/configuration/front-matter-defaults/#glob-patterns-in-front-matter-defaults) + for more details. + + A huge shout-out to @mmistakes for bringing this to our notice and + additionally providing us with a test repository to aid in resolving the issue. + + * Another regression reported was related to our "Custom collections + directory" feature introduced in 3.7.0. + + Users setting `collection_dir` to a certain directory would have *altered* + paths to their posts still at the root of their site's source. This + roughly translated to 404 errors on URLs to their posts. + + Props to @localheinz for bringing this regression to our notice. + + We decided to resolve this by having Jekyll ignore posts and drafts at the + root of the site's source directory if the user customizes the + `collection_dir` setting. + + Ergo, if you set a custom location for your collections, please ensure you + move all of your collections into that directory. **This includes posts and + drafts as well**. Your links generated by + {% raw %}`{% post_url %}`{% endraw %} or {% raw %}`{% link %}`{% endraw %} + will adapt automatically. + + * We also found out that `gem "wdm"` boosts performance while directories are + being watched on Windows. So we recommend having it included in your Gemfile + for a better development experience on Windows. (Newly generated Gemfiles + will hereafter have that gem listed automatically :wink:) + +In addition to the above, numerous other minor fixes and documentation updates +have been made that should improve your Jekyll experience. All of which, would +not have been possible without our wonderful contributors: + +Alexandr, Andreas Möller, Ashwin Maroli, Chayoung You, Florian Thomas, +Frank Taillandier, Hendrik Schneider, Kacper Duras, Olivia, Parker Moore and +Paul Robert Lloyd. + +As always, you can see our full changelog on [the History page](/docs/history/). + +Happy Jekylling! :sparkles: diff --git a/docs/_posts/2018-02-19-meet-jekyll-s-new-lead-developer.markdown b/docs/_posts/2018-02-19-meet-jekyll-s-new-lead-developer.markdown new file mode 100644 index 0000000..1e229fa --- /dev/null +++ b/docs/_posts/2018-02-19-meet-jekyll-s-new-lead-developer.markdown @@ -0,0 +1,43 @@ +--- +layout: news_item +title: "Meet Jekyll's New Lead Developer" +date: "2018-02-19 20:48:09 -0500" +author: parkr +categories: [team] +--- + +Jekyll has a new Lead Developer: Olivia! + +After over 5 years of leading Jekyll, many releases from 0.12.1 to 3.6.0, and +countless conversations in GitHub Issues, Pull Requests, Jekyll Talk, and +more, I am passing on the torch as Lead Developer of Jekyll. + +Olivia has been working with the Jekyll community for some time now. You +may have seen her around in issues and pull requests on the various Jekyll +repositories. She started as a contributor, then joined the Core team as our +community lead. Olivia joined the Jekyll Core Team with experience in the +Node.js community, both online and as a volunteer organizer with JSConf EU. + +In my conversations with Olivia, it is clear that Jekyll's vision of +simplicity for the user ([no magic!](/philosophy/#1-no-magic)) and letting +users' [content be king](/philosophy/#3-content-is-king) will remain a top +priority. In just the last few weeks as the transition has been occurring, +we have seen some incredible work on performance that will make future +versions of Jekyll work better at scale. She will be prioritizing work on +innovative improvements to make Jekyll that much better for all of us. +Olivia balances an eye for quality with the need for shipping well. + +When Tom Preston-Werner met me at GitHub HQ 2.0 in January 2013 to pass on +the torch, I could never have dreamed of all the amazing experiences this +community would share with me over the next 5 years. From visiting @qrush +in Buffalo, NY for a hack night on Jekyll to attending a Jekyll planning +session hosted by @benbalter at GitHub to Google Summer of Code which gave +us jekyll-admin, I am eternally grateful to all of you for the opportunity +to lead this excellent community. I'm confident Olivia will continue to +lead Jekyll to even greater heights. + +As always, Happy Jekylling! + +Parker + +*Curious about who else runs this show? [Check out our excellent team.](/team/)* diff --git a/docs/_posts/2018-02-25-jekyll-3-7-3-released.markdown b/docs/_posts/2018-02-25-jekyll-3-7-3-released.markdown new file mode 100644 index 0000000..d66afd6 --- /dev/null +++ b/docs/_posts/2018-02-25-jekyll-3-7-3-released.markdown @@ -0,0 +1,19 @@ +--- +title: 'Jekyll 3.7.3 Released' +date: 2018-02-25 13:02:08 +0530 +author: ashmaroli +version: 3.7.3 +category: release +--- + +Hello Jekyllers!! :wave: + +We're pleased to announce the release of `v3.7.3` which fixes a bug one might encounter while using `Jekyll - 3.7.x` along with a +Jekyll plugin that in turn uses the `I18n` library. + +When [v3.7.0]({% link _posts/2018-01-02-jekyll-3-7-0-released.md %}) enhanced our `slugify` filter with a `latin` option, we also +hardcoded a default fallback locale for the `I18n` library to avoid an exception raised in the event the library fails to find +any locale. This led to issues with third-party i18n plugins for Jekyll, especially since the default locale got assigned before +the plugin was loaded, irrespective of whether the `slugify` filter was used. + +Jekyll will henceforth set the default locale if and only if necessary. diff --git a/docs/_posts/2018-03-14-development-update.md b/docs/_posts/2018-03-14-development-update.md new file mode 100644 index 0000000..f0eb1c5 --- /dev/null +++ b/docs/_posts/2018-03-14-development-update.md @@ -0,0 +1,28 @@ +--- +title: "Jekyll 4.0 is on the Horizon!" +date: "2018-04-19 16:07:00 +0100" +author: oe +categories: [community] +--- + +With the release of Jekyll 3.8.0, it's been 2 and a half years since the last major release. Jekyll 3.0.0 was released in late October of 2015! That's a long time ago, and we've been working towards the next major release of Jekyll for a couple of months now. Here's a small preview of what's to come: + +- Dropping support for Ruby 2.1 and 2.2. Both versions have reached their EOL period. +- Dropping Pygments as a dependency. We're already defaulting to Rouge, and this removes the implicit Python dependency. (finally!) +- Making the `link` tag use relative URLs. This is a big breaking change, but it's the cleaner solution. + +We're open to more ideas, though. If the development cost isn't too high, or if someone volunteers to take care of the implementation, it's likely that your suggestion might make it into Jekyll 4.0. Head over to this [issue] for more details. Some interesting topics might be improving Internationalization support in Jekyll, creating convenience Liquid tags, et cetera. + +That being said, the development period of version 4.0 begins _now_. This means a couple of things: + +- New features will only be implemented in Jekyll 4.0. There will be no 3.9.0 or the like. +- Same with bug fixes, unless they concern something introduced in Jekyll 3.7 or 3.8, in which case we will backport the fixes and release a patch version. +- Now is a great time to finally take on the feature you've wanted to see in Jekyll for ages! Just open an issue or experiment with the code to get going! + +As for a release date, we're currently aiming for late summer, around September or so. However, keep in mind that this project is purely volunteer-run, and as such, delays might occur and we might not hit that release date. + +Finally, this is a great time for newcomers to open-source to make their first contribution. We'll be doing our best to mark recommended contributions and create newcomer-friendly issues, as well as to provide mentoring throughout the contribution process (although we'd like to think that we're already pretty proficient at that). So if you've always been hesitant about contributing to a large open-source project, Jekyll is a good place to start! + +Happy Jekylling! :wave: + +[issue]: https://github.com/jekyll/jekyll/issues/6948 diff --git a/docs/_posts/2018-03-15-jekyll-3-8-0-released.markdown b/docs/_posts/2018-03-15-jekyll-3-8-0-released.markdown new file mode 100644 index 0000000..14e2d61 --- /dev/null +++ b/docs/_posts/2018-03-15-jekyll-3-8-0-released.markdown @@ -0,0 +1,43 @@ +--- +title: 'Jekyll 3.8.0 Released' +date: 2018-04-19 19:45:15 +0530 +author: ashmaroli +version: 3.8.0 +category: release +--- + +Aloha Jekyllers!! :wave: + +After months of toiling on the codebase and shipping a couple of release-candidates, the Jekyll Team is delighted to finally +present `v3.8.0`, packed with optimizations, improvements, some new features and a couple of bug-fixes. Yay!!! + +Under the hood, Jekyll has undergone many minor changes that will allow it to run more performantly in the coming years. :smiley: +Rest assured, our users should see minor improvements in their site's build times. + +Speaking of improvements, users running a site containing a huge amount of posts or those who like to use our `where` filter +frequently in a single template, are going to see a massive reduction in their total build times!! :tada: + +Hold on, this version is not just about optimizations, there are some new features as well..: + * Detect non-existent variables and filters specified in a template by enabling `strict_variables` and `strict_filters` under the + `liquid` key in your config file. + * Allow *date filters* to output ordinal days. + * `jekyll doctor` now warns you if you have opted for custom `collections_dir` but placed `_posts` directory outside that + directory. + +..and yes, a couple of bug-fixes, notably: + * Jekyll now handles future-dated documents properly. + * Jekyll is able to handle Liquid blocks intelligently in excerpts. + * A few methods that were *not meant to be publically accessible* have been entombed properly. + * A few bugs that still plagued our `collections_dir` feature from `v3.7` got crushed. + +As always, the full list of changes since last release can be viewed [here](/docs/history/#v3-8-0). + +A big thanks to the following people who contributed to our repository with pull-requests that improved our codebase, documentation +and tests: + +Ana María Martínez Gómez, Antonio Argote, Ashwin Maroli, Awjin Ahn, Ben Balter, Benjamin Høegh, Christian Oliff, Damien Solodow, +David Zhang, Delson Lima, Eric Cornelissen, Florian Thomas, Frank Taillandier, Heinrich Hartmann, Jakob Vad Nielsen, John Eismeier, +Kacper Duras, KajMagnus, Mario Cekic, Max Vilimpoc, Michael H, Mike Kasberg, Parker Moore, Pat Hawks, Paweł Kuna, Robert Riemann, +Roger Rohrbach, Semen Zhydenko, Stefan Dellmuth, Tim Carry, olivia, and steelman. + +Happy Jekylling!! :sparkles: diff --git a/docs/_posts/2018-05-01-jekyll-3-8-1-released.markdown b/docs/_posts/2018-05-01-jekyll-3-8-1-released.markdown new file mode 100644 index 0000000..cb8c7a5 --- /dev/null +++ b/docs/_posts/2018-05-01-jekyll-3-8-1-released.markdown @@ -0,0 +1,20 @@ +--- +title: 'Jekyll 3.8.1 Released' +date: 2018-05-01 11:56:01 -0500 +author: pathawks +version: 3.8.1 +category: release +--- + +Happy May Day :tada: + +The Jekyll team is happy to announce the release of `v3.8.1`, which fixes +a couple of bugs that were introduced two weeks ago in `v3.8.0`. If you have +experienced trouble regarding post excerpts or non-published posts, this release +should be the remedy. Thanks to @Chaosed0 and @domLocalHeroes for originally +reporting these issues, and to @ashmaroli for fixing them so quickly. + +As a reminder, we have started work on Jekyll 4.0. If there are any +features that you would love to see added to Jekyll, or any pain points you +would like to see removed, please do add your ideas to the [Jekyll 4.0 idea +list](https://github.com/jekyll/jekyll/issues/6948). diff --git a/docs/_posts/2018-05-18-jekyll-3-8-2-released.markdown b/docs/_posts/2018-05-18-jekyll-3-8-2-released.markdown new file mode 100644 index 0000000..d54a982 --- /dev/null +++ b/docs/_posts/2018-05-18-jekyll-3-8-2-released.markdown @@ -0,0 +1,19 @@ +--- +title: 'Jekyll 3.8.2 Released' +date: 2018-05-19 10:30:00 -0500 +author: pathawks +version: 3.8.2 +category: release +--- + +Hello Jekyllers!! + +Today we are releasing `v3.8.2`, which fixes the way Jekyll generates excerpts +for posts when the first paragraph of the post contains Liquid tags that take +advantage of [Liquid's whitespace control feature][Liquid whitespace]. + +Big thanks to @kylebarbour, who first reported this issue and also very quickly +submitted a fix. Also thanks to @nickskalkin for making sure that we are using +the latest version of Rubocop to lint our code. + +[Liquid whitespace]: https://shopify.github.io/liquid/basics/whitespace/ diff --git a/docs/_posts/2018-06-04-jekyll-3-8-3-released.markdown b/docs/_posts/2018-06-04-jekyll-3-8-3-released.markdown new file mode 100644 index 0000000..a2a7056 --- /dev/null +++ b/docs/_posts/2018-06-04-jekyll-3-8-3-released.markdown @@ -0,0 +1,13 @@ +--- +title: 'Jekyll 3.8.3 Released' +date: 2018-06-05 09:00:00 -0500 +author: pathawks +version: 3.8.3 +category: release +--- + +This release fixes a regression in 3.8 where collections with `published: false` +do not show when using the `--unpublished` flag. + +Thanks to @philipbelesky for reporting and fixing this issue; collections with +`published: false` now behave the same way as Posts. diff --git a/docs/_posts/2018-08-01-jekyll-sponsoring.markdown b/docs/_posts/2018-08-01-jekyll-sponsoring.markdown new file mode 100644 index 0000000..1f223df --- /dev/null +++ b/docs/_posts/2018-08-01-jekyll-sponsoring.markdown @@ -0,0 +1,78 @@ +--- +title: "Sponsoring Jekyll's development" +date: 2018-08-01 15:00:00 +0200 +author: oe +categories: [community] +--- + +_(TL;DR: We're open for sponsorships on our [OpenCollective page](https://opencollective.com/jekyll))_ + +Hi Jekyllers, + +As you may know, Jekyll is a completely free and open source project. We offer +our software and its related plugins and documentation at no cost because we +believe that good software should not cost anything. We're not planning on +changing that, but today I want to talk about a different monetary aspect of +open source. + +Open source developers being paid for the work they do is a rare sight. Most +open source software is effectively the result of hundreds and thousands of +hours of free labor provided by individuals who are passionate enough to work +outside of their day job to create software that, ironically, is being used by +almost every company that offers digital services. It's a problem that has +gotten more attention in recent years, with the open source community becoming +more diverse and more and more companies actively investing in providing +monetary support for open source developers. + +Jekyll has always been a product of volunteers. Rarely has someone been paid to +implement a certain plugin or feature. Today, we're excited to announce that we +will finally be able to fund our contributors! __We are opening an +OpenCollective to receive individual and corporate sponsorships__. +This is not unheard of, [Hugo](http://gohugo.io) is also funded by sponsorships, +as are many other similar projects, such as +[webpack](https://opencollective.com/webpack), +[Babel](https://opencollective.com/babel) or +[RuboCop](https://opencollective.com/rubocop). + +OpenCollective is a service that makes it easy for open source projects to +receive funding from individuals and companies alike. It's specifically designed +for open source and many other projects already use it for funding. + +Sponsoring is, for us, a method to finally realize some of the more ambitious +goals we've had with the project for years. The closest thing we want to realize +is to __release Jekyll 4.0, and to make it as polished as we can__. In the +future, we would also like to work on other things that will improve the Jekyll +ecosystem. Here's a couple of ideas: + +- Create a comprehensive official plugin and theme directory site +- Improve tooling built around measuring and improving Jekyll's performance +- Improve maintenance for official plugins +- Including the community into official decisions; making Jekyll more friendly to folks in the community + +Again, these are just some ideas, but with the help of sponsoring, they are now +one step closer to being realized :heart: + +<div align="center" style="background-color: white;padding: 1em;"> + <a href="https://forestry/io"><img src="/img/forestry-logo.svg" alt="Forestry" /></a> +</div> + +With that, we would like to announce our very first sponsor: +[__Forestry.io__](https://forestry.io)! +Forestry is a CMS that integrates with your Jekyll sites and lets you update +content using a beautiful interface, and then automatically commits it back to +your GitHub repository. We're excited to have them on board on a new, exciting +step of our journey. + +Will anything change for Jekyll users? The answer is no - this step does not +impact the Jekyll software in any aspect. In fact, you might see positive +changes, such as more features and better performance. Surprisingly, that's what +happens when you properly fund people for their work! + +If you have been a long time user for Jekyll and would like to give something +back to the project, you can consider a small monthly donation to our +[OpenCollective page](http://opencollective.com/jekyll). If your company heavily +relies on Jekyll, do consider sponsoring us! + +Contact [matt@jekyllrb.com](mailto:matt@jekyllrb.com) and we'll figure something out together. + +Thanks for sticking with us, and happy Jekylling! :tada: diff --git a/docs/_posts/2018-09-19-security-fixes-for-3-6-3-7-3-8.markdown b/docs/_posts/2018-09-19-security-fixes-for-3-6-3-7-3-8.markdown new file mode 100644 index 0000000..9f92e80 --- /dev/null +++ b/docs/_posts/2018-09-19-security-fixes-for-3-6-3-7-3-8.markdown @@ -0,0 +1,26 @@ +--- +title: "Security Fixes for series 3.6, 3.7 and 3.8" +date: 2018-09-19 18:00:00 +0530 +author: ashmaroli +category: release +version: 3.8.4 +--- + +Hi Jekyllers, + +We have patched a **critical vulnerability** reported to GitHub a couple of weeks ago and have released a set of new gems to +bring that patch to you. The vulnerability allowed arbitrary file reads with the cunning use of the `include:` setting in the +config file. + +By simply including a symlink in the `include` array allowed the symlinked file to be read into the build when they shouldn't +actually be read in any circumstance. +Further details regarding the patch can be viewed at the [pull request URL]({{ site.repository }}/pull/7224) + +The patch has been released as versions `3.6.3`, `3.7.4` and `3.8.4`. +Thanks to @parkr `v3.7.4` was released a couple of weeks prior and has been bundled with `github-pages-v192`. + + +Please keep in mind that this issue affects _all previously released Jekyll versions_. If you have not had +a good reason to upgrade to `3.6`, `3.7` or `3.8` yet, we advise that you do so at the earliest. + +As always, Happy Jekylling! :sparkles: diff --git a/docs/_posts/2018-11-04-jekyll-3-8-5-released.markdown b/docs/_posts/2018-11-04-jekyll-3-8-5-released.markdown new file mode 100644 index 0000000..c854051 --- /dev/null +++ b/docs/_posts/2018-11-04-jekyll-3-8-5-released.markdown @@ -0,0 +1,16 @@ +--- +title: 'Jekyll 3.8.5 Released' +date: 2018-11-04 20:58:20 +0100 +author: oe +version: 3.8.5 +category: release +--- + +This release fixes a bug where multiple Liquid tags were not supported in +excerpts. + +Thanks to @ashmaroli for fixing this issue in [#7250]. + +Happy Jekylling! + +[#7250]: https://github.com/jekyll/jekyll/pull/7250 diff --git a/docs/_posts/2019-03-18-jekyll-4-0-0-pre-alpha1-released.markdown b/docs/_posts/2019-03-18-jekyll-4-0-0-pre-alpha1-released.markdown new file mode 100644 index 0000000..ffa900a --- /dev/null +++ b/docs/_posts/2019-03-18-jekyll-4-0-0-pre-alpha1-released.markdown @@ -0,0 +1,42 @@ +--- +title: Jekyll 4.0.0.pre.alpha1 Released +date: 2019-03-18 18:17:31 +0100 +author: dirtyf +version: 4.0.0.pre.alpha1 +category: release +--- + +Dear Jekyllers, + +Time has come to release a first alpha for Jekyll 4! + +This pre version fixes many bugs, and should improve your build times. Some of you already shared [really](https://forestry.io/blog/how-i-reduced-my-jekyll-build-time-by-61/) [good](https://boris.schapira.dev/2018/11/jekyll-build-optimization/) results. We hope your Jekyll sites will also benefit from these optimizations. + +If you're a plugin developer, we definitely need your feedback, especially if your plugin does not work with v4. + +Jekyll now exposes a [caching API](/tutorials/cache-api/), that some plugins could benefit from. + +Also be aware that it's a new *major* version, and it comes with a few breaking changes, notably : + +1. We dropped support for [Ruby 2.3 who goes EOL at the end of the month](https://www.ruby-lang.org/en/downloads/). + GitHub Pages runs Ruby 2.5.x, services like Netlify or Forestry already upgraded to latest Ruby 2.6.x. +2. `link` tag now include `relative_url` filter, hurray [no more need to prepend `{% raw %}{{ site.baseurl }}{% endraw %}` ](https://github.com/jekyll/jekyll/pull/6727). +3. [`{% raw %}{% highlight %}{% endraw %}` now behaves like `{% raw %}{% raw %}{% endraw %}`](https://github.com/jekyll/jekyll/pull/6821), so you can no longer use `include` tags within. +4. We dropped support for Pygments, RedCarpet and rdiscount. +5. We bumped kramdown to v2. + +Checkout the complete [changelog](https://github.com/jekyll/jekyll/releases/tag/v4.0.0.pre.alpha1) for more details. + +To test this pre version run: + +```sh +gem install jekyll --pre +``` + +Please test this version thoroughly and file bugs as you encounter them. + +Thanks to our dear contributors for helping making Jekyll better: + +Aidan Fitzgerald, Akshat Kedia, Alex Wood, Alexey Kopytko, Alexey Pelykh, Ali Thompson, Ana María Martínez Gómez, Ananthakumar, Andreas Möller, Andrew Lyndem, Andy Alt, Anne Gentle, Anny, Arjun Thakur, Arthur Attwell, Ashwin Maroli, Behrang, Belhassen Chelbi, Ben Keith, Ben Otte, Bilawal Hameed, Boris Schapira, Boris van Hoytema, Brett C, Chris Finazzo, Christian Oliff, Damien Solodow, Dan Allen, Dan Friedman, Daniel Höpfl, David J. Malan, Denis McDonald, Derek Smart, Derpy, Dusty Candland, ExE Boss, Frank Taillandier, Gareth Cooper, Grzegorz Kaczorek, Isaac Goodman, Jacob Byers, Jakob Krigovsky, Jan Pobořil, Joe Shannon, Jordan Morgan, Jorie Tappa, Josue Caraballo, Justin Vallelonga, Jörg Steinsträter, Karel Bílek, Keith Mifsud, Kelly-Ann Green, Ken Salomon, Kevin Plattret, Kyle Barbour, Lars Kanis, Leandro Facchinetti, Luis Enrique Perez Alvarez, Luis Guillermo Yáñez, Ma HongJun, Manu Mathew, Mario, Martin Scharm, Matt Massicotte, Matthew Rathbone, Maxwell Gerber, Mertcan Yücel, Michael Hiiva, Mike Kasberg, Mike Neumegen, Monica Powell, Nicolas Hoizey, Nikhil Swaminathan, Nikita Skalkin, Olivia Hugger, Parker Moore, Pat Hawks, Patrick Favre-Bulle, Paul Kim, Philip Belesky, Preston Lim, Ralph, Robert Riemann, Rosário Pereira Fernandes, Samuel Gruetter, Scott Killen, Sri Pravan Paturi, Stephan Fischer, Stephen Weiss, Steven Westmoreland, Sundaram Kalyan Vedala, Thanos Kolovos, Timo Schuhmacher, Tobias, Tom Harvey, Tushar Prajapati, Victor Afanasev, Vitor Oliveira, Wouter Schoot, XhmikosR, Zhang Xiangze, _94gsc, argv-minus-one, chrisfinazzo, ikeji, jess, jpasholk, makmm, mo khan, ninevra, penguinpet, 김정환, 104fps + +Happy Jekylling everyone! diff --git a/docs/_posts/2019-07-02-jekyll-3-8-6-released.markdown b/docs/_posts/2019-07-02-jekyll-3-8-6-released.markdown new file mode 100644 index 0000000..f05cd7c --- /dev/null +++ b/docs/_posts/2019-07-02-jekyll-3-8-6-released.markdown @@ -0,0 +1,19 @@ +--- +title: 'Jekyll 3.8.6 Released' +date: 2019-07-02 11:21:02 -0400 +author: parkr +version: 3.8.6 +categories: [release] +--- + +We have another patch release in the 3.8 series! This time, we have one security patch +and a handful of bug patches, including: + +- Filter symlinks from theme gems +- Fix excerpt handling of some Liquid tags +- Handle case where a theme directory doesn't exist +- A few internal optimizations to reduce memory overhead + +... and a few more! You can check out the patches and see all the details in [the release notes](/docs/history/#v3-8-6) + +Happy Jekylling! diff --git a/docs/_posts/2019-08-04-jekyll-4-0-0-pre-beta1-released.markdown b/docs/_posts/2019-08-04-jekyll-4-0-0-pre-beta1-released.markdown new file mode 100644 index 0000000..64f1697 --- /dev/null +++ b/docs/_posts/2019-08-04-jekyll-4-0-0-pre-beta1-released.markdown @@ -0,0 +1,41 @@ +--- +title: Jekyll 4.0.0.pre.beta1 Released +date: 2019-08-04 10:43:31 -0500 +author: mattr- +version: 4.0.0.pre.beta1 +categories: [release] +redirect_from: /news/2019/07/20/jekyll-4-0-0-pre-beta1-released/ +--- + +Dear Jekyllers, + +It's time for another pre-release of Jekyll 4! 🎉 + +This pre-release moves us further down the path of releasing Jekyll 4.0.0. All the same goodies [from the last pre-release](/news/2019/03/18/jekyll-4-0-0-pre-alpha1-released/) are here, along with a few more things I want to highlight: + +Jekyll 4.0 is a new *major* version and it comes with a few breaking changes, notably : + +1. We dropped support for [Ruby 2.3 which EOL at the end of March 2019](https://www.ruby-lang.org/en/downloads/). + GitHub Pages runs Ruby 2.5.x, services like Netlify or Forestry already upgraded to latest Ruby 2.6.x. +2. `link` tag now include `relative_url` filter, hurray [no more need to prepend `{% raw %}{{ site.baseurl }}{% endraw %}` ](https://github.com/jekyll/jekyll/pull/6727). +3. [`{% raw %}{% highlight %}{% endraw %}` now behaves like `{% raw %}{% raw %}{% endraw %}`](https://github.com/jekyll/jekyll/pull/6821), so you can no longer use `include` tags within. +4. We dropped support for Pygments, RedCarpet and rdiscount. +5. We bumped kramdown to v2. + +If you're a plugin developer, we still need your feedback! Your plugin may not work with version 4 and we'd like to fix those issues before we release. + +Checkout the complete [changelog](https://github.com/jekyll/jekyll/releases/tag/v4.0.0.pre.beta1) for more details. + +To test this pre version run: + +```sh +gem install jekyll --pre +``` + +Please test this version thoroughly and file bugs as you encounter them. + +Thanks to our dear contributors for helping making Jekyll better: + +Aidan Fitzgerald, Akshat Kedia, Alex Wood, Alexey Kopytko, Alexey Pelykh, Ali Thompson, Ana María Martínez Gómez, Ananthakumar, Andreas Möller, Andrew Lyndem, Andy Alt, Anne Gentle, Anny, Arjun Thakur, Arthur Attwell, Ashwin Maroli, Behrang, Belhassen Chelbi, Ben Keith, Ben Otte, Bilawal Hameed, Boris Schapira, Boris van Hoytema, Brett C, Chris Finazzo, Christian Oliff, Damien Solodow, Dan Allen, Dan Friedman, Daniel Höpfl, David J. Malan, Denis McDonald, Derek Smart, Derpy, Dusty Candland, ExE Boss, Frank Taillandier, Gareth Cooper, Grzegorz Kaczorek, Isaac Goodman, Jacob Byers, Jakob Krigovsky, Jan Pobořil, Joe Shannon, Jordan Morgan, Jorie Tappa, Josue Caraballo, Justin Vallelonga, Jörg Steinsträter, Karel Bílek, Keith Mifsud, Kelly-Ann Green, Ken Salomon, Kevin Plattret, Kyle Barbour, Lars Kanis, Leandro Facchinetti, Luis Enrique Perez Alvarez, Luis Guillermo Yáñez, Ma HongJun, Manu Mathew, Mario, Martin Scharm, Matt Massicotte, Matthew Rathbone, Maxwell Gerber, Mertcan Yücel, Michael Hiiva, Mike Kasberg, Mike Neumegen, Monica Powell, Nicolas Hoizey, Nikhil Swaminathan, Nikita Skalkin, Olivia Hugger, Parker Moore, Pat Hawks, Patrick Favre-Bulle, Paul Kim, Philip Belesky, Preston Lim, Ralph, Robert Riemann, Rosário Pereira Fernandes, Samuel Gruetter, Scott Killen, Sri Pravan Paturi, Stephan Fischer, Stephen Weiss, Steven Westmoreland, Sundaram Kalyan Vedala, Thanos Kolovos, Timo Schuhmacher, Tobias, Tom Harvey, Tushar Prajapati, Victor Afanasev, Vitor Oliveira, Wouter Schoot, XhmikosR, Zhang Xiangze, _94gsc, argv-minus-one, chrisfinazzo, ikeji, jess, jpasholk, makmm, mo khan, ninevra, penguinpet, 김정환, 104fps + +Happy Jekylling everyone! diff --git a/docs/_posts/2019-08-19-jekyll-4-0-0-released.markdown b/docs/_posts/2019-08-19-jekyll-4-0-0-released.markdown new file mode 100644 index 0000000..dec7401 --- /dev/null +++ b/docs/_posts/2019-08-19-jekyll-4-0-0-released.markdown @@ -0,0 +1,139 @@ +--- +title: 'Jekyll 4.0.0 Released' +date: 2019-08-20 10:00:00 -0500 +author: mattr- +version: 4.0.0 +category: release +--- + +Hi! 👋 I bring some good news! Jekyll 4.0.0 is finally here! 🎉 + +There's quite a bit in this release to unpack, so let me hit the high points quickly: + - Ruby 2.4.0 or greater is now required. + - Rouge 3.0 or greater is now required for syntax highlighting. + - Jekyll builds should be much faster. + - Kramdown 2.1 is now the default markdown engine. + - Sass processing should be faster. + - We dropped support for a lot of stuff, specifically: + - Pygments + - RedCarpet + - RDiscount + + +Alright, so with the high points out of the way, let's get into the details a little bit. + +### Cache all the things! 💰 + +While some optimizations first made an appearance with Jekyll 3.8.0, Jekyll 4.0 takes +it to another level altogether. + +Jekyll 4.0 caches the processing done by Liquid in memory. So every Liquid +template is processed only as required. If you have 10 pages depending on a +single layout, the layout is cached and that data is then rendered as per the +10 different contexts of the individual files. + +There's also a disk cache! Jekyll can now cache data to disk to avoid repeated +processing of content that doesn't change between build sessions. Currently, +this is limited to markdown. So while the very first build will take a certain +amount of time, consequent builds for content that hasn't changed will take +much less time due to the disk-cache. Disk caching is disabled for `safe` mode, +however. + +### Super-powered content transformations 💪 + +We've upgraded Sass support so it should be faster. There's also +support for sourcemaps now! Under the hood, our Sass support uses the `SassC` +library now, which is supported directly by the Sass team, which should mean +better support for everybody in the long run. + +Kramdown is updated to version 2.1. This also brings with it a bunch of changes +to the Kramdown configuration, as the Kramdown team have extracted a fair +number of features into separate gems. Support for GitHub Flavored Markdown is +enabled by default, but if you're using another Kramdown extension in your +site, you'll likely need to update your plugin configuration. See the [upgrade +guide](/docs/upgrading/3-to-4/) for more details. + +The `link` and `post_url` tags no longer need `site.baseurl` prepended every +time they're used. Those tags now use our `relative_url` filter to take care of +this for you. Existing uses of the prepending pattern will break though! +Sorry! :sweat_smile: + +A few other smaller features when it comes to content: + - The `link` tag understands Liquid variables in the same fashion our + `include` tag does now. + - Disable Liquid processing for a particular page / document by adding + `render_with_liquid: false` to its front matter. + - Liquid's binary `and` and `or` operations can be used in the `where_exp` + filter for more powerful filtering + +There's some goodies for theme community as well. Developers may now bundle a +`config.yml` into their theme-gem to provide some boilerplate configurations for +the theme. Like other resources in the theme, these configuration values can also +be customized at the user's end. + + +Check out the [full history](/docs/history/#v4-0-0) and the various pull requests +for more details on all the enhancements and bug-fixes. + +### Upgrading 📈 + +First, read the [upgrade guide](/docs/upgrading/3-to-4/)! + +Next, Edit your project's `Gemfile` to test Jekyll v4.x: + +```ruby +gem "jekyll", "~> 4.0" +``` + +Then run `bundle update` to update all dependencies. Unless you're using +third-party plugins that haven't yet added support for Jekyll 4.0, you should be +good to go. + +Plugins and themes authors must relax the jekyll dependency in their `gemspec` file +to allow for Jekyll v4.0: + +`spec.add_runtime_dependency "jekyll", ">= 3.6", "< 5.0"` + +If your favorite plugin hasn't relaxed that dependency yet, please gently +encourage them to do so. :slightly_smiling_face: + +### Have questions❓ + +Please reach out on our [community forum](https://talk.jekyllrb.com) + + +### Thank you!! 🙇 + +Jekyll would not be possible without the many people who have taken the time to write issues, submit pull requests, create themes, answer questions for other users, or make their own sites using our project. Thanks to all of you who contribute, no matter how small you think your contribution might have been. + +In addition, special thanks to the 139 contributors who made this +release possible via a pull request submission (in alphabetical order): Aidan +Fitzgerald, Akshat Kedia, Ale Muñoz, Alex Wood, +Alexey Kopytko, Alexey Pelykh, Ali Thompson, Ana María Martínez Gómez, +Ananthakumar, Andreas Möller, Andrew Lyndem, Andrew Marcuse, Andy Alt, Anne +Gentle, Anny, Anuj Bhatnagar, argv-minus-one, Arjun Thakur, Arthur Attwell, +Ashwin Maroli, Behrang, Belhassen Chelbi, Ben Keith, Ben Otte, Bilawal Hameed, +Bjorn Krols, Boris Schapira, Boris van Hoytema, Brett C, Chris Finazzo, Chris +Oliver, chrisfinazzo, Christian Oliff, Christoph Päper, Damien Solodow, Dan +Allen, Dan Friedman, Daniel Höpfl, David J. Malan, David Kennell, David Zhang, +Denis McDonald, Derek Smart, Derpy, Dusty Candland, Edgar Tinajero, Elvio +Vicosa, ExE Boss, Fons van der Plas, Frank Taillandier, Gareth Cooper, Grzegorz +Kaczorek, Haris Bjelic, Hodong Kim, ikeji, Isaac Goodman, Jacob Byers, Jakob +Krigovsky, James Rhea, Jan Pobořil, jess, jingze_lu, Joe Shannon, Jordan Morgan, +Jörg Steinsträter, Jorie Tappa, Josue Caraballo, jpasholk, Justin Vallelonga, +Karel Bílek, Keith Mifsud, Kelly-Ann Green, Ken Salomon, Kevin Plattret, krissy, +Kyle Barbour, Lars Kanis, Leandro Facchinetti, Liam Rosenfeld, Luis Enrique +Perez Alvarez, Luis Guillermo Yáñez, Ma HongJun, makmm, Manu Mathew, Mario, +Martin Scharm, Matt Kraai, Matt Massicotte, Matt Rogers, Matthew Rathbone, +Maxwell Gerber, Mertcan Yücel, Michael Bishop, Michael Hiiva, Michelle Greer, +Mike Kasberg, Mike Neumegen, mo khan, Monica Powell, Nicolas Hoizey, Nikhil +Benesch, Nikhil Swaminathan, Nikita Skalkin, Niklas Eicker, ninevra, Olivia +Hugger, Parker Moore, Pat Hawks, Patrick Favre-Bulle, Paul Kim, penguinpet, +Philip Belesky, Preston Lim, Ralph, Robert Riemann, Rosário Pereira Fernandes, +Sadik Kuzu, Samuel Gruetter, Scott Killen, Sri Pravan Paturi, Stephan Fischer, +Stephen Weiss, Steven Westmoreland, strangehill, Sundaram Kalyan Vedala, Thanos +Kolovos, Timo Schuhmacher, Tobias, Tom Harvey, Tushar Prajapati, Victor Afanasev, +Vinicius Flores, Vitor Oliveira, Wouter Schoot, XhmikosR, Yi Feng Xie, Zhang +Xiangze, 김정환, 104fps. + +Happy Jekylling everyone! diff --git a/docs/_posts/2020-05-08-jekyll-4-0-1-released.markdown b/docs/_posts/2020-05-08-jekyll-4-0-1-released.markdown new file mode 100644 index 0000000..f0d5635 --- /dev/null +++ b/docs/_posts/2020-05-08-jekyll-4-0-1-released.markdown @@ -0,0 +1,23 @@ +--- +title: 'Jekyll 4.0.1 Released' +date: 2020-05-08 18:00:00 +0100 +author: dirtyf +version: 4.0.1 +category: release +--- + +Jekyll 4.0.1 is out and fixes a few issues reported by the community since 4.0. + +- When using Ruby 2.7, you won't get any more warnings in your console when running Jekyll. (This fix is also backported in [Jekyll 3.8.7](https://github.com/jekyll/jekyll/releases/tag/v3.8.7)). +- Liquid variables are now properly cached. +- Jekyll build will no longer fail for collections with a custom permalink containing static files. +- Jekyll filters now properly recognize integers. + +👀 A [release changelog](https://github.com/jekyll/jekyll/releases/tag/v4.0.1) is available for your perusal. + +🙏 Many thanks to our contributors without whom this release could not be +possible: @ashmaroli and [Ivan Gromov](https://github.com/summerisgone) + +Expect more fixes and improvements on the next minor release! + +Happy Jekylling! diff --git a/docs/_posts/2020-05-27-jekyll-4-1-0-released.markdown b/docs/_posts/2020-05-27-jekyll-4-1-0-released.markdown new file mode 100644 index 0000000..28b2dea --- /dev/null +++ b/docs/_posts/2020-05-27-jekyll-4-1-0-released.markdown @@ -0,0 +1,74 @@ +--- +title: 'Jekyll 4.1.0 Released' +date: 2020-05-27 15:20:30 +0530 +author: ashmaroli +version: 4.1.0 +category: release + +filters_linked_to: +- where expression +- find expression +- find +- number of words +--- + +Hello Jekyllers! + +It's time for yet another release that includes enhancements, optimizations and bug-fixes. Highlights of this release +are: + +* Jekyll now supports rendering excerpts for *pages* in addition to documents and posts. +* The [`where_exp`][where-expression-filter] filter got enhanced. Earlier, one could just use either `and` or `or` once +per expression. Now, one may use those binary operators multiple times in the filter's expression. +* Jekyll has a new set of filters based on *its flavor* of the `where` and `where_exp` filters. Named +[`find`][find-filter] and [`find_exp`][find-expression-filter] filters respectively, they work similar to their ancestors +except that they return **the first object** that satisfies the given conditions. +* Jekyll's [`number_of_words`][number-of-words-filter] filter can now take an optional argument to better count words +of text containing Chinese, Japanese or Korean characters. +* One may now use `:slugified_categories` in their permalink configurations to generate a more apt URL (categories are +downcased and non-alphanumeric characters replaced by dashes) for their for posts and documents. +* The logic for *slugifying* a given string has been enhanced to support more Unicode characters. +* If you face issues from Jekyll importing a config file bundled within a theme, you can now disable the import entirely +by setting `ignore_theme_config: true` in your site's configuration file. +* If you face issues from Jekyll's disk-caching feature, you can now disable the mechanism without opting to build in +`safe` mode, by either setting `disable_disk_cache: true` in your configuration file or by passing the CLI switch +`--disable-disk-cache` to `jekyll build` or `jekyll serve` commands. +* When you build a site with the `--profile` switch, Jekyll will now additionally output a small table showing the amount +of time taken during various stages of the *build process*. +* Jekyll's development server now supports certificates based on Elliptic-curve cryptography. + +For the interest of plugin authors: +* Excerpts won't be generated for `Jekyll::Page` subclasses automatically unless such instances have an `excerpt` key in +their `data` hash. + +For the interest of gem-based theme authors: +* From `v4.1.0` onwards, a newly generated theme workspace (via `jekyll new-theme ...`) will have the gemspec configured +to bundle a `_config.yml` at the root of the workspace. If you don't wish to include the configuration file in the +released gem, please remove `|_config\.yml` from the regular expression in the gemspec. + +{% for filter in page.filters_linked_to %} +{% assign filter_slug = filter | slugify %} +[{{ filter_slug }}-filter]: {{ filter_slug | prepend: '/docs/liquid/filters/#' | relative_url }} +{% endfor %} + +### Have questions? + +Please reach out on our [community forum](https://talk.jekyllrb.com) + + +### Thank you!! :bow: + +We are thankful to our community for all the contributions that helped shape this release. +Special thanks to the following 78 contributors (in alphabetical order) who made this release possible and took the time +to submit a pull request: + +Aaron Adams, Aaron K Redshaw, Alexandre Zanni, Anindita Basu, Arthur Zey, Artyom Tokachev, Ashwin Maroli, Atlas Cove, +Ben Stolovitz, Billy Kong, Christian Oliff, codenitpicker, csquare, Damien St Pierre, Daniel Leidert, David Zhang, +ddocs, dgolant, dkalev, Dmitry Egorov, dotnetCarpenter, Edward Thomson, Eric Knibbe, Frank Taillandier, Gabriel Rubens, +Gareth Mcshane, Grzegorz Kaczorek, guanicoe, Harry Wood, HTeuMeuLeu, iBug, İsmail Arılık, Itay Shakury, Ivan Gromov, +Ivan Raszl, J·Y, James Buckley, Jason Taylor, JC, jeffreytse, Johan Wigert, jonas-krummenacher, Justin Jia, +Kayce Basques, Kieran Barker, Leo, Liam Bigelow, lizharris, Lizzy Kate, Luis Puente, Mark Bennett, Matt Penna, +Matt Rogers, matt swanson, Max Chadwick, michaelcurrin, Mike Kasberg, Mike Neumegen, Muhammed Salih, Nikhil Benesch, +Paramdeo Singh, Patrik Eriksson, Phil Nash, Philip Eriksson, R.P. Pedraza, Radoslav Karlík, Riccardo Porreca, +sharath Kumar, Simone Arpe, Takashi Udagawa, Tobias Klüpfel, Toby Glei, vhermecz, Viktor Szakats, Ward Sandler, wzy, +XhmikosR, Zlatan Vasović. diff --git a/docs/_posts/2020-06-24-jekyll-4-1-1-released.markdown b/docs/_posts/2020-06-24-jekyll-4-1-1-released.markdown new file mode 100644 index 0000000..599578e --- /dev/null +++ b/docs/_posts/2020-06-24-jekyll-4-1-1-released.markdown @@ -0,0 +1,38 @@ +--- +title: 'Jekyll 4.1.1 Released' +date: 2020-06-24 16:45:35 +0530 +author: ashmaroli +version: 4.1.1 +category: release +--- + +Jekyll 4.1.0 brought two notable changes: *Page-excerpts* and *Liquid Drop for Page objects*. +However these seemingly benign changes had unexpected adverse side-effects which did not figure in our tests. + +The Core team decided that the best way forward is to revert introduction of the Liquid drop for Pages but push back +generating excerpts for pages behind a flag until `v5.0`. + +Page-excerpts are henceforth an opt-in experimental feature which can be enabled by setting `page_excerpts: true` in +your configuration file. Due to its experimental nature, we have narrowed the scope for page-excerpts to limit their +negative effect on builds. Excerpts will not be generated for pages that *do not* output into an HTML file even if +`page_excerpts: true` has been set in the configuration file. + +Another known issue with page-excerpts is that an infinite loop is created in certain use-cases such as any construct +that involves iterating through `site.pages` directly within a `Jekyll::Page` instance. A couple of examples would be +having a variant of either of the following code blocks inside a page source, say `index.markdown` or `about.markdown`: + +{% raw %} + +```liquid +{% for entry in site.pages %} + {{ entry.name }} +{% endfor %} +``` + +```liquid +{{ site.pages | sort: 'title' }} +``` + +{% endraw %} + +Therefore, we advise caution when opting to use the page-excerpt feature. diff --git a/docs/_posts/2020-08-05-jekyll-3-9-0-released.markdown b/docs/_posts/2020-08-05-jekyll-3-9-0-released.markdown new file mode 100644 index 0000000..f2e79e3 --- /dev/null +++ b/docs/_posts/2020-08-05-jekyll-3-9-0-released.markdown @@ -0,0 +1,28 @@ +--- +title: 'Jekyll 3.9.0 Released' +author: parkr +version: 3.9.0 +categories: [release] +--- + +Jekyll 3.9.0 allows use of kramdown v2, the latest series of kramdown. + +If you choose to upgrade, please note that the GitHub-Flavored Markdown +parser and other features of kramdown v1 are now distributed via +separate gems. If you would like to continue using these features, you will +need to add the gems to your `Gemfile`. They are as follows: + +- GFM parser – `kramdown-parser-gfm` +- coderay syntax highlighter – `kramdown-syntax-coderay` +- mathjaxnode math engine – `kramdown-math-mathjaxnode` +- sskatex math engine – `kramdown-math-sskatex` +- katex math engine – `kramdown-math-katex` +- ritex math engine – `kramdown-math-ritex` +- itex2mml math engine – `kramdown-math-itex2mml` + +Jekyll will require the given gem when the configuration requires it, and +will show a helpful message when a dependency is missing. + +You can check out the patches and see all the details in [the release notes](/docs/history/#v3-9-0) + +Happy Jekylling! diff --git a/docs/_posts/2020-12-14-jekyll-4-2-0-released.markdown b/docs/_posts/2020-12-14-jekyll-4-2-0-released.markdown new file mode 100644 index 0000000..b77ccbf --- /dev/null +++ b/docs/_posts/2020-12-14-jekyll-4-2-0-released.markdown @@ -0,0 +1,31 @@ +--- +title: "Jekyll 4.2.0 Released" +date: 2020-12-14 14:12:20 +0530 +author: ashmaroli +version: 4.2.0 +category: release +--- + +Greetings Jekyllers! Jekyll v4.2.0 is out! + +This release gives you a new hook named `:post_convert` that allows modifying rendered HTML contents before they are +placed into the designated layout(s). + +Detecting files that get written into the same destination path has been a part of the diagnostics from `jekyll doctor` +for quite some time now. However, v4.2 has integrated that feature into the build process itself. + +On the topic of log output, the `--verbose` output got a bit more verbose. Instead of just showing *documents* that are +being read, the output will now also show *pages* and *layouts* that are being read into the site. + +Additionally, we have stopped overriding the `site.url` to `http://localhost:4000` in absolute URLs while developing +via `jekyll serve`. + +As always, you can go through [the full list of changes](/docs/history/#v4-2-0) if you are interested in the various +memory-allocation optimizations made to Jekyll. + +Special thanks to our community members who helped improving Jekyll codebase and documentation from v4.1.1: +Adam Alton, Alex Malaszkiewicz, Alexey Pelykh, Brittany Joiner, bytecode1024, Christopher Brown, Chuck Houpt, +Corey Megown, Dan Nemenyi, Enrico Tolotto, fauno, Felix Breidenstein, Francesco Bianco, Frank Taillandier, +Gabriel Staples, iBug, Jacobo Vidal, jaybe@jekyll, jesuslerma, jnozsc, joelkennedy, Joe Marshall, Liam Cooke, +Lou Rectoret, Malathi, m-naumann, Nicholas Paxford, Nikita Skalkin, Parker Moore, Pratyaksh Gautam, Rachel Cheyfitz, +SaintMalik, Seeker, Shannon Kularathna, Steven Xu, Takuya N, Thelonius Kort and Toby Glei. diff --git a/docs/_posts/2021-04-08-jekyll-3-9-1-released.markdown b/docs/_posts/2021-04-08-jekyll-3-9-1-released.markdown new file mode 100644 index 0000000..6449d45 --- /dev/null +++ b/docs/_posts/2021-04-08-jekyll-3-9-1-released.markdown @@ -0,0 +1,28 @@ +--- +title: 'Jekyll 3.9.1 Released' +date: 2021-04-08 10:51:12 -0400 +author: parkr +version: 3.9.1 +categories: [release] +--- + +This patch release of the 3.9 series is released to fix a bug where the +`include` tag does not allow valid filename characters. For example, this +would previously fail: + +{% raw %} +```text +{% include my-logo@2x.svg %} +``` +{% endraw %} + +This release adds support for the following characters in filenames: + +- `@` +- `-` +- `(` and `)` +- `+` +- `~` +- `#` + +Happy Jekylling! diff --git a/docs/_posts/2021-09-14-goodbye-dear-frank.markdown b/docs/_posts/2021-09-14-goodbye-dear-frank.markdown new file mode 100644 index 0000000..60d352a --- /dev/null +++ b/docs/_posts/2021-09-14-goodbye-dear-frank.markdown @@ -0,0 +1,32 @@ +--- +title: 'Goodbye, Dear Frank.' +date: 2021-09-14 11:28:02 -0500 +author: ashmaroli +categories: [team, community] +--- + +Over the weekend, the Jekyll core team learned of the passing of one of our own: *Frank Taillandier*, popularly known +by his GitHub username @DirtyF. + +Ruby not being his forte, he chose to avoid code-level changes and instead focus on what he did best — *engage with +the community*. + +He helped resolve complaints reported on the GitHub issue tracker, ensured that Jekyll documentation remained simple for +novice users yet detailed enough for advanced users seeking additional information. + +He also served as the administrator for Jekyll's public [discourse forum](https://talk.jekyllrb.com/) where he not only +addressed queries from users and provided tips to improve Jekyll workflow, he also shared feedback on Jekyll sites +created by the community, and used the forum as a platform to gather feedback on unreleased iterations of Jekyll and +in-house plugins. + +Abreast with latest developments in the Web-verse, Frank was always quick to introduce technologies that vastly improved +maintenance in the Jekyll organization. He was instrumental in setting up deploy previews for patches to Jekyll's +documentation site and later wiring GitHub Actions to handle continuous integrations for Jekyll and in-house projects. + +In spite of spiritually moving away from Jekyll during the later part of his career, choosing to concentrate efforts on +furthering JAMstack projects, he greatly remained active on Jekyll's development channel on Slack relaying key feedback +from the community or discuss concerns regarding the future of Jekyll at length. + +Having untimely left Jekyll and our community with an unfillable void, he will be missed immensely. :broken_heart: + +Rest in Peace, friend and colleague. :bouquet: diff --git a/docs/_posts/2021-09-27-jekyll-4-2-1-released.markdown b/docs/_posts/2021-09-27-jekyll-4-2-1-released.markdown new file mode 100644 index 0000000..bb9a46c --- /dev/null +++ b/docs/_posts/2021-09-27-jekyll-4-2-1-released.markdown @@ -0,0 +1,40 @@ +--- +title: "Jekyll 4.2.1 Released" +date: 2021-09-27 14:45:46 +0530 +author: ashmaroli +version: 4.2.1 +category: release +--- + +Hello Jekyllers! + +The Jekyll team is happy to announce the release of `v4.2.1` which fixes a couple of +regressions introduced in `v4.2.0` and another bug inherited from Jekyll 3. + +In `v4.2.0`, we decided to stop overriding {% raw %}`{{ site.url }}`{% endraw %} with +the *localhost* address when running the command `jekyll serve` with the default +*development* mode. While the intent behind the change was to avoid forcing users to +generate a *production build* separately by invoking `jekyll build`, it however had an +unforeseen consequence — absolute URLs for assets now pointed to +resources that were at times not yet been deployed to the configured `site.url`. That +broke the users' local development workflow. + +`v4.2.0` also added a series of optimizations surrounding the generation of Liquid +representation for a site's standalone pages and layouts. However, that prevented +{% raw %}`{{ page.content }}`{% endraw %} and other mutable attributes from reflecting +the latest state of the requested attribute, thereby breaking the render of all resources +that were dependent on such mutable attributes. + +The last fix included in this release addresses the issue where incremental regeneration +ignored changes to documents in collections when the site is configured to use a custom +`collections_dir` for all collections. + +Special thanks to @benik for helping us understand the regression caused by the decision +to stop overriding `site.url` and proposing to revert the change. Another special thanks +to @pdmosses for helping us discover the regression surrounding Liquid representation of +pages by providing with a test repository. + +<div style="padding:8px 0 2px;text-align:center;background:rgba(240,0,0,0.1)"> + :bouquet: <span style="margin:0 6px;font-size:0.75em;vertical-align:top"> + Dedicated to our colleague Frank who passed away recently</span> :bouquet: +</div> diff --git a/docs/_posts/2022-03-03-jekyll-4-2-2-released.markdown b/docs/_posts/2022-03-03-jekyll-4-2-2-released.markdown new file mode 100644 index 0000000..d175aa2 --- /dev/null +++ b/docs/_posts/2022-03-03-jekyll-4-2-2-released.markdown @@ -0,0 +1,30 @@ +--- +title: "Jekyll 4.2.2 Released" +date: 2022-03-03 19:15:20 +0530 +author: ashmaroli +version: 4.2.2 +category: release +--- + +Hello Jekyllers! + +Jekyll 4.2.2 has been released. Unlike prior releases, this is a simple maintenance release and may be skipped. + +For those who are still curious about the current release, here is some technical context: The previous `jekyll-4.2.1` package was built and +published using a Windows system. A side-effect of that action was that every file bundled into the gem ended up with Windows-style CRLF +line-endings instead of Unix-style LF line-endings. + +For our end-users, this difference holds no significance. However, a third-party entity vendoring the release faced a roadblock. The executable +program `jekyll` apparently misplaced the executable bit because of the change in line-endings. + +To that end, the Jekyll team decided to use the GitHub Actions service to build and publish releases. In-house plugins have already published +releases via this route serving as trials. Henceforth, and unless explicitly reported, all Jekyll releases will be built on GitHub Actions' +Ubuntu platform and published to Rubygems by @jekyllbot irrespective of the maintainer overseeing the release. + +That is all for now. +Happy Jekyllin'!! + +*P.S.: Jekyll 4.3.0 will be bringing you some new features very soon.. Also, our sass-converter plugin has been [enhanced][sass-220] to support +modern improvements to Sass.* + +[sass-220]: https://github.com/jekyll/jekyll-sass-converter/tree/v2.2.0#sass-embedded diff --git a/docs/_posts/2022-03-27-jekyll-3-9-2-released.markdown b/docs/_posts/2022-03-27-jekyll-3-9-2-released.markdown new file mode 100644 index 0000000..5ea445d --- /dev/null +++ b/docs/_posts/2022-03-27-jekyll-3-9-2-released.markdown @@ -0,0 +1,19 @@ +--- +title: 'Jekyll 3.9.2 Released' +date: 2022-03-27 13:20:00 -0700 +author: parkr +version: 3.9.2 +categories: [release] +--- + +Hey Jekyllers, + +Quick bug-fix release for you all today: + +1. Ruby 3.0 and 3.1 support :tada: (you will need to run `bundle add webrick` for `jekyll serve` to work) +2. `jekyll serve` will no longer inject a charset into the MIME type for +binary types +3. Incremental regeneration now handles includes in collection files + correctly + +That's all, Happy Jekylling! diff --git a/docs/_posts/2022-10-20-jekyll-4-3-0-released.markdown b/docs/_posts/2022-10-20-jekyll-4-3-0-released.markdown new file mode 100644 index 0000000..752e796 --- /dev/null +++ b/docs/_posts/2022-10-20-jekyll-4-3-0-released.markdown @@ -0,0 +1,88 @@ +--- +title: 'Jekyll 4.3.0 Released' +date: 2022-10-20 10:20:22 -0500 +author: ashmaroli +version: 4.3.0 +category: release +--- + +Hello Jekyllers! + +The Jekyll team is happy to announce the release of `v4.3.0` shipping with some nice improvements and bug-fixes. + +## Improvements + +### Dependencies + +- Gem `webrick` is now a listed dependency. You no longer have to add the gem to your Gemfile when using Jekyll with +Ruby 3.0 or newer. +- You may now use Rouge v4 or continue using Rouge v3.x by explicitly mentioning the version in your Gemfile. +- Support for gem `tzinfo` v2 and non-half-hour offsets have been added. +- You will be able to use v3 of `jekyll-sass-converter` when it ships. + +### Builds + +- Added support for bundling and loading data files from within a theme-gem similar to existing theme-gem contents. +- Changes to data files at source will now be respected during incremental builds. +- `site.static_files` now include static files within a collection. +- You may now configure converters for CSV data. +- `.jekyll-cache` or its equivalent custom cache directory will be automatically ignored by Git. +- Vendor the current latest mime-types dataset for use with local development server. + +{% raw %} +### Liquid Templates + +- `basename` attribute of documents are now exposed to Liquid as `name`, for example `{{ page.name }}`. Excerpts delegate +to associated document attribute. +- Top-level variable `{{ theme }}` introduced to expose gemspec details of theme-gem. (Valid only when using theme-gem) +{% endraw %} + +## Bug-fixes + +Some noteworthy bug-fixes include: + +- Respect `BUNDLE_GEMFILE` when loading Jekyll plugins via Bundler. +- Prevent loading versions older than kramdown-2.3.1 as a security measure. +- Trigger livereloading even if the site has *no pages*. +- Ensure the expected class of theme config is returned following a merger. +- Enable BOM encoding only if configured encoding is 'UTF-8'. +- Respect server protocol while injecting livereload script. +- The table output for `--profile` stops printing incorrect "TOTALS" row. + +[The full list of changes](/docs/history/#v4-3-0) may be perused if interested. + +As always, we are grateful to the many contributors that helped improve the project codebase and documentation: + +<small>Ashwin Maroli, Frank Taillandier, Matt Rogers, Parker Moore, Kelvin M. Klann, Josh Soref, Youssef Boulkaid, +Emily Grace Seville, Robert Martin, jaybe@jekyll, Ben Keith, Jonathan Darrer, Kaben, Mike Kasberg, Moncef Belyamani, +Phil Ross, Sesh Sadasivam, Adam Bell, Alaz Tetik, Alex Malaszkiewicz, Alex Saveau, Andreas Deininger, Andrew Davis, +Andrew Gutekanst, Andrii Abramov, Aram Akhavan, Atlas Cove, Attaphong Rattanaveerachanon, Ben Whetton, Chris Keefe, +Clayton Smith, Craig H Maynard, Curious Cat, Daniel Haim, Daniel Kehoe, Daryl Hepting, David Bruant, David Zhang, +Edson Jiménez, Eric Cousineau, Gary, Giuseppe Bertone, Ikko Ashimine, JJ, JT, Jeff Wilcox, Jeffrey Veen, +Jesse van der Pluijm, John Losito, Kantanat-Stamp, Kirstin Heidler, Korbs, Laurence Andrews, Liam Bigelow, Maik Riechert, +Meet Gor, Meg Gutshall, Michael Gerzabek, MichaelCordingley, Miguel Brandão, Nahin Khan, Nemo, Nicholas Paxford, +Nick Coish, Otto Urpelainen, Parikshit87, Phil Kirlin, Qasim Qureshi, Ricardo N Feliciano, Rishi Raj Jain, SNVMK, +SaintMalik, Sampath Sukesh Ravolaparthi, Shannon Kularathna, Shyam Mohan K, Takuya N, Tejas Bubane, Toshimaru, Tyler887, +Vinhas Kevin, alena-ko, fauno, lm, lucafrance, nusu, shorty, なつき</small> + +--- + +### Announcement + +I would like to inform you that following this release, Jekyll will start developing towards a v5.0 milestone that will +**definitely contain breaking changes**. I have set up a [tentative roadmap at the GitHub repository][roadmap] to give everyone +a glimpse of the PROBABLE OUTCOME. Towards that end, we will no longer accept documentation fixes on `master`. The `4.3-stable` +branch will be used to build and deploy the site for https://jekyllrb.com. + +Jekyll 3.x series is now under security-maintenance phase. Only security patches will be released when necessary. + +Jekyll 4.x series will continue receiving bug-fixes and security-patches only. Depending on the state of progress towards v5.0, +there will be *at least* one minor version release serving as a transitionary version containing deprecations and bridge code +to ease the eventual upgrade to v5.0. + +[roadmap]: {{ site.repository }}/issues/9156 + +--- + +That is all for now. +Happy Jekyllin'!! diff --git a/docs/_posts/2022-10-26-jekyll-4-3-1-released.markdown b/docs/_posts/2022-10-26-jekyll-4-3-1-released.markdown new file mode 100644 index 0000000..77cd962 --- /dev/null +++ b/docs/_posts/2022-10-26-jekyll-4-3-1-released.markdown @@ -0,0 +1,21 @@ +--- +title: 'Jekyll 4.3.1 Released' +date: 2022-10-26 19:09:42 +0530 +author: ashmaroli +version: 4.3.1 +category: release +--- + +Hello Jekyllers! + +We're shipping `v4.3.1` containing fixes for two issues with v4.3.0: + - Jekyll now respects user-defined `name` attribute for collection documents when accessed in Liquid templates. + - Revert the changes made to trigger incremental rebuilds when data files are changed. + +Thanks to the users who took the time to report the issues to us. +Happy Jekyllin' + +P.S. Development towards v5 has taken a back seat as of now. I plan on releasing a v4.4.0 instead. +That said, please feel free to comment on the [tentative roadmap to v5][roadmap]. + +[roadmap]: {{ site.repository }}/issues/9156 diff --git a/docs/_sass/_docsearch.scss b/docs/_sass/_docsearch.scss new file mode 100644 index 0000000..46d75e7 --- /dev/null +++ b/docs/_sass/_docsearch.scss @@ -0,0 +1,579 @@ +.searchbox { + display: inline-block; + position: relative; + width: 200px; + height: 32px !important; + padding-top: 1px; + white-space: nowrap; + box-sizing: border-box; + visibility: visible !important; +} + +.searchbox .algolia-autocomplete { + display: block; + width: 100%; + height: 100%; +} + +.searchbox__wrapper { + width: 100%; + height: 100%; + z-index: 999; + position: relative; +} + +.searchbox__input { + display: inline-block; + box-sizing: border-box; + transition: box-shadow 0.4s ease, background 0.4s ease; + border: 0; + border-radius: 16px; + box-shadow: inset 0 0 0 1px #cccccc; + background: #ffffff !important; + padding: 0; + padding-right: 26px; + padding-left: 32px; + width: 100%; + height: 100%; + vertical-align: middle; + white-space: normal; + font-size: 12px; + appearance: none; +} + +.searchbox__input::-webkit-search-decoration, .searchbox__input::-webkit-search-cancel-button, .searchbox__input::-webkit-search-results-button, .searchbox__input::-webkit-search-results-decoration { + display: none; +} + +.searchbox__input:hover { + box-shadow: inset 0 0 0 1px #b3b3b3; +} + +.searchbox__input:focus, .searchbox__input:active { + outline: 0; + box-shadow: inset 0 0 0 1px #aaaaaa; + background: #ffffff; +} + +.searchbox__input::placeholder { + color: #aaaaaa; +} + +.searchbox__submit { + position: absolute; + top: 0; + margin: 0; + border: 0; + border-radius: 16px 0 0 16px; + background-color: rgba(69, 142, 225, 0); + padding: 0; + width: 32px; + height: 100%; + vertical-align: middle; + text-align: center; + font-size: inherit; + user-select: none; + right: inherit; + left: 0; +} + +.searchbox__submit::before { + display: inline-block; + margin-right: -4px; + height: 100%; + vertical-align: middle; + content: ''; +} + +.searchbox__submit:hover, .searchbox__submit:active { + cursor: pointer; +} + +.searchbox__submit:focus { + outline: 0; +} + +.searchbox__submit svg { + width: 14px; + height: 14px; + vertical-align: middle; + fill: #6d7e96; +} + +.searchbox__reset { + display: block; + position: absolute; + top: 8px; + right: 8px; + margin: 0; + border: 0; + background: none; + cursor: pointer; + padding: 0; + font-size: inherit; + user-select: none; + fill: rgba(0, 0, 0, 0.5); +} + +.searchbox__reset.hide { + display: none; +} + +.searchbox__reset:focus { + outline: 0; +} + +.searchbox__reset svg { + display: block; + margin: 4px; + width: 8px; + height: 8px; +} + +.searchbox__input:valid ~ .searchbox__reset { + display: block; + animation-name: sbx-reset-in; + animation-duration: 0.15s; +} + +@keyframes sbx-reset-in { + 0% { + transform: translate3d(-20%, 0, 0); + opacity: 0; + } + 100% { + transform: none; + opacity: 1; + } +} + +.algolia-autocomplete.algolia-autocomplete-right .ds-dropdown-menu { + right: 0 !important; + left: inherit !important; +} + +.algolia-autocomplete.algolia-autocomplete-right .ds-dropdown-menu:before { + right: 48px; +} + +.algolia-autocomplete.algolia-autocomplete-left .ds-dropdown-menu { + left: 0 !important; + right: inherit !important; +} + +.algolia-autocomplete.algolia-autocomplete-left .ds-dropdown-menu:before { + left: 48px; +} + +.algolia-autocomplete .ds-dropdown-menu { + position: relative; + top: -6px; + border-radius: 4px; + margin: 6px 0 0; + padding: 0; + text-align: left; + height: auto; + position: relative; + background: transparent; + border: none; + z-index: 999; + max-width: 600px; + min-width: 500px; + box-shadow: 0 1px 0 0 rgba(0, 0, 0, 0.2), 0 2px 3px 0 rgba(0, 0, 0, 0.1); +} + +.algolia-autocomplete .ds-dropdown-menu:before { + display: block; + position: absolute; + content: ''; + width: 14px; + height: 14px; + background: #fff; + z-index: 1000; + top: -7px; + border-top: 1px solid #d9d9d9; + border-right: 1px solid #d9d9d9; + transform: rotate(-45deg); + border-radius: 2px; +} + +.algolia-autocomplete .ds-dropdown-menu .ds-suggestions { + position: relative; + z-index: 1000; + margin-top: 8px; +} + +.algolia-autocomplete .ds-dropdown-menu .ds-suggestions a:hover { + text-decoration: none; +} + +.algolia-autocomplete .ds-dropdown-menu .ds-suggestion { + cursor: pointer; +} + +.algolia-autocomplete .ds-dropdown-menu .ds-suggestion.ds-cursor .algolia-docsearch-suggestion.suggestion-layout-simple { + background-color: rgba(69, 142, 225, 0.05); +} + +.algolia-autocomplete .ds-dropdown-menu .ds-suggestion.ds-cursor .algolia-docsearch-suggestion:not(.suggestion-layout-simple) .algolia-docsearch-suggestion--content { + background-color: rgba(69, 142, 225, 0.05); +} + +.algolia-autocomplete .ds-dropdown-menu [class^='ds-dataset-'] { + position: relative; + border: solid 1px #d9d9d9; + background: #fff; + border-radius: 4px; + overflow: auto; + padding: 0 8px 8px; +} + +.algolia-autocomplete .ds-dropdown-menu * { + box-sizing: border-box; +} + +.algolia-autocomplete .algolia-docsearch-suggestion { + display: block; + position: relative; + padding: 0 8px; + background: #fff; + color: #02060c; + overflow: hidden; +} + +.algolia-autocomplete .algolia-docsearch-suggestion--highlight { + color: #174d8c; + background: rgba(143, 187, 237, 0.1); + padding: 0.1em 0.05em; +} + +.algolia-autocomplete .algolia-docsearch-suggestion--category-header .algolia-docsearch-suggestion--category-header-lvl0 +.algolia-docsearch-suggestion--highlight, +.algolia-autocomplete .algolia-docsearch-suggestion--category-header .algolia-docsearch-suggestion--category-header-lvl1 +.algolia-docsearch-suggestion--highlight { + padding: 0 0 1px; + background: inherit; + box-shadow: inset 0 -2px 0 0 rgba(69, 142, 225, 0.8); + color: inherit; +} + +.algolia-autocomplete .algolia-docsearch-suggestion--text .algolia-docsearch-suggestion--highlight { + padding: 0 0 1px; + background: inherit; + box-shadow: inset 0 -2px 0 0 rgba(69, 142, 225, 0.8); + color: inherit; +} + +.algolia-autocomplete .algolia-docsearch-suggestion--content { + display: block; + float: right; + width: 70%; + position: relative; + padding: 5.33333px 0 5.33333px 10.66667px; + cursor: pointer; +} + +.algolia-autocomplete .algolia-docsearch-suggestion--content:before { + content: ''; + position: absolute; + display: block; + top: 0; + height: 100%; + width: 1px; + background: #ddd; + left: -1px; +} + +.algolia-autocomplete .algolia-docsearch-suggestion--category-header { + position: relative; + border-bottom: 1px solid #ddd; + display: none; + margin-top: 8px; + padding: 4px 0; + font-size: 1em; + color: #33363d; +} + +.algolia-autocomplete .algolia-docsearch-suggestion--wrapper { + width: 100%; + float: left; + padding: 8px 0 0 0; +} + +.algolia-autocomplete .algolia-docsearch-suggestion--subcategory-column { + float: left; + width: 30%; + padding-left: 0; + text-align: right; + position: relative; + padding: 5.33333px 10.66667px; + color: #a4a7ae; + font-size: 0.9em; + word-wrap: break-word; +} + +.algolia-autocomplete .algolia-docsearch-suggestion--subcategory-column:before { + content: ''; + position: absolute; + display: block; + top: 0; + height: 100%; + width: 1px; + background: #ddd; + right: 0; +} + +.algolia-autocomplete .algolia-docsearch-suggestion--subcategory-inline { + display: none; +} + +.algolia-autocomplete .algolia-docsearch-suggestion--title { + margin-bottom: 4px; + color: #02060c; + font-size: 0.9em; + font-weight: bold; +} + +.algolia-autocomplete .algolia-docsearch-suggestion--text { + display: block; + line-height: 1.2em; + font-size: 0.85em; + color: #63676d; +} + +.algolia-autocomplete .algolia-docsearch-suggestion--no-results { + width: 100%; + padding: 8px 0; + text-align: center; + font-size: 1.2em; +} + +.algolia-autocomplete .algolia-docsearch-suggestion--no-results::before { + display: none; +} + +.algolia-autocomplete .algolia-docsearch-suggestion code { + padding: 1px 5px; + font-size: 90%; + border: none; + color: #222222; + background-color: #ebebeb; + border-radius: 3px; + font-family: Menlo, Monaco, Consolas, 'Courier New', monospace; +} + +.algolia-autocomplete .algolia-docsearch-suggestion code .algolia-docsearch-suggestion--highlight { + background: none; +} + +.algolia-autocomplete .algolia-docsearch-suggestion.algolia-docsearch-suggestion__main .algolia-docsearch-suggestion--category-header { + display: block; +} + +.algolia-autocomplete .algolia-docsearch-suggestion.algolia-docsearch-suggestion__secondary { + display: block; +} + +@media all and (min-width: 768px) { + .algolia-autocomplete .algolia-docsearch-suggestion .algolia-docsearch-suggestion--subcategory-column { + display: block; + } +} + +@media all and (max-width: 768px) { + .algolia-autocomplete .algolia-docsearch-suggestion .algolia-docsearch-suggestion--subcategory-column { + display: inline-block; + width: auto; + text-align: left; + float: left; + padding: 0; + color: #02060c; + font-size: 0.9em; + font-weight: bold; + text-align: left; + opacity: 0.5; + } + .algolia-autocomplete .algolia-docsearch-suggestion .algolia-docsearch-suggestion--subcategory-column:before { + display: none; + } + .algolia-autocomplete .algolia-docsearch-suggestion .algolia-docsearch-suggestion--subcategory-column:after { + content: '|'; + } + .algolia-autocomplete .algolia-docsearch-suggestion .algolia-docsearch-suggestion--content { + display: inline-block; + width: auto; + text-align: left; + float: left; + padding: 0; + } + .algolia-autocomplete .algolia-docsearch-suggestion .algolia-docsearch-suggestion--content:before { + display: none; + } +} + +.algolia-autocomplete .suggestion-layout-simple.algolia-docsearch-suggestion { + border-bottom: solid 1px #eee; + padding: 8px; + margin: 0; +} + +.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--content { + width: 100%; + padding: 0; +} + +.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--content::before { + display: none; +} + +.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--category-header { + margin: 0; + padding: 0; + display: block; + width: 100%; + border: none; +} + +.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--category-header-lvl0 { + opacity: 0.6; + font-size: 0.85em; +} + +.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--category-header-lvl1 { + opacity: 0.6; + font-size: 0.85em; +} + +.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--category-header-lvl1::before { + background-image: url('data:image/svg+xml;utf8,<svg width="10" height="10" viewBox="0 0 20 38" xmlns="http://www.w3.org/2000/svg"><path d="M1.49 4.31l14 16.126.002-2.624-14 16.074-1.314 1.51 3.017 2.626 1.313-1.508 14-16.075 1.142-1.313-1.14-1.313-14-16.125L3.2.18.18 2.8l1.31 1.51z" fill-rule="evenodd" fill="%231D3657" /></svg>'); + content: ''; + width: 10px; + height: 10px; + display: inline-block; +} + +.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--wrapper { + width: 100%; + float: left; + margin: 0; + padding: 0; +} + +.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--duplicate-content, .algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--subcategory-inline { + display: none !important; +} + +.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--title { + margin: 0; + color: #458ee1; + font-size: 0.9em; + font-weight: normal; +} + +.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--title::before { + content: '#'; + font-weight: bold; + color: #458ee1; + display: inline-block; +} + +.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--text { + margin: 4px 0 0; + display: block; + line-height: 1.4em; + padding: 5.33333px 8px; + background: #f8f8f8; + font-size: 0.85em; + opacity: 0.8; +} + +.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--text .algolia-docsearch-suggestion--highlight { + color: #3f4145; + font-weight: bold; + box-shadow: none; +} + +.algolia-autocomplete .algolia-docsearch-footer { + width: 134px; + height: 20px; + z-index: 2000; + margin-top: 10.66667px; + float: right; + font-size: 0; + line-height: 0; +} + +.algolia-autocomplete .algolia-docsearch-footer--logo { + background-image: url("data:image/svg+xml,%3Csvg width='168' height='24' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cpath d='M78.988.938h16.594a2.968 2.968 0 0 1 2.966 2.966V20.5a2.967 2.967 0 0 1-2.966 2.964H78.988a2.967 2.967 0 0 1-2.966-2.964V3.897A2.961 2.961 0 0 1 78.988.938zm41.937 17.866c-4.386.02-4.386-3.54-4.386-4.106l-.007-13.336 2.675-.424v13.254c0 .322 0 2.358 1.718 2.364v2.248zm-10.846-2.18c.821 0 1.43-.047 1.855-.129v-2.719a6.334 6.334 0 0 0-1.574-.199c-.295 0-.596.021-.897.069a2.699 2.699 0 0 0-.814.24c-.24.116-.439.28-.582.491-.15.212-.219.335-.219.656 0 .628.219.991.616 1.23s.938.362 1.615.362zm-.233-9.7c.883 0 1.629.109 2.231.328.602.218 1.088.525 1.444.915.363.396.609.922.76 1.483.157.56.232 1.175.232 1.85v6.874c-.41.089-1.034.19-1.868.314-.834.123-1.772.185-2.813.185-.69 0-1.327-.069-1.895-.198a4.001 4.001 0 0 1-1.471-.636 3.085 3.085 0 0 1-.951-1.134c-.226-.465-.343-1.12-.343-1.803 0-.656.13-1.073.384-1.525.26-.45.608-.819 1.047-1.106.445-.287.95-.492 1.532-.615a8.8 8.8 0 0 1 1.82-.185 8.404 8.404 0 0 1 1.972.24v-.438c0-.307-.035-.6-.11-.874a1.88 1.88 0 0 0-.384-.73 1.784 1.784 0 0 0-.724-.493 3.164 3.164 0 0 0-1.143-.205c-.616 0-1.177.075-1.69.164a7.735 7.735 0 0 0-1.26.307l-.321-2.192c.335-.117.834-.233 1.478-.349a10.98 10.98 0 0 1 2.073-.178zm52.842 9.626c.822 0 1.43-.048 1.854-.13V13.7a6.347 6.347 0 0 0-1.574-.199c-.294 0-.595.021-.896.069a2.7 2.7 0 0 0-.814.24 1.46 1.46 0 0 0-.582.491c-.15.212-.218.335-.218.656 0 .628.218.991.615 1.23.404.245.938.362 1.615.362zm-.226-9.694c.883 0 1.629.108 2.231.327.602.219 1.088.526 1.444.915.355.39.609.923.759 1.483.158.56.233 1.175.233 1.852v6.873c-.41.088-1.034.19-1.868.314-.834.123-1.772.184-2.813.184-.69 0-1.327-.068-1.895-.198a4.001 4.001 0 0 1-1.471-.635 3.085 3.085 0 0 1-.951-1.134c-.226-.465-.343-1.12-.343-1.804 0-.656.13-1.073.384-1.524.26-.45.608-.82 1.047-1.107.445-.286.95-.491 1.532-.614a8.803 8.803 0 0 1 2.751-.13c.329.034.671.096 1.04.185v-.437a3.3 3.3 0 0 0-.109-.875 1.873 1.873 0 0 0-.384-.731 1.784 1.784 0 0 0-.724-.492 3.165 3.165 0 0 0-1.143-.205c-.616 0-1.177.075-1.69.164-.514.089-.938.191-1.26.307l-.321-2.193c.335-.116.834-.232 1.478-.348a11.633 11.633 0 0 1 2.073-.177zm-8.034-1.271a1.626 1.626 0 0 1-1.628-1.62c0-.895.725-1.62 1.628-1.62.904 0 1.63.725 1.63 1.62 0 .895-.733 1.62-1.63 1.62zm1.348 13.22h-2.689V7.27l2.69-.423v11.956zm-4.714 0c-4.386.02-4.386-3.54-4.386-4.107l-.008-13.336 2.676-.424v13.254c0 .322 0 2.358 1.718 2.364v2.248zm-8.698-5.903c0-1.156-.253-2.119-.746-2.788-.493-.677-1.183-1.01-2.067-1.01-.882 0-1.574.333-2.065 1.01-.493.676-.733 1.632-.733 2.788 0 1.168.246 1.953.74 2.63.492.683 1.183 1.018 2.066 1.018.882 0 1.574-.342 2.067-1.019.492-.683.738-1.46.738-2.63zm2.737-.007c0 .902-.13 1.584-.397 2.33a5.52 5.52 0 0 1-1.128 1.906 4.986 4.986 0 0 1-1.752 1.223c-.685.286-1.739.45-2.265.45-.528-.006-1.574-.157-2.252-.45a5.096 5.096 0 0 1-1.744-1.223c-.487-.527-.863-1.162-1.137-1.906a6.345 6.345 0 0 1-.41-2.33c0-.902.123-1.77.397-2.508a5.554 5.554 0 0 1 1.15-1.892 5.133 5.133 0 0 1 1.75-1.216c.679-.287 1.425-.423 2.232-.423.808 0 1.553.142 2.237.423.685.286 1.274.69 1.753 1.216a5.644 5.644 0 0 1 1.135 1.892c.287.738.431 1.606.431 2.508zm-20.138 0c0 1.12.246 2.363.738 2.882.493.52 1.13.78 1.91.78.424 0 .828-.062 1.204-.178.377-.116.677-.253.917-.417V9.33a10.476 10.476 0 0 0-1.766-.226c-.971-.028-1.71.37-2.23 1.004-.513.636-.773 1.75-.773 2.788zm7.438 5.274c0 1.824-.466 3.156-1.404 4.004-.936.846-2.367 1.27-4.296 1.27-.705 0-2.17-.137-3.34-.396l.431-2.118c.98.205 2.272.26 2.95.26 1.074 0 1.84-.219 2.299-.656.459-.437.684-1.086.684-1.948v-.437a8.07 8.07 0 0 1-1.047.397c-.43.13-.93.198-1.492.198-.739 0-1.41-.116-2.018-.349a4.206 4.206 0 0 1-1.567-1.025c-.431-.45-.774-1.017-1.013-1.694-.24-.677-.363-1.885-.363-2.773 0-.834.13-1.88.384-2.577.26-.696.629-1.298 1.129-1.796.493-.498 1.095-.881 1.8-1.162a6.605 6.605 0 0 1 2.428-.457c.87 0 1.67.109 2.45.24.78.129 1.444.265 1.985.415V18.17z' fill='%235468FF'/%3E%3Cpath d='M6.972 6.677v1.627c-.712-.446-1.52-.67-2.425-.67-.585 0-1.045.13-1.38.391a1.24 1.24 0 0 0-.502 1.03c0 .425.164.765.494 1.02.33.256.835.532 1.516.83.447.192.795.356 1.045.495.25.138.537.332.862.582.324.25.563.548.718.894.154.345.23.741.23 1.188 0 .947-.334 1.691-1.004 2.234-.67.542-1.537.814-2.601.814-1.18 0-2.16-.229-2.936-.686v-1.708c.84.628 1.814.942 2.92.942.585 0 1.048-.136 1.388-.407.34-.271.51-.646.51-1.125 0-.287-.1-.55-.302-.79-.203-.24-.42-.42-.655-.542-.234-.123-.585-.29-1.053-.503-.276-.127-.47-.218-.582-.271a13.67 13.67 0 0 1-.55-.287 4.275 4.275 0 0 1-.567-.351 6.92 6.92 0 0 1-.455-.4c-.18-.17-.31-.34-.39-.51-.08-.17-.155-.37-.224-.598a2.553 2.553 0 0 1-.104-.742c0-.915.333-1.638.998-2.17.664-.532 1.523-.798 2.576-.798.968 0 1.793.17 2.473.51zm7.468 5.696v-.287c-.022-.607-.187-1.088-.495-1.444-.309-.357-.75-.535-1.324-.535-.532 0-.99.194-1.373.583-.382.388-.622.949-.717 1.683h3.909zm1.005 2.792v1.404c-.596.34-1.383.51-2.362.51-1.255 0-2.255-.377-3-1.132-.744-.755-1.116-1.744-1.116-2.968 0-1.297.34-2.316 1.021-3.055.68-.74 1.548-1.11 2.6-1.11 1.033 0 1.852.323 2.458.966.606.644.91 1.572.91 2.784 0 .33-.033.676-.096 1.038h-5.314c.107.702.405 1.239.894 1.611.49.372 1.106.558 1.85.558.862 0 1.58-.202 2.155-.606zm6.605-1.77h-1.212c-.596 0-1.045.116-1.349.35-.303.234-.454.532-.454.894 0 .372.117.664.35.877.235.213.575.32 1.022.32.51 0 .912-.142 1.204-.424.293-.281.44-.651.44-1.108v-.91zm-4.068-2.554V9.325c.627-.361 1.457-.542 2.489-.542 2.116 0 3.175 1.026 3.175 3.08V17h-1.548v-.957c-.415.68-1.143 1.02-2.186 1.02-.766 0-1.38-.22-1.843-.661-.462-.442-.694-1.003-.694-1.684 0-.776.293-1.38.878-1.81.585-.431 1.404-.647 2.457-.647h1.34V11.8c0-.554-.133-.971-.399-1.253-.266-.282-.707-.423-1.324-.423a4.07 4.07 0 0 0-2.345.718zm9.333-1.93v1.42c.394-1 1.101-1.5 2.123-1.5.148 0 .313.016.494.048v1.531a1.885 1.885 0 0 0-.75-.143c-.542 0-.989.24-1.34.718-.351.479-.527 1.048-.527 1.707V17h-1.563V8.91h1.563zm5.01 4.084c.022.82.272 1.492.75 2.019.479.526 1.15.79 2.01.79.639 0 1.235-.176 1.788-.527v1.404c-.521.319-1.186.479-1.995.479-1.265 0-2.276-.4-3.031-1.197-.755-.798-1.133-1.792-1.133-2.984 0-1.16.38-2.151 1.14-2.975.761-.825 1.79-1.237 3.088-1.237.702 0 1.346.149 1.93.447v1.436a3.242 3.242 0 0 0-1.77-.495c-.84 0-1.513.266-2.019.798-.505.532-.758 1.213-.758 2.042zM40.24 5.72v4.579c.458-1 1.293-1.5 2.505-1.5.787 0 1.42.245 1.899.734.479.49.718 1.17.718 2.042V17h-1.564v-5.106c0-.553-.14-.98-.422-1.284-.282-.303-.652-.455-1.11-.455-.531 0-1.002.202-1.411.606-.41.405-.615 1.022-.615 1.851V17h-1.563V5.72h1.563zm14.966 10.02c.596 0 1.096-.253 1.5-.758.404-.506.606-1.157.606-1.955 0-.915-.202-1.62-.606-2.114-.404-.495-.92-.742-1.548-.742-.553 0-1.05.224-1.491.67-.442.447-.662 1.133-.662 2.058 0 .958.212 1.67.638 2.138.425.469.946.703 1.563.703zM53.004 5.72v4.42c.574-.894 1.388-1.341 2.44-1.341 1.022 0 1.857.383 2.506 1.149.649.766.973 1.781.973 3.047 0 1.138-.309 2.109-.925 2.912-.617.803-1.463 1.205-2.537 1.205-1.075 0-1.894-.447-2.457-1.34V17h-1.58V5.72h1.58zm9.908 11.104l-3.223-7.913h1.739l1.005 2.632 1.26 3.415c.096-.32.48-1.458 1.15-3.415l.909-2.632h1.66l-2.92 7.866c-.777 2.074-1.963 3.11-3.559 3.11a2.92 2.92 0 0 1-.734-.079v-1.34c.17.042.351.064.543.064 1.032 0 1.755-.57 2.17-1.708z' fill='%235D6494'/%3E%3Cpath d='M89.632 5.967v-.772a.978.978 0 0 0-.978-.977h-2.28a.978.978 0 0 0-.978.977v.793c0 .088.082.15.171.13a7.127 7.127 0 0 1 1.984-.28c.65 0 1.295.088 1.917.259.082.02.164-.04.164-.13m-6.248 1.01l-.39-.389a.977.977 0 0 0-1.382 0l-.465.465a.973.973 0 0 0 0 1.38l.383.383c.062.061.15.047.205-.014.226-.307.472-.601.746-.874.281-.28.568-.526.883-.751.068-.042.075-.137.02-.2m4.16 2.453v3.341c0 .096.104.165.192.117l2.97-1.537c.068-.034.089-.117.055-.184a3.695 3.695 0 0 0-3.08-1.866c-.068 0-.136.054-.136.13m0 8.048a4.489 4.489 0 0 1-4.49-4.482 4.488 4.488 0 0 1 4.49-4.482 4.488 4.488 0 0 1 4.489 4.482 4.484 4.484 0 0 1-4.49 4.482m0-10.85a6.363 6.363 0 1 0 0 12.729c3.518 0 6.372-2.85 6.372-6.368a6.358 6.358 0 0 0-6.371-6.36' fill='%23FFF'/%3E%3C/g%3E%3C/svg%3E%0A"); + background-repeat: no-repeat; + background-position: center; + background-size: 100%; + overflow: hidden; + text-indent: -9000px; + padding: 0 !important; + width: 100%; + height: 100%; + display: block; +} + +// Overrides + +.searchbox { + .searchbox__input { + padding: 6px 5px 5px 29px; + font-size: 0.75em; + border: none; + border-radius: 5px; + 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; + 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/_font-awesome.scss b/docs/_sass/_font-awesome.scss new file mode 100644 index 0000000..84e4cb8 --- /dev/null +++ b/docs/_sass/_font-awesome.scss @@ -0,0 +1,28 @@ +@font-face { + font-family: 'FontAwesome'; + font-weight: normal; + font-style: normal; + font-display: swap; + src: url('../fonts/FontAwesome.eot?9h6hxj'); + src: url('../fonts/FontAwesome.eot?9h6hxj#iefix') format('embedded-opentype'), + url('../fonts/FontAwesome.woff?9h6hxj') format('woff'), + url('../fonts/FontAwesome.ttf?9h6hxj') format('truetype'), + url('../fonts/FontAwesome.svg?9h6hxj#FontAwesome') format('svg'); +} + +.fa { + display: inline-block; + font: normal normal normal 14px/1 FontAwesome; + font-size: inherit; + text-rendering: auto; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +.fa-link:before { + content: "\f0c1"; +} + +.fa-pencil:before { + content: "\f040"; +} diff --git a/docs/_sass/_fonts.scss b/docs/_sass/_fonts.scss new file mode 100644 index 0000000..ea0c8de --- /dev/null +++ b/docs/_sass/_fonts.scss @@ -0,0 +1,90 @@ +// *.woff2 support: Chrome 26+, Opera 23+, FireFox 3.6 +// *.woff support: Chrome 6+, Firefox 3.6+, IE9+, Safari 5.1+ + +// Lato Light (300) +@font-face { + font-family: 'Lato'; + font-style: normal; + font-weight: 300; + src: local('Lato Light'), local('Lato-Light'), + url('../fonts/lato-v14-latin-300.woff2') format('woff2'), + url('../fonts/lato-v14-latin-300.woff') format('woff'); + font-display: swap; +} + +// Lato Light, Italic (300) +@font-face { + font-family: 'Lato'; + font-style: italic; + font-weight: 300; + src: local('Lato Light Italic'), local('Lato-LightItalic'), + url('../fonts/lato-v14-latin-300italic.woff2') format('woff2'), + url('../fonts/lato-v14-latin-300italic.woff') format('woff'); + font-display: swap; +} + +// Lato Regular (400) +@font-face { + font-family: 'Lato'; + font-style: normal; + font-weight: 400; + src: local('Lato Regular'), local('Lato-Regular'), + url('../fonts/lato-v14-latin-regular.woff2') format('woff2'), + url('../fonts/lato-v14-latin-regular.woff') format('woff'); + font-display: swap; +} + +// Lato Regular, Italic (400) +@font-face { + font-family: 'Lato'; + font-style: italic; + font-weight: 400; + src: local('Lato Italic'), local('Lato-Italic'), + url('../fonts/lato-v14-latin-italic.woff2') format('woff2'), + url('../fonts/lato-v14-latin-italic.woff') format('woff'); + font-display: swap; +} + +// Lato Bold (700) +@font-face { + font-family: 'Lato'; + font-style: normal; + font-weight: 700; + src: local('Lato Bold'), local('Lato-Bold'), + url('../fonts/lato-v14-latin-700.woff2') format('woff2'), + url('../fonts/lato-v14-latin-700.woff') format('woff'); + font-display: swap; +} + +// Lato Bold, Italic (700) +@font-face { + font-family: 'Lato'; + font-style: italic; + font-weight: 700; + src: local('Lato Bold Italic'), local('Lato-BoldItalic'), + url('../fonts/lato-v14-latin-700italic.woff2') format('woff2'), + url('../fonts/lato-v14-latin-700italic.woff') format('woff'); + font-display: swap; +} + +// Lato Black (900) +@font-face { + font-family: 'Lato'; + font-style: normal; + font-weight: 900; + src: local('Lato Black'), local('Lato-Black'), + url('../fonts/lato-v14-latin-900.woff2') format('woff2'), + url('../fonts/lato-v14-latin-900.woff') format('woff'); + font-display: swap; +} + +// Lato Black, Italic (900) +@font-face { + font-family: 'Lato'; + font-style: italic; + font-weight: 900; + src: local('Lato Black Italic'), local('Lato-BlackItalic'), + url('../fonts/lato-v14-latin-900italic.woff2') format('woff2'), + url('../fonts/lato-v14-latin-900italic.woff') format('woff'); + font-display: swap; +} diff --git a/docs/_sass/_gridism.scss b/docs/_sass/_gridism.scss new file mode 100644 index 0000000..4c4ce32 --- /dev/null +++ b/docs/_sass/_gridism.scss @@ -0,0 +1,125 @@ +/* + * Gridism + * A simple, responsive, and handy CSS grid by @cobyism + * https://github.com/cobyism/gridism + */ + +/* Preserve some sanity */ +.grid, +.unit { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +/* Set up some rules to govern the grid */ +.grid { + display: block; + clear: both; +} +.grid .unit { + float: left; + width: 100%; + padding: 10px; +} + +/* This ensures the outer gutters are equal to the (doubled) inner gutters. */ +.grid .unit:first-child { padding-left: 20px; } +.grid .unit:last-child { padding-right: 20px; } + +/* Nested grids already have padding though, so let's nuke it */ +.unit .unit:first-child { padding-left: 0; } +.unit .unit:last-child { padding-right: 0; } +.unit .grid:first-child > .unit { padding-top: 0; } +.unit .grid:last-child > .unit { padding-bottom: 0; } + +/* Let people nuke the gutters/padding completely in a couple of ways */ +.no-gutters .unit, +.unit.no-gutters { + padding: 0 !important; +} + +/* Wrapping at a maximum width is optional */ +.wrap .grid, +.grid.wrap { + max-width: 978px; + margin: 0 auto; +} + +/* Width classes also have shorthand versions numbered as fractions + * For example: for a grid unit 1/3 (one third) of the parent width, + * simply apply class="w-1-3" to the element. */ +.grid .whole, .grid .w-1-1 { width: 100%; } +.grid .half, .grid .w-1-2 { width: 50%; } +.grid .one-third, .grid .w-1-3 { width: 33.3332%; } +.grid .two-thirds, .grid .w-2-3 { width: 66.6665%; } +.grid .one-quarter, +.grid .one-fourth, .grid .w-1-4 { width: 25%; } +.grid .three-quarters, +.grid .three-fourths, .grid .w-3-4 { width: 75%; } +.grid .one-fifth, .grid .w-1-5 { width: 20%; } +.grid .two-fifths, .grid .w-2-5 { width: 40%; } +.grid .three-fifths, .grid .w-3-5 { width: 60%; } +.grid .four-fifths, .grid .w-4-5 { width: 80%; } +.grid .golden-small, .grid .w-g-s { width: 38.2716%; } /* Golden section: smaller piece */ +.grid .golden-large, .grid .w-g-l { width: 61.7283%; } /* Golden section: larger piece */ + +/* Clearfix after every .grid */ +.grid { + *zoom: 1; +} +.grid:before, .grid:after { + display: table; + content: ""; + line-height: 0; +} +.grid:after { + clear: both; +} + +/* Utility classes */ +.align-center { text-align: center; } +.align-left { text-align: left; } +.align-right { text-align: right; } +.pull-left { float: left; } +.pull-right { float: right; } + +/* A property for a better rendering of images in units: in + this way bigger pictures are just resized if the unit + becomes smaller */ +.unit img { + max-width: 100%; + height: auto; +} + +/* Responsive Stuff */ +@media screen and (max-width: 568px) { + /* Stack anything that isn't full-width on smaller screens + and doesn't provide the no-stacking-on-mobiles class */ + .grid:not(.no-stacking-on-mobiles) > .unit { + width: 100% !important; + padding-left: 20px; + padding-right: 20px; + } + .unit .grid .unit { + padding-left: 0px; + padding-right: 0px; + } + + /* Sometimes, you just want to be different on small screens */ + .center-on-mobiles { + text-align: center !important; + } + .hide-on-mobiles { + display: none !important; + } +} + +/* Expand the wrap a bit further on larger screens */ +@media screen and (min-width: 1180px) { + .wider .grid, + .grid.wider { + max-width: 1180px; + margin: 0 auto; + } +} diff --git a/docs/_sass/_mixins.scss b/docs/_sass/_mixins.scss new file mode 100644 index 0000000..5b9bb43 --- /dev/null +++ b/docs/_sass/_mixins.scss @@ -0,0 +1,38 @@ +@mixin box-shadow($shadow...) { + -webkit-box-shadow: $shadow; + -moz-box-shadow: $shadow; + box-shadow: $shadow; +} + +@mixin border-radius($radius...) { + -webkit-border-radius: $radius; + -moz-border-radius: $radius; + border-radius: $radius; +} + +@mixin border-top-left-radius($radius...) { + -webkit-border-top-left-radius: $radius; + -moz-border-radius-topleft: $radius; + border-top-left-radius: $radius; +} + +@mixin border-top-right-radius($radius...) { + -webkit-border-top-right-radius: $radius; + -moz-border-radius-topright: $radius; + border-top-right-radius: $radius; +} + +@mixin transition($transition...) { + -webkit-transition: $transition; + -moz-transition: $transition; + -o-transition: $transition; + transition: $transition; +} + +@mixin user-select($select...) { + -webkit-user-select: $select; /* Chrome all / Safari all */ + -moz-user-select: $select; /* Firefox all */ + -ms-user-select: $select; /* IE 10+ */ + -o-user-select: $select; + user-select: $select; +} diff --git a/docs/_sass/_normalize.scss b/docs/_sass/_normalize.scss new file mode 100644 index 0000000..fa4e73d --- /dev/null +++ b/docs/_sass/_normalize.scss @@ -0,0 +1,447 @@ +/*! normalize.css v7.0.0 | MIT License | github.com/necolas/normalize.css */ + +/* Document + ========================================================================== */ + +/** + * 1. Correct the line height in all browsers. + * 2. Prevent adjustments of font size after orientation changes in + * IE on Windows Phone and in iOS. + */ + +html { + line-height: 1.15; /* 1 */ + -ms-text-size-adjust: 100%; /* 2 */ + -webkit-text-size-adjust: 100%; /* 2 */ +} + +/* Sections + ========================================================================== */ + +/** + * Remove the margin in all browsers (opinionated). + */ + +body { + margin: 0; +} + +/** + * Add the correct display in IE 9-. + */ + +article, +aside, +footer, +header, +nav, +section { + display: block; +} + +/** + * Correct the font size and margin on `h1` elements within `section` and + * `article` contexts in Chrome, Firefox, and Safari. + */ + +h1 { + font-size: 2em; + margin: 0.67em 0; +} + +/* Grouping content + ========================================================================== */ + +/** + * Add the correct display in IE 9-. + * 1. Add the correct display in IE. + */ + +figcaption, +figure, +main { /* 1 */ + display: block; +} + +/** + * Add the correct margin in IE 8. + */ + +figure { + margin: 1em 40px; +} + +/** + * 1. Add the correct box sizing in Firefox. + * 2. Show the overflow in Edge and IE. + */ + +hr { + box-sizing: content-box; /* 1 */ + height: 0; /* 1 */ + overflow: visible; /* 2 */ +} + +/** + * 1. Correct the inheritance and scaling of font size in all browsers. + * 2. Correct the odd `em` font sizing in all browsers. + */ + +pre { + font-family: monospace, monospace; /* 1 */ + font-size: 1em; /* 2 */ +} + +/* Text-level semantics + ========================================================================== */ + +/** + * 1. Remove the gray background on active links in IE 10. + * 2. Remove gaps in links underline in iOS 8+ and Safari 8+. + */ + +a { + background-color: transparent; /* 1 */ + -webkit-text-decoration-skip: objects; /* 2 */ +} + +/** + * 1. Remove the bottom border in Chrome 57- and Firefox 39-. + * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari. + */ + +abbr[title] { + border-bottom: none; /* 1 */ + text-decoration: underline; /* 2 */ + text-decoration: underline dotted; /* 2 */ +} + +/** + * Prevent the duplicate application of `bolder` by the next rule in Safari 6. + */ + +b, +strong { + font-weight: inherit; +} + +/** + * Add the correct font weight in Chrome, Edge, and Safari. + */ + +b, +strong { + font-weight: bolder; +} + +/** + * 1. Correct the inheritance and scaling of font size in all browsers. + * 2. Correct the odd `em` font sizing in all browsers. + */ + +code, +kbd, +samp { + font-family: monospace, monospace; /* 1 */ + font-size: 1em; /* 2 */ +} + +/** + * Add the correct font style in Android 4.3-. + */ + +dfn { + font-style: italic; +} + +/** + * Add the correct background and color in IE 9-. + */ + +mark { + background-color: #ff0; + color: #000; +} + +/** + * Add the correct font size in all browsers. + */ + +small { + font-size: 80%; +} + +/** + * Prevent `sub` and `sup` elements from affecting the line height in + * all browsers. + */ + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sub { + bottom: -0.25em; +} + +sup { + top: -0.5em; +} + +/* Embedded content + ========================================================================== */ + +/** + * Add the correct display in IE 9-. + */ + +audio, +video { + display: inline-block; +} + +/** + * Add the correct display in iOS 4-7. + */ + +audio:not([controls]) { + display: none; + height: 0; +} + +/** + * Remove the border on images inside links in IE 10-. + */ + +img { + border-style: none; +} + +/** + * Hide the overflow in IE. + */ + +svg:not(:root) { + overflow: hidden; +} + +/* Forms + ========================================================================== */ + +/** + * 1. Change the font styles in all browsers (opinionated). + * 2. Remove the margin in Firefox and Safari. + */ + +button, +input, +optgroup, +select, +textarea { + font-family: sans-serif; /* 1 */ + font-size: 100%; /* 1 */ + line-height: 1.15; /* 1 */ + margin: 0; /* 2 */ +} + +/** + * Show the overflow in IE. + * 1. Show the overflow in Edge. + */ + +button, +input { /* 1 */ + overflow: visible; +} + +/** + * Remove the inheritance of text transform in Edge, Firefox, and IE. + * 1. Remove the inheritance of text transform in Firefox. + */ + +button, +select { /* 1 */ + text-transform: none; +} + +/** + * 1. Prevent a WebKit bug where (2) destroys native `audio` and `video` + * controls in Android 4. + * 2. Correct the inability to style clickable types in iOS and Safari. + */ + +button, +html [type="button"], /* 1 */ +[type="reset"], +[type="submit"] { + -webkit-appearance: button; /* 2 */ +} + +/** + * Remove the inner border and padding in Firefox. + */ + +button::-moz-focus-inner, +[type="button"]::-moz-focus-inner, +[type="reset"]::-moz-focus-inner, +[type="submit"]::-moz-focus-inner { + border-style: none; + padding: 0; +} + +/** + * Restore the focus styles unset by the previous rule. + */ + +button:-moz-focusring, +[type="button"]:-moz-focusring, +[type="reset"]:-moz-focusring, +[type="submit"]:-moz-focusring { + outline: 1px dotted ButtonText; +} + +/** + * Correct the padding in Firefox. + */ + +fieldset { + padding: 0.35em 0.75em 0.625em; +} + +/** + * 1. Correct the text wrapping in Edge and IE. + * 2. Correct the color inheritance from `fieldset` elements in IE. + * 3. Remove the padding so developers are not caught out when they zero out + * `fieldset` elements in all browsers. + */ + +legend { + box-sizing: border-box; /* 1 */ + color: inherit; /* 2 */ + display: table; /* 1 */ + max-width: 100%; /* 1 */ + padding: 0; /* 3 */ + white-space: normal; /* 1 */ +} + +/** + * 1. Add the correct display in IE 9-. + * 2. Add the correct vertical alignment in Chrome, Firefox, and Opera. + */ + +progress { + display: inline-block; /* 1 */ + vertical-align: baseline; /* 2 */ +} + +/** + * Remove the default vertical scrollbar in IE. + */ + +textarea { + overflow: auto; +} + +/** + * 1. Add the correct box sizing in IE 10-. + * 2. Remove the padding in IE 10-. + */ + +[type="checkbox"], +[type="radio"] { + box-sizing: border-box; /* 1 */ + padding: 0; /* 2 */ +} + +/** + * Correct the cursor style of increment and decrement buttons in Chrome. + */ + +[type="number"]::-webkit-inner-spin-button, +[type="number"]::-webkit-outer-spin-button { + height: auto; +} + +/** + * 1. Correct the odd appearance in Chrome and Safari. + * 2. Correct the outline style in Safari. + */ + +[type="search"] { + -webkit-appearance: textfield; /* 1 */ + outline-offset: -2px; /* 2 */ +} + +/** + * Remove the inner padding and cancel buttons in Chrome and Safari on macOS. + */ + +[type="search"]::-webkit-search-cancel-button, +[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; +} + +/** + * 1. Correct the inability to style clickable types in iOS and Safari. + * 2. Change font properties to `inherit` in Safari. + */ + +::-webkit-file-upload-button { + -webkit-appearance: button; /* 1 */ + font: inherit; /* 2 */ +} + +/* Interactive + ========================================================================== */ + +/* + * Add the correct display in IE 9-. + * 1. Add the correct display in Edge, IE, and Firefox. + */ + +details, /* 1 */ +menu { + display: block; +} + +/* + * Add the correct display in all browsers. + */ + +summary { + display: list-item; +} + +/* Scripting + ========================================================================== */ + +/** + * Add the correct display in IE 9-. + */ + +canvas { + display: inline-block; +} + +/** + * Add the correct display in IE. + */ + +template { + display: none; +} + +/* Hidden + ========================================================================== */ + +/** + * Add the correct display in IE 10-. + */ + +[hidden] { + display: none; +} diff --git a/docs/_sass/_pygments.scss b/docs/_sass/_pygments.scss new file mode 100644 index 0000000..1ce7387 --- /dev/null +++ b/docs/_sass/_pygments.scss @@ -0,0 +1,86 @@ +.highlight { + .hll { background-color: #ffffcc } + .err { color: #ce342c } /* Error */ + .c { color: #818181 } /* Comment */ + .g { color: #ffffff } /* Generic */ + .k { color: #ff4287 } /* Keyword */ + .l { color: #ffffff } /* Literal */ + .n { color: #ffffff } /* Name */ + .o { color: #ffffff } /* Operator */ + .x { color: #ffffff } /* Other */ + .p { color: #ffffff } /* Punctuation */ + .cm { color: #818181 } /* Comment.Multiline */ + .cp { color: #d1c2f4 } /* Comment.Preproc */ + .c1 { color: #818181 } /* Comment.Single */ + .cs { color: #818181 } /* Comment.Special */ + .gd { color: #ce342c } /* Generic.Deleted */ + .ge { color: #c000c0; text-decoration: underline } /* Generic.Emph */ + .gr { color: #c0c0c0; font-weight: bold; background-color: #c00000 } /* Generic.Error */ + .gh { color: #ffffff } /* Generic.Heading */ + .gi { color: #27b42c } /* Generic.Inserted */ + span.go { color: #add8e6; font-weight: bold; background-color: #4d4d4d } /* Generic.Output, qualified with span to prevent applying this style to the Go language, see #1153. */ + .gp { color: #ffffff } /* Generic.Prompt */ + .gs { color: #ffffff } /* Generic.Strong */ + .gu { color: #ffffff } /* Generic.Subheading */ + .gt { color: #c0c0c0; font-weight: bold; background-color: #c00000 } /* Generic.Traceback */ + .kc { color: #ff4287 } /* Keyword.Constant */ + .kd { color: #ff4287 } /* Keyword.Declaration */ + .kn { color: #ff4287 } /* Keyword.Namespace */ + .kp { color: #ff4287 } /* Keyword.Pseudo */ + .kr { color: #ff4287 } /* Keyword.Reserved */ + .kt { color: #bdb76b } /* Keyword.Type */ + .ld { color: #ffffff } /* Literal.Date */ + .m { color: #ffffff } /* Literal.Number */ + .s { color: #ffe580 } /* Literal.String */ + .na { color: #b6e382 } /* Name.Attribute */ + .nb { color: #ffffff } /* Name.Builtin */ + .nc { color: #b6e382 } /* Name.Class */ + .no { color: #87ceeb } /* Name.Constant */ + .nd { color: #ffffff } /* Name.Decorator */ + .ni { color: #ffdead } /* Name.Entity */ + .ne { color: #ffffff } /* Name.Exception */ + .nf { color: #ffffff } /* Name.Function */ + .nl { color: #ffffff } /* Name.Label */ + .nn { color: #ffffff } /* Name.Namespace */ + .nx { color: #ffffff } /* Name.Other */ + .py { color: #ffffff } /* Name.Property */ + .nt { color: #ff4287 } /* Name.Tag */ + .nv { color: #ffffff } /* Name.Variable */ + .ow { color: #ffffff } /* Operator.Word */ + .w { color: #ffffff } /* Text.Whitespace */ + .mf { color: #ffffff } /* Literal.Number.Float */ + .mh { color: #ffffff } /* Literal.Number.Hex */ + .mi { color: #ffffff } /* Literal.Number.Integer */ + .mo { color: #ffffff } /* Literal.Number.Oct */ + .sb { color: #ffffff } /* Literal.String.Backtick */ + .sc { color: #ffffff } /* Literal.String.Char */ + .sd { color: #ffffff } /* Literal.String.Doc */ + .s2 { color: #ffe580 } /* Literal.String.Double */ + .se { color: #ffffff } /* Literal.String.Escape */ + .sh { color: #ffffff } /* Literal.String.Heredoc */ + .si { color: #ffffff } /* Literal.String.Interpol */ + .sx { color: #ffffff } /* Literal.String.Other */ + .sr { color: #ffffff } /* Literal.String.Regex */ + .s1 { color: #ffe580 } /* Literal.String.Single */ + .ss { color: #a47bea } /* Literal.String.Symbol */ + .bp { color: #ffffff } /* Name.Builtin.Pseudo */ + .vc { color: #98fb98 } /* Name.Variable.Class */ + .vg { color: #98fb98 } /* Name.Variable.Global */ + .vi { color: #ffffff } /* Name.Variable.Instance */ + .il { color: #ffffff } /* Literal.Number.Integer.Long */ + .bash .nv { + user-select: none; + } +} + +.language-liquid { + .highlight { + .p { color: #87ceeb } + .kr { color: #87ceeb } + .nf { color: #b899ff } + .nt { color: #87ceeb } + .nv { color: #b6e382 } + } +} + +.language-sh .highlight * { color: #eaeaea } diff --git a/docs/_sass/_style.scss b/docs/_sass/_style.scss new file mode 100644 index 0000000..b97a8cb --- /dev/null +++ b/docs/_sass/_style.scss @@ -0,0 +1,1350 @@ +/* Base */ + +@charset "utf-8"; + +html { + box-sizing: border-box; +} + +*, +*:before, +*:after { + box-sizing: inherit; +} + +body { + font-family: 'Lato', 'Helvetica Neue', Helvetica, Arial, sans-serif; + font-size: 21px; + font-weight: 300; + color: #ddd; + background-color: #333; + @include box-shadow(inset 0 3px 30px rgba(0,0,0,.3)); + text-shadow: 0 1px 3px rgba(0,0,0,.5); + + // Not legible with 300 weight + // -moz-osx-font-smoothing: grayscale; + // -webkit-font-smoothing: antialiased; + text-rendering: optimizeLegibility; + + -webkit-font-feature-settings: 'kern' on, 'liga' on, 'calt' on, 'onum', 'pnum'; + -moz-font-feature-settings: 'kern' on, 'liga' on, 'calt' on, 'onum', 'pnum'; + -ms-font-feature-settings: 'kern' on, 'liga' on, 'calt' on, 'onum', 'pnum'; + -o-font-feature-settings: 'kern' on, 'liga' on, 'calt' on, 'onum', 'pnum'; + font-feature-settings: 'kern' on, 'liga' on, 'calt' on, 'onum', 'pnum'; + margin: 0; +} + +.clear { + display: block; +} + +.clear:after { + content: " "; + display: block; + height: 0; + clear: both; + visibility: hidden; +} + +/* Sections */ + +header, +section, +footer { + float: left; + width: 100%; + clear: both; +} + +/* 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, .meta { + + ul { + padding: 0; + margin: 0; + white-space: nowrap; + display: inline-block; + } + + li { display: inline-block; } +} + +.meta ul { + margin-left: 10px; + + li { vertical-align: middle; } +} + +.main-nav, .meta { + + li { + + a { + @include border-radius(5px); + font-weight: 900; + font-size: 0.75em; + padding: 0.5em 1em; + text-shadow: none; + text-transform: uppercase; + @include transition(all .25s); + + &:hover { + background-color: #252525; + @include box-shadow(inset 0 1px 3px rgba(0,0,0,.5), 0 1px 0 rgba(255,255,255,.1)); + text-shadow: 0 1px 3px rgba(0,0,0,.5); + } + } + + &.current { + + a { + background-color: #fc0; + color: #222; + @include box-shadow(inset 0 1px 0 rgba(255,255,255,.5), 0 1px 5px rgba(0,0,0,.5)); + text-shadow: 0 1px 0 rgba(255,255,255,.3); + } + } + } +} +.mobile-nav { + padding: 0 5px; + + ul { + overflow: hidden; + width: 100%; + display: table; + } + + a { + float: left; + width: 100%; + background-color: #333; + color: #fc0; + text-align: center; + text-transform: uppercase; + font-size: 0.625em; + font-weight: 900; + padding: 10px 5px; + @include border-radius(5px); + } + + li { + display: table-cell; + width: 20%; + padding: 8px 2px; + } + + .current { + + a { + background-color: #fc0; + color: #222; + @include box-shadow(inset 0 1px 0 rgba(255,255,255,.5), 0 1px 5px rgba(0,0,0,.5)); + text-shadow: 0 1px 0 rgba(255,255,255,.3); + } + } +} + +/* + * This code is courtesy Ben Balter, modified by Parker Moore for jekyllrb.com + * http://ben.balter.com/2014/03/13/pages-anchor-links/ + */ +.header-link { + position: relative; + left: 0.5em; + opacity: 0; + font-size: 0.8em; + @include transition(opacity 0.2s ease-in-out 0.1s); +} +h2:hover .header-link, +h3:hover .header-link, +h4:hover .header-link, +h5:hover .header-link, +h6:hover .header-link { + opacity: 1; +} + +@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: 890px) { + .meta { display: none; } +} + +/* Footer */ + +footer { + background-color: #212121; + font-size: 16px; + padding-bottom: 5px; + color: #c0c0c0; + margin-top: 40px; + + a { + color: #fff; + + &:hover { + + img { opacity: 1; } + } + } + + .align-right { + + p { display: inline-block; } + } + + img { + display: inline-block; + vertical-align: middle; + margin-left: 5px; + opacity: .8; + padding: 1px; + @include transition(opacity .2s); + } +} + +@media (max-width: 568px) { + footer { + .one-third p { margin-bottom: 0; } + .two-thirds p { margin-top: -20px; } + } +} + +/* Intro */ + +.intro { + + .unit { padding: 10px 0 40px; } + + p { + font-size: 1.75em; + line-height: 1em; + margin: 0; + } +} + +@media (min-width: 569px) { + .intro p { font-size: 3.2em; } +} + +/* Quickstart */ + +.quickstart { + background-color: #3F1F1F; + color: #fff; + margin: 60px 0; + @include box-shadow(inset 0 3px 10px rgba(0,0,0,.4)); + + .content { padding: 0; } + + h3 { + font-size: 24px; + line-height: 24px; + margin-top: 20px; + text-shadow: 0 1px 3px rgba(0,0,0,.8); + } + + .code { + font-size: 12px; + display: block; + margin: 0 0 -30px; + } +} + +@media (min-width: 768px) { + .quickstart { + + .code { + font-size: 18px; + margin: -30px 0; + float: right; + } + + h3 { + margin: 50px 0 0; + text-align: center; + } + } +} + +/* Code */ + +.quickstart { + + .code { + display: block; + padding: 0; + font-family: Menlo, Consolas, "Courier New", Courier, "Liberation Mono", monospace; + line-height: 1.3em; + + .title { + display: block; + text-align: center; + margin: 0 20px; + padding: 5px 0; + @include border-radius(5px 5px 0 0); + @include box-shadow(0 3px 10px rgba(0,0,0,.5)); + font: 400 16px/24px 'Helvetica Neue', Helvetica, Arial, sans-serif; + color: #444; + text-shadow: 0 1px 0 rgba(255,255,255,.5); + background-color: #f7f7f7; + background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/Pgo8c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgdmlld0JveD0iMCAwIDEgMSIgcHJlc2VydmVBc3BlY3RSYXRpbz0ibm9uZSI+CiAgPGxpbmVhckdyYWRpZW50IGlkPSJncmFkLXVjZ2ctZ2VuZXJhdGVkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjAlIiB5MT0iMCUiIHgyPSIwJSIgeTI9IjEwMCUiPgogICAgPHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2Y3ZjdmNyIgc3RvcC1vcGFjaXR5PSIxIi8+CiAgICA8c3RvcCBvZmZzZXQ9IjclIiBzdG9wLWNvbG9yPSIjY2ZjZmNmIiBzdG9wLW9wYWNpdHk9IjEiLz4KICAgIDxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iI2FhYWFhYSIgc3RvcC1vcGFjaXR5PSIxIi8+CiAgPC9saW5lYXJHcmFkaWVudD4KICA8cmVjdCB4PSIwIiB5PSIwIiB3aWR0aD0iMSIgaGVpZ2h0PSIxIiBmaWxsPSJ1cmwoI2dyYWQtdWNnZy1nZW5lcmF0ZWQpIiAvPgo8L3N2Zz4=); + background-image: -webkit-gradient(linear, left top, left bottom, from(#f7f7f7), color-stop(7%, #cfcfcf), to(#aaaaaa)); + background-image: -webkit-linear-gradient(top, #f7f7f7 0%, #cfcfcf 7%, #aaaaaa 100%); + background-image: -moz-linear-gradient(top, #f7f7f7 0%, #cfcfcf 7%, #aaaaaa 100%); + background-image: -o-linear-gradient(top, #f7f7f7 0%, #cfcfcf 7%, #aaaaaa 100%); + background-image: linear-gradient(top, #f7f7f7 0%,#cfcfcf 7%,#aaaaaa 100%); + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#f7f7f7', endColorstr='#aaaaaa',GradientType=0 ); + border-bottom: 1px solid #111; + } + + .shell { + padding: 20px; + text-shadow: none; + margin: 0 20px; + background-color: #171717; + @include border-radius(0 0 5px 5px); + @include box-shadow(0 5px 30px rgba(0,0,0,.3)); + } + + .line { + display: block; + margin: 0; + padding: 0; + + span { display: inline-block; } + } + + .path { + color: #87ceeb; + @include user-select(none); + } + + .prompt { + color: #cd5c5c; + -webkit-user-select: none; /* Chrome all / Safari all */ + -moz-user-select: none; /* Firefox all */ + -ms-user-select: none; /* IE 10+ */ + -o-user-select: none; + user-select: none; + } + + .command { color: #f0e68c; } + + .output { color: #888; } + } +} + +/* Free Hosting */ + +.free-hosting { + + .pane { + background-color: #3e3e3e; + @include border-radius(10px); + text-shadow: none; + position: relative; + padding: 0 20px 30px; + } + + img { + margin: -30px 0 0; + width: 180px; + height: 150px; + } + + h2 { font-size: 28px; } + + p, + a { font-size: 16px; } + + p { margin: .75em 0; } +} + +@media (min-width: 768px) { + .free-hosting { + + img { + float: left; + margin: -20px -30px -30px -50px; + width: 300px; + height: 251px; + } + + .pane-content { + margin-top: 35px; + padding-right: 30px; + } + + p, + a { font-size: 18px; } + + .pane:after { + content: " "; + float: right; + background: url(../img/footer-arrow.png) top left no-repeat; + width: 73px; + height: 186px; + position: absolute; + right: 0; + bottom: -30px; + } + } +} + +/* Article - Used for both docs and news */ + + +article { + background-color: #444; + @include border-radius(10px); + padding: 20px; + margin: 0 10px; + @include box-shadow(0 3px 10px rgba(0,0,0,.1)); + font-size: 16px; +} + +@media (max-width: 480px) { + article ul { padding-left: 20px; } +} + +@media (max-width: 568px) { + article { margin: 0; } +} + +@media (min-width: 768px) { + article { + padding: 40px 40px 30px; + font-size: 21px; + } +} + +/* Right-side nav - used by both docs and news */ + +aside { + padding-top: 30px; + + h4 { + text-transform: uppercase; + font-size: 14px; + font-weight: 700; + padding: 0 0 10px 30px; + margin-left: -30px; + display: inline-block; + border-bottom: 1px solid #c00; + } + + ul { + padding-left: 0; + + &:first-child { margin-top: 0; } + } + + li { + list-style-type: none; + + a { + font-size: 16px; + position: relative + } + + &.current a:before { + content: ""; + border-color: transparent transparent transparent #444; + border-style: solid; + border-width: 10px; + width: 0; + height: 0; + position: absolute; + top: 0; + left: -30px; + } + &.current a { + color: #f90; + } + } +} + +/* Documentation */ + +.docs { + + article { min-height: 800px; } + + .content { padding: 0; } +} + +.section-nav { + text-align: center; + padding-top: 40px; + position: relative; + background: url(../img/article-footer.png) top center no-repeat; + margin: 40px -20px 10px; + + > div { width: 49.5%; } + + a, + span { + color: #fff; + font-size: 16px; + text-transform: uppercase; + font-weight: 700; + padding: 8px 12px 10px; + @include border-radius(5px); + /*border: 1px solid #333;*/ + @include box-shadow(0 1px 3px rgba(0,0,0,.3), inset 0 1px 1px rgba(255,255,255,.5)); + background-color: #767676; + } + + a:hover { + color: #fff; + background-color: #888; + } + + .next, + .prev { position: relative; } + + .next:after, + .prev:before { + font-size: 36px; + color: #222; + font-weight: 900; + text-shadow: 0 1px 0 rgba(255,255,255,.4); + position: absolute; + top: -7px; + } + + .next:after { + content: '\203A'; + right: 10px; + } + + .prev:before { + content: '\2039'; + left: 10px; + } + + .prev, + .prev:hover { padding-left: 30px; } + + .next, + .next:hover { padding-right: 30px; } + + .disabled { + opacity: .5; + cursor: default; + } +} + +.improve { + padding-top: 25px; + font-size: 16px; + a { + color: #999; + } +} + +.docs-nav-mobile select { + padding: 5px; + width: 100%; + font-size: 0.85em; + color: #000; + background: #ddd; + border-color: transparent; + @include border-radius(5px); + + &:focus { + outline: none; + } + + option { + font-size: 0.9em; + padding: 3px; + } +} + +/* News */ + +article h2:first-child { margin-top: 0; } + +.post-category, +.post-meta { + display: inline-block; + vertical-align: middle; + font-size: .8em; +} + +.post-category { + display: inline-block; + margin-left: -30px; + padding: 6px 10px 8px; + padding-left: 50px; + @include border-radius(0 5px 5px 0); + position: relative; + @include box-shadow(0 1px 5px rgba(0, 0, 0, .3), inset 0 1px 0 rgba(255,255,255,.2), inset 0 -1px 0 rgba(0,0,0,.3)); + background-color: #9e2812; + background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/Pgo8c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgdmlld0JveD0iMCAwIDEgMSIgcHJlc2VydmVBc3BlY3RSYXRpbz0ibm9uZSI+CiAgPGxpbmVhckdyYWRpZW50IGlkPSJncmFkLXVjZ2ctZ2VuZXJhdGVkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjAlIiB5MT0iMCUiIHgyPSIwJSIgeTI9IjEwMCUiPgogICAgPHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzllMjgxMiIgc3RvcC1vcGFjaXR5PSIxIi8+CiAgICA8c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiM2ZjBkMGQiIHN0b3Atb3BhY2l0eT0iMSIvPgogIDwvbGluZWFyR3JhZGllbnQ+CiAgPHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9IjEiIGhlaWdodD0iMSIgZmlsbD0idXJsKCNncmFkLXVjZ2ctZ2VuZXJhdGVkKSIgLz4KPC9zdmc+); + background-image: -webkit-gradient(linear, left top, left bottom, from(#9e2812), to(#6f0d0d)); + background-image: -webkit-linear-gradient(top, #9e2812 0%, #6f0d0d 100%); + background-image: -moz-linear-gradient(top, #9e2812 0%, #6f0d0d 100%); + background-image: -o-linear-gradient(top, #9e2812 0%, #6f0d0d 100%); + background-image: linear-gradient(to bottom, #9e2812 0%,#6f0d0d 100%); + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#9e2812', endColorstr='#6f0d0d',GradientType=0 ); + + &:before { + content: ""; + position: absolute; + top: -10px; + left: 0; + border-color: transparent #6f0d0d #6f0d0d transparent; + border-style: solid; + border-width: 5px; + width: 0; + height: 0; + } +} + +.post-content img { max-width: 100% } + +.label { + float: left; + text-transform: uppercase; + font-weight: 700; + text-shadow: 0 -1px 0 rgba(0,0,0,.5); +} + +@media (max-width: 568px) { + .post-category { padding-left: 30px; } +} + +@media (min-width: 768px) { + .post-category { margin-left: -50px; } +} + +.avatar { + @include border-radius(3px); + display: inline-block; + vertical-align: middle; +} + +.post-meta { + padding: 5px 0; + color: #c0c0c0; + font-weight: 600; + text-shadow: 0 -1px 0 #000; +} + +.post-date, +.post-author { margin-left: 15px; } +.post-author .author-name { margin-left: 8px } + +.news article + article { + margin-top: -6px; + padding-top: 15px; + padding-bottom: 15px; + border-top: 1px solid #555; + border-radius: 0; + @include box-shadow(0 -1px 0 #2f2f2f); + + h2.post-title { + margin-bottom: 0.45em; + } + .post-date { margin-left: 0 } +} + +/* Tutorials */ + +.tutorial-header { + float: none; + margin-bottom: 30px; + padding: 0 0 15px; + background: transparent; + border-bottom: 1px solid #545454; + .improve { padding-top: 14px } + .tutorial-title { + display: block; + width: calc(100% - 160px); + font-size: 1.75em + } +} + +.tutorial-meta { + @extend .post-meta; + font-weight: 400; + text-shadow: none; + .tutorial-date, + .tutorial-author { + display: inline-block; + height: 24px + } +} + +.tutorial-date { + vertical-align: sub +} +.tutorial-author { + margin-left: 15px; + .author-name { vertical-align: middle } +} + + +/* Code Highlighting */ + +pre, +code { + white-space: pre; + display: inline-block; + margin: 0; + font: 14px/1.625em Menlo, Consolas, "Courier New", Courier, "Liberation Mono", monospace; + padding: 0 0.5em; +} + +@media (min-width: 768px) { + pre, code { font-size: 16px; } +} + +.highlight, +a > code, +p > pre, +p > code, +p > nobr > code, +li > code, +li> pre, +h5 > code, +.note > code { + background-color: #272727; + color: #fff; + max-width: 100%; + overflow-x: auto; + vertical-align: middle; + @include border-radius(5px); + @include box-shadow(inset 0 1px 10px rgba(0,0,0,.3), + 0 1px 0 rgba(255,255,255,.1), + 0 -1px 0 rgba(0,0,0,.5)); +} + +a > code { color: inherit } + +.note .highlight { + width: 94%; + pre code { + font-size: 0.9em; + background-color: transparent; + box-shadow: none; + } +} + +.note code { + background-color: #333; + background-color: rgba(0,0,0,0.2); + margin-left: 2.5px; + margin-right: 2.5px; + font-size: 0.8em; +} + +.code-block { + margin: 10px 0; + code { background: none; } +} + +.highlight { + margin: 1em 0; + width: 100%; + overflow: auto; +} + +pre.highlight, +.highlight > pre { + padding: 10px 0.5em; +} + +.highlighter-rouge .highlight { + @extend .highlight; + margin: 0; +} + +div.highlighter-rouge + div.highlighter-rouge { + margin: 30px 0 0; +} + +/* HTML Elements */ + +h1, h2, h3, h4, h5, h6 { margin: 0; } + +a { + color: #fc0; + text-decoration: none; + @include transition(all .25s); + + &:hover { color: #f90; } +} + +strong { font-weight: 700; } + +p { line-height: 1.5em; } + +.left { float: left; } +.right { float: right; } +.align-right { text-align: right; } +.align-left { text-align: left; } +.align-center { text-align: center; } + +/* Article HTML */ + +article { + + h2, h3, h4, h5, h6 { margin: 1em 0; } + + h4 { color: #fff; } + + ul li { + + p { margin: 0; } + + blockquote { margin: 10px 0; } + } + + ul li, + ol li { + line-height: 1.5em; + margin-bottom: 0.5em; + } + +} + +h5, h6 { + font-size: 1em; + font-style: italic; +} + +blockquote { + border-left: 2px solid #777; + padding-left: 20px; + font-style: italic; + font-size: 18px; + font-weight: 500; +} + + +/* Tables */ + +table { + width: 100%; + background-color: #555; + margin: .5em 0; + @include border-radius(5px); + @include box-shadow(0 1px 3px rgba(0,0,0,.3)); +} + +thead { + @include border-top-left-radius(5px); + @include border-top-right-radius(5px); + color: #fff; + background-color: #3a3a3a; + background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/Pgo8c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgdmlld0JveD0iMCAwIDEgMSIgcHJlc2VydmVBc3BlY3RSYXRpbz0ibm9uZSI+CiAgPGxpbmVhckdyYWRpZW50IGlkPSJncmFkLXVjZ2ctZ2VuZXJhdGVkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjAlIiB5MT0iMCUiIHgyPSIwJSIgeTI9IjEwMCUiPgogICAgPHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzNhM2EzYSIgc3RvcC1vcGFjaXR5PSIxIi8+CiAgICA8c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiMxZTFlMWUiIHN0b3Atb3BhY2l0eT0iMSIvPgogIDwvbGluZWFyR3JhZGllbnQ+CiAgPHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9IjEiIGhlaWdodD0iMSIgZmlsbD0idXJsKCNncmFkLXVjZ2ctZ2VuZXJhdGVkKSIgLz4KPC9zdmc+); + background-image: -webkit-gradient(linear, left top, left bottom, from(#3a3a3a), to(#1e1e1e)); + background-image: -webkit-linear-gradient(top, #3a3a3a 0%, #1e1e1e 100%); + background-image: -moz-linear-gradient(top, #3a3a3a 0%, #1e1e1e 100%); + background-image: -o-linear-gradient(top, #3a3a3a 0%, #1e1e1e 100%); + background-image: linear-gradient(to bottom, #3a3a3a 0%,#1e1e1e 100%); + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#3a3a3a', endColorstr='#1e1e1e',GradientType=0 ); + + th { + position: relative; + @include box-shadow(inset 0 1px 0 rgba(255,255,255,.1)); + + &:first-child { + @include border-top-left-radius(5px); + } + + &:last-child { + @include border-top-right-radius(5px); + } + } +} + +td { padding: .5em .75em; } + +td p { margin: 0; } + +th { + text-transform: uppercase; + font-size: 16px; + padding: .5em .75em; + text-shadow: 0 -1px 0 rgba(0,0,0,.9); + color: #888; +} + +tbody td { + border-top: 1px solid #747474; + border-top: 1px solid rgba(0,0,0,.1); + @include box-shadow(inset 0 1px 0 rgba(255,255,255,.1)); + background: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/Pgo8c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgdmlld0JveD0iMCAwIDEgMSIgcHJlc2VydmVBc3BlY3RSYXRpbz0ibm9uZSI+CiAgPGxpbmVhckdyYWRpZW50IGlkPSJncmFkLXVjZ2ctZ2VuZXJhdGVkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjAlIiB5MT0iMCUiIHgyPSIwJSIgeTI9IjEwMCUiPgogICAgPHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2ZmZmZmZiIgc3RvcC1vcGFjaXR5PSIwLjEiLz4KICAgIDxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iI2ZmZmZmZiIgc3RvcC1vcGFjaXR5PSIwIi8+CiAgPC9saW5lYXJHcmFkaWVudD4KICA8cmVjdCB4PSIwIiB5PSIwIiB3aWR0aD0iMSIgaGVpZ2h0PSIxIiBmaWxsPSJ1cmwoI2dyYWQtdWNnZy1nZW5lcmF0ZWQpIiAvPgo8L3N2Zz4=); + background-image: -webkit-gradient(linear, left top, left bottom, from(rgba(255,255,255,0.1)), to(rgba(255,255,255,0))); + background-image: -webkit-linear-gradient(top, rgba(255,255,255,0.1) 0%, rgba(255,255,255,0) 100%); + background-image: -moz-linear-gradient(top, rgba(255,255,255,0.1) 0%, rgba(255,255,255,0) 100%); + background-image: -o-linear-gradient(top, rgba(255,255,255,0.1) 0%, rgba(255,255,255,0) 100%); + background-image: linear-gradient(to bottom, rgba(255,255,255,0.1) 0%,rgba(255,255,255,0) 100%); + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#1affffff', endColorstr='#00ffffff',GradientType=0 ); + + ul { + padding-left: 1em; + } + + p, + ul { + font-size: 16px; + + code { font-size: 14px; } + } +} + +code.option, +th .option, +code.filter, +th .filter { + color: #50B600; +} + +code.flag, +th .flag, +code.output, +th .output { + color: #049DCE; +} + +code.option, +code.flag, +code.filter, +code.output { + margin-bottom: 2px; +} + +table#builtin-hooks { + td { + text-align: center; + &:first-of-type { + max-width: 330px + } + } +} + +/* Note types */ + +.note { + margin: 30px 0; + margin-left: -30px; + padding: 20px 20px 24px; + padding-left: 50px; + @include border-radius(0 5px 5px 0); + position: relative; + @include box-shadow(0 1px 5px rgba(0, 0, 0, .3), inset 0 1px 0 rgba(255,255,255,.2), inset 0 -1px 0 rgba(0,0,0,.3)); + background-color: #7e6d42; + background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/Pgo8c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgdmlld0JveD0iMCAwIDEgMSIgcHJlc2VydmVBc3BlY3RSYXRpbz0ibm9uZSI+CiAgPGxpbmVhckdyYWRpZW50IGlkPSJncmFkLXVjZ2ctZ2VuZXJhdGVkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjAlIiB5MT0iMCUiIHgyPSIwJSIgeTI9IjEwMCUiPgogICAgPHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzdlNmQ0MiIgc3RvcC1vcGFjaXR5PSIxIi8+CiAgICA8c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiM1YzRlMzUiIHN0b3Atb3BhY2l0eT0iMSIvPgogIDwvbGluZWFyR3JhZGllbnQ+CiAgPHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9IjEiIGhlaWdodD0iMSIgZmlsbD0idXJsKCNncmFkLXVjZ2ctZ2VuZXJhdGVkKSIgLz4KPC9zdmc+); + background-image: -webkit-gradient(linear, left top, left bottom, from(#7e6d42), to(#5c4e35)); + background-image: -webkit-linear-gradient(top, #7e6d42 0%, #5c4e35 100%); + background-image: -moz-linear-gradient(top, #7e6d42 0%, #5c4e35 100%); + background-image: -o-linear-gradient(top, #7e6d42 0%, #5c4e35 100%); + background-image: linear-gradient(to bottom, #7e6d42 0%,#5c4e35 100%); + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#7e6d42', endColorstr='#5c4e35',GradientType=0 ); +} + +@media (max-width: 568px) { + .note { margin-right: -30px; } +} + +@media (min-width: 768px) { + .note { margin-left: -50px; } +} + +.note { + &:before { + content: ""; + position: absolute; + top: -10px; + left: 0; + border-color: transparent #222 #222 transparent; + border-style: solid; + border-width: 5px; + width: 0; + height: 0; + } + + h5, + p { + margin: 0; + color: #fff; + } + + h5 { + line-height: 1.5em; + font-weight: 900; + font-style: normal; + } + + p { + font-weight: 400; + font-size: .75em; + } + + &:after { + content: '\2605'; + color: #fc0; + position: absolute; + top: 14px; + left: 14px; + font-size: 28px; + font-weight: 700; + text-shadow: 0 -1px 0 rgba(0,0,0,.5); + } +} + +p.note { + color: #fff; + font-weight: 400; + font-size: .75em; + + &:after { + line-height: 1.21; + } +} + +.info { + background-color: #0389aa; + background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/Pgo8c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgdmlld0JveD0iMCAwIDEgMSIgcHJlc2VydmVBc3BlY3RSYXRpbz0ibm9uZSI+CiAgPGxpbmVhckdyYWRpZW50IGlkPSJncmFkLXVjZ2ctZ2VuZXJhdGVkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjAlIiB5MT0iMCUiIHgyPSIwJSIgeTI9IjEwMCUiPgogICAgPHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzAzODlhYSIgc3RvcC1vcGFjaXR5PSIxIi8+CiAgICA8c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiMwMDYxN2YiIHN0b3Atb3BhY2l0eT0iMSIvPgogIDwvbGluZWFyR3JhZGllbnQ+CiAgPHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9IjEiIGhlaWdodD0iMSIgZmlsbD0idXJsKCNncmFkLXVjZ2ctZ2VuZXJhdGVkKSIgLz4KPC9zdmc+); + background-image: -webkit-gradient(linear, left top, left bottom, from(#0389aa), to(#00617f)); + background-image: -webkit-linear-gradient(top, #0389aa 0%, #00617f 100%); + background-image: -moz-linear-gradient(top, #0389aa 0%, #00617f 100%); + background-image: -o-linear-gradient(top, #0389aa 0%, #00617f 100%); + background-image: linear-gradient(to bottom, #0389aa 0%,#00617f 100%); + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#0389aa', endColorstr='#00617f',GradientType=0 ); +} + +.warning { + background-color: #9e2812; + background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/Pgo8c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgdmlld0JveD0iMCAwIDEgMSIgcHJlc2VydmVBc3BlY3RSYXRpbz0ibm9uZSI+CiAgPGxpbmVhckdyYWRpZW50IGlkPSJncmFkLXVjZ2ctZ2VuZXJhdGVkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjAlIiB5MT0iMCUiIHgyPSIwJSIgeTI9IjEwMCUiPgogICAgPHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzllMjgxMiIgc3RvcC1vcGFjaXR5PSIxIi8+CiAgICA8c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiM2ZjBkMGQiIHN0b3Atb3BhY2l0eT0iMSIvPgogIDwvbGluZWFyR3JhZGllbnQ+CiAgPHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9IjEiIGhlaWdodD0iMSIgZmlsbD0idXJsKCNncmFkLXVjZ2ctZ2VuZXJhdGVkKSIgLz4KPC9zdmc+); + background-image: -webkit-gradient(linear, left top, left bottom, from(#9e2812), to(#6f0d0d)); + background-image: -webkit-linear-gradient(top, #9e2812 0%, #6f0d0d 100%); + background-image: -moz-linear-gradient(top, #9e2812 0%, #6f0d0d 100%); + background-image: -o-linear-gradient(top, #9e2812 0%, #6f0d0d 100%); + background-image: linear-gradient(to bottom, #9e2812 0%,#6f0d0d 100%); + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#9e2812', endColorstr='#6f0d0d',GradientType=0 ); +} + +.unreleased { + background-color: #cd9239; + background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/Pgo8c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgdmlld0JveD0iMCAwIDEgMSIgcHJlc2VydmVBc3BlY3RSYXRpbz0ibm9uZSI+CiAgPGxpbmVhckdyYWRpZW50IGlkPSJncmFkLXVjZ2ctZ2VuZXJhdGVkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjAlIiB5MT0iMCUiIHgyPSIwJSIgeTI9IjEwMCUiPgogICAgPHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2NkOTIzOSIgc3RvcC1vcGFjaXR5PSIxIi8+CiAgICA8c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiNhMjc1MjgiIHN0b3Atb3BhY2l0eT0iMSIvPgogIDwvbGluZWFyR3JhZGllbnQ+CiAgPHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9IjEiIGhlaWdodD0iMSIgZmlsbD0idXJsKCNncmFkLXVjZ2ctZ2VuZXJhdGVkKSIgLz4KPC9zdmc+); + background-image: -webkit-gradient(linear, left top, left bottom, from(rgba(205,146,57,1)), to(rgba(162,117,40,1))); + background-image: -webkit-linear-gradient(top, rgba(205,146,57,1) 0%, rgba(162,117,40,1) 100%); + background-image: -moz-linear-gradient(top, rgba(205,146,57,1) 0%, rgba(162,117,40,1) 100%); + background-image: -o-linear-gradient(top, rgba(205,146,57,1) 0%, rgba(162,117,40,1) 100%); + background-image: linear-gradient(to bottom, rgba(205,146,57,1) 0%,rgba(162,117,40,1) 100%); + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#cd9239', endColorstr='#a27528',GradientType=0 ); +} + +.info:before { border-color: transparent #00617f #00617f transparent; } + +.warning:before { border-color: transparent #6f0d0d #6f0d0d transparent; } + +.unreleased:before { border-color: transparent #664719 #664719 transparent; } + +.info:after { + content: '\24D8'; + color: #fff; + position: absolute; + top: 15px; + left: 15px; + font-size: 28px; + font-weight: 700; + text-shadow: 0 -1px 0 rgba(0,0,0,.5); +} + +.warning:after { + content: '\203C'; + color: #fc0; + position: absolute; + top: 15px; + left: 15px; + font-size: 32px; + font-weight: 700; + text-shadow: 0 -1px 0 rgba(0,0,0,.5); +} + +.unreleased:after { + content: '\2692'; + color: #2b2a12; + position: absolute; + top: 8px; + left: 15px; + font-size: 38px; + font-weight: 700; + 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) { + .mobile-side-scroller { + overflow-x: scroll; + margin: 0 -40px; + padding: 0 10px; + } +} + + +.show-on-mobiles { + display: none; +} + +@media screen and (max-width: 568px) { + .show-on-mobiles { + display: block !important; + } + a .show-on-mobiles { + display: inline !important; + } +} + +.videoWrapper { + position: relative; + padding-bottom: 52.4%; + padding-top: 25px; + height: 0; +} + +.videoWrapper iframe { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; +} + +.imageWrapper { + width: 100%; + height: 0; + padding-bottom: 62.623762376237624%; /* You define this doing height / width * 100% */ + position: relative; + background: #717171; + display: block; + + img { + width: 100%; + position: absolute; + opacity: 0; + } + img.b-loaded { + opacity: 1; + transition: opacity .5s; + } +} + + +/* Helper class taken from Bootstrap. + Hides an element to all devices except screen readers. +*/ +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0; +} + +.result { + padding: 12px; +} + +.image-description { + margin: -20px 0 20px; + padding: 10px 15px; + font-size: 0.81em; + text-align: justify; + background: #5c5c5c; + + pre, code { + font-size: 0.75em; + background: #454545; + } +} + +.language-sh { + margin-top: 5px; + margin-bottom: 5px; + position: relative; + &:before { + display: inline-table; + padding: 8px; + width: 100%; + padding: 5px 0; + font: 400 16px/24px 'Helvetica Neue', Helvetica, Arial, sans-serif; + color: #444; + text-shadow: 0 1px 0 rgba(255,255,255,.5); + background-color: #f7f7f7; + background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/Pgo8c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgdmlld0JveD0iMCAwIDEgMSIgcHJlc2VydmVBc3BlY3RSYXRpbz0ibm9uZSI+CiAgPGxpbmVhckdyYWRpZW50IGlkPSJncmFkLXVjZ2ctZ2VuZXJhdGVkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjAlIiB5MT0iMCUiIHgyPSIwJSIgeTI9IjEwMCUiPgogICAgPHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2Y3ZjdmNyIgc3RvcC1vcGFjaXR5PSIxIi8+CiAgICA8c3RvcCBvZmZzZXQ9IjclIiBzdG9wLWNvbG9yPSIjY2ZjZmNmIiBzdG9wLW9wYWNpdHk9IjEiLz4KICAgIDxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iI2FhYWFhYSIgc3RvcC1vcGFjaXR5PSIxIi8+CiAgPC9saW5lYXJHcmFkaWVudD4KICA8cmVjdCB4PSIwIiB5PSIwIiB3aWR0aD0iMSIgaGVpZ2h0PSIxIiBmaWxsPSJ1cmwoI2dyYWQtdWNnZy1nZW5lcmF0ZWQpIiAvPgo8L3N2Zz4=); + background-image: -webkit-gradient(linear, left top, left bottom, from(#f7f7f7), color-stop(7%, #cfcfcf), to(#aaaaaa)); + background-image: -webkit-linear-gradient(top, #f7f7f7 0%, #cfcfcf 7%, #aaaaaa 100%); + background-image: -moz-linear-gradient(top, #f7f7f7 0%, #cfcfcf 7%, #aaaaaa 100%); + background-image: -o-linear-gradient(top, #f7f7f7 0%, #cfcfcf 7%, #aaaaaa 100%); + background-image: linear-gradient(to bottom, #ddd 0%,#999 84%,#bbb 100%); + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#f7f7f7', endColorstr='#aaaaaa',GradientType=0 ); + text-align: center; + content: "terminal"; + @include border-radius(3px 3px 0 0); + @include box-shadow(0 3px 10px rgba(0,0,0,.5)); + } + .highlight { + @include border-radius(0 0 3px 3px); + } + div.highlight { + border: 1px solid; + border-color: transparent #bbb #bbb #bbb; + @include box-shadow(0 3px 10px rgba(0,0,0,.5)); + } + pre.highlight { + min-height: 48px; + background: #1c1c1c; + } + code { + font-size: 15px + } +} + +.showcase { + display: flex; + list-style: none; + padding: 0; + margin: 0; + flex-wrap: wrap; + justify-content: space-between; + + li { + flex: 1 1 300px; + box-sizing: border-box; + margin: 10px; + figure { + margin: 0; + } + figcaption { + text-align: center; + } + &.spacer { + height: 0; + margin: 0; + } + } +} + +.step-nav { + background: #2b2b2b; + border-radius: 5px; + color: #fc0; + padding: 8px 45px; + + li { + margin: 0; + padding: 0; + + &.current, &.current a { + color: #f90; + font-weight: bold; + } + } +} + +ol div.highlighter-rouge { + margin: 8px 0 10px 0; +} + +.disclaimer-ribbon { + position: relative; + width: calc(100% + 100px); + margin: 45px -50px 0; + padding: 5px 20px; + font-size: 0.725em; + font-weight: 600; + color: #efc98f; + background: #695949; + &:before, &:after { + position: absolute; + top: -9px; + content: ""; + display: table; + border: 5px solid; + z-index: -1; + } + &:before { + left: 0; + border-color: transparent #b28b70 #b28b70 transparent; + } + &:after { + right: 0; + border-color: transparent transparent #b28b70 #b28b70; + } +} diff --git a/docs/_tutorials/cache-api.md b/docs/_tutorials/cache-api.md new file mode 100644 index 0000000..b354ed0 --- /dev/null +++ b/docs/_tutorials/cache-api.md @@ -0,0 +1,86 @@ +--- +title: Cache API +author: pathawks +date: 2018-08-17 12:56:24 -0400 +--- + +Jekyll includes a caching API, which is used both internally as well as exposed +for plugins, which can be used to cache the output of deterministic functions to +speed up site generation. This cache will be persistent across builds, but +cleared when Jekyll detects any changes to `_config.yml`. + +## Jekyll::Cache.new(name) → new_cache + +If there has already been a cache created with `name`, this will return a +reference to that existing Cache. Otherwise, create a new Cache called `name`. + +If this Cache will be used by a Gem-packaged plugin, `name` should either be the +name of the Gem, or prefixed with the name of the Gem followed by `::` (if a +plugin expects to use multiple Caches). If this Cache will be used internally by +Jekyll, `name` should be the name of the class that is using the Cache (ie: +`"Jekyll::Converters::Markdown"`). + +Cached objects are shared between all Caches created with the same `name`, but +are _not_ shared between Caches with different names. There can be an object +stored with key `1` in `Jekyll::Cache.new("a")` and an object stored with key +`1` in `Jekyll::Cache.new("b")` and these will not point to the same cached +object. This way, you do not need to ensure that keys are globally unique. + +## getset(key) {block} + +This is the most common way to utilize the Cache. + +`block` is a bit of code that takes a lot of time to compute, but always +generates the same output given a particular input (like converting Markdown to +HTML). `key` is a `String` (or an object with `to_s`) that uniquely identifies +the input to the function. + +If `key` already exists in the Cache, it will be returned and `block` will never +be executed. If `key` does not exist in the Cache, `block` will be executed and +the result will be added to the Cache and returned. + +```ruby +def cache + @@cache ||= Jekyll::Cache.new("ConvertMarkdown") +end + +def convert_markdown_to_html(markdown) + cache.getset(markdown) do + expensive_conversion_method(markdown) + end +end +``` + +In the above example, `expensive_conversion_method` will only be called once for +any given `markdown` input. If `convert_markdown_to_html` is called a second +time with the same input, the cached output will be returned. + +Because posts will frequently remain unchanged from one build to the next, this +is an effective way to avoid performing the same computations each time the site +is built. + +## clear + +This will clear all cached objects from a particular Cache. The Cache will be +empty, both in memory and on disk. + +### The following methods will probably only be used in special circumstances + +## cache[key] → value + +Fetches `key` from Cache and returns its `value`. Raises if `key` does not exist +in Cache. + +## cache[key] = value + +Adds `value` to Cache under `key`. +Returns nothing. + +## key?(key) → true or false + +Returns `true` if `key` already exists in Cache. False otherwise. + +## delete(key) + +Removes `key` from Cache. +Returns nothing. diff --git a/docs/_tutorials/convert-site-to-jekyll.md b/docs/_tutorials/convert-site-to-jekyll.md new file mode 100644 index 0000000..e31c0a1 --- /dev/null +++ b/docs/_tutorials/convert-site-to-jekyll.md @@ -0,0 +1,540 @@ +--- +title: Convert an HTML site to Jekyll +author: tomjoht +date: 2017-02-10 21:58:56 -0800 +--- + +If you're looking for themes for your Jekyll site, you don't have to restrict yourself to existing Jekyll themes. It's pretty easy to convert almost any static HTML files into a Jekyll website. + +In many ways, any site that is currently a static site is *already* a Jekyll website. Jekyll just allows you to automate parts of the site (like inserting pages into templates, rendering lists for navigation, generating feeds and sitemaps, and more) as it processes the files. + +Understanding how to convert any HTML site into Jekyll templates will open your world to many more options for Jekyll themes. Instead of [searching online for *Jekyll themes*](https://duckduckgo.com/?q=Jekyll+themes), you can choose from the large variety of HTML templates for your site, quickly Jekyll-ize the HTML templates as you need to, and build the output with Jekyll. + +Although websites can have sophisticated features and controls, we'll keep things simple in this tutorial. + +## What is a Jekyll Website? + +First, let's start with a grounding in the basics. Stripping a Jekyll site down to an extremely basic level will help clarify what happens in a Jekyll site. If you haven't already installed the jekyll gem, [install it]({% link _docs/installation.md %}). + +We'll start with a *basic Jekyll site* consisting of three files: + +``` +. +├── _config.yml +├── _layouts +│ └── default.html +└── index.md +``` + +Manually create these three files in a folder called `my_jekyll_site` or whatever suits you the most, and place `default.html` inside a folder named `_layouts`. + +```sh +touch _config.yml index.md default.html +mkdir _layouts && mv default.html _layouts +``` + +Fire up your favorite editor, and populate the contents of the `default.html` and `index.md` files as follows: + +**_config.yml** + +```yaml +name: My Jekyll Website +``` + +**_layouts/default.html** + +{% raw %} +```html +<!DOCTYPE html> +<html> + <body> + {{ content }} + </body> +</html> +``` +{% endraw %} + +**index.md** + +{% raw %} +```markdown +--- +title: My page +layout: default +--- + +# {{ page.title }} + +Content is written in [Markdown](https://learnxinyminutes.com/docs/markdown/). +Plain text format allows you to focus on your **content**. + +<!-- +You can use HTML elements in Markdown, such as the comment element, and they won't +be affected by a markdown parser. However, if you create an HTML element in your +markdown file, you cannot use markdown syntax within that element's contents. +--> +``` +{% endraw %} + +Now `cd` to `my_jekyll_site` and serve the site with the built-in server: + +```sh +cd my_jekyll_site +jekyll serve +``` + +{: .note .info} +If you have a Gemfile, [use Bundler](/docs/ruby-101/#bundler) by typing `bundle exec jekyll serve` instead. + +When you serve the site, you get a preview URL such as `http://127.0.0.1:4000/` (which is the same as `http://localhost:4000/`). The site's files are built into the `_site` folder by default. + +This is a Jekyll site at the most basic functional level. Here's what is happening: + + * The `_config.yml` file contains settings that Jekyll uses as it processes your site. An empty config file will use default values for building a Jekyll site. For example, to convert [Markdown](https://learnxinyminutes.com/docs/markdown/) to HTML, Jekyll will automatically use the [kramdown Markdown filter](https://rubygems.org/gems/kramdown/), without any need to specify it. + * Jekyll looks for files with [front matter tags]({% link _docs/front-matter.md %}) (the two sets of dashed lines `---` like those in `index.md`) and processes the files (populating site variables, rendering any [Liquid](https://shopify.github.io/liquid/), and converting Markdown to HTML). + * Jekyll pushes the content from all pages and posts into the {% raw %}`{{ content }}`{% endraw %} variable in the layout specified (`default`) in the front matter tags. + * The processed files get written as `.html` files in the `_site` directory. + +You can read more about how Jekyll processes the files in [order of Interpretation]({% link _tutorials/orderofinterpretation.md %}). + +With this basic understanding of how a Jekyll site works, you can convert almost any HTML theme for Jekyll. The following sections will take you through a step-by-step tutorial to do so. + +## 1. Create a template for your default layout + +Find your HTML theme and save it as a `default` layout. If you're converting or cloning an existing site, you can right-click the page and view the source code. + +For example, suppose you're cloning your company site to create a documentation site with the same branding. Or suppose you have a personal site that you built with HTML and now want to make it a Jekyll site. Get the HTML source code for your site. + +{: .note .info} +Regardless of the site, do check the license and make sure you have permission to copy and use the code. + +Copy and paste the source code into a file called `default.html`. Put the `default.html` file inside the `_layouts` folder. This will be the default layout template for your pages and posts — that is, each page or post will use this layout when Jekyll builds the site. + +Note that in looking for templates, you want the HTML output of the template. If the template has PHP tags or other dynamic scripts, these dynamic elements will need to be converted to HTML or to [Liquid](https://shopify.github.io/liquid/). Liquid is [Jekyll templating system](/docs/liquid/) to retrieve dynamic content. + +Open `default.html` into your browser locally to ensure the site looks and behaves like it does online. You will likely need to adjust CSS, JS, and image paths so they work. + +For example, if the paths were relative on the site you copied, you'll need to either download the same assets into your Jekyll site or use absolute paths to the same assets in the cloud. (Syntax such as `src="//` requires a prefix such as `src="http://` to work in your local browser.) + +Jekyll provides some [filters](/docs/liquid/filters/) to prepend a site URL before path. For example, you could preface your stylesheet like this: + +{% raw %} +```liquid +{{ "/assets/style.css" | relative_url }} +``` +{% endraw %} + +The `relative_url` filter will prepend the [`baseurl`](https://byparker.com/blog/2014/clearing-up-confusion-around-baseurl/) value from your config file (as `blog` for instance) to the input. This is useful if your site is hosted at a subpath rather than at the root of the domain (for example, `http://mysite.com/blog/`). + +You can also use an `absolute_url` filter. This filter will prepend the `url` *and* `baseurl` value to the input: + +{% raw %} +```liquid +{{ "/assets/style.css" | absolute_url }} +``` +{% endraw %} + +Again, both `url` and `baseurl` can be defined in your site's config file, like this: + +```yaml +url: http://mysite.com +baseurl: /blog +``` + +The result in the output will be `http://mysite.com/blog/assets/style.css`. + +Note that the `url` property of any page begins with a forward slash (`/`), so omit this at the end of your `url` or `baseurl` property. + +You don't have to prepend filters to link paths like this. You could also use relative links across your entire site. However you decide to code the paths to your assets, make sure they render correctly. + +Does your local `default.html` page look good in your browser? Are all images, styles, and other elements showing up correctly? If so, great. Keep going. You can use this template as the layout for all your pages and posts or create as many templates as you need. + +In the next section, you'll blank out the content of the layout and replace it with placeholder tags that get populated dynamically with your Jekyll pages. + +## 2. Identify the content part of the layout + +In `default.html`, find where the page content begins (usually at `h1` or `h2` tags). Replace the title that appears inside these tags with {% raw %}`{{ page.title }}`{% endraw %}. + +Remove the content part (keep everything else: navigation menu, sidebar, footer, etc.) and replace it with {% raw %}`{{ content }}`{% endraw %}. + +Check the layout again in your browser and make sure you didn't corrupt or alter it up by inadvertently removing a crucial `div` tag or other element. The only change should be to the title and page content, which are now blanked out or showing the placeholder tag. + +## 3. Create a couple of files with front matter tags + +Create a couple of files (`index.md` and `about.md`) in your root directory. + +In your `index.md` file, add some front matter tags containing a `title` and `layout` property, like this: + +```markdown +--- +title: Home +layout: default +--- + +Some page content here... +``` + +Create another page for testing called `about.md` with similar front matter tags. + +{: .note .info} +If you don't specify a layout in your pages, Jekyll will simply render that page as an unstyled basic HTML page. + +## 4. Add a configuration file + +Add a `_config.yml` file in your root directory. In `_config.yml`, you can optionally specify the markdown filter you want. By default, the [GitHub Flavored Markdown (GFM) processor](https://github.com/kramdown/parser-gfm) for [kramdown](https://kramdown.gettalong.org/) is used. If no other filter is specified, your config file will automatically apply the following as a [default](/docs/configuration/default/) setting: + +```yaml +markdown: kramdown +kramdown: + input: GFM +``` + +You can find additional [Markdown Options](/docs/configuration/markdown/) in the Jekyll docs, though it's unlikely that you'll need them. + +## 5. Test your pages + +Now run `jekyll serve` and toggle between your `index.html` and `about.html` pages. The default layout should load for both pages. + +You've now extracted your content out into separate files and defined a common layout for pages. + +You could define any number of layouts you want for pages. Then just identify the layout you want that particular page to use. For example: + +```yaml +--- +title: Sample page +layout: homepage +--- +``` + +This page would then use the `homepage.html` template in the `_layouts` folder. + +You can even set [default front matter tags](/docs/configuration/front-matter-defaults/) for pages, posts, or [collections]({% link _docs/collections.md %}) in your `_config.yml` file so that you don't have to specify the layout in the front matter variables. Anyways, setting defaults is beyond the scope of this tutorial, let's get back to work. + +## 6. Configure site variables + +You already configured the page title using {% raw %}`{{ page.title }}`{% endraw %} tags. But there are more `title` tags to populate. Pages also have a [`title`](https://moz.com/learn/seo/title-tag) tag that appears in the browser tab or window. Typically you put the page title followed by the site title here. + +In your `default.html` layout, look for the `title` tags below your `head` tags: + +``` +<title>ACME Website</title> +``` + +Insert the following site variables: + +{% raw %} +```liquid +<title>{{ page.title }} | {{ site.title }}</title> +``` +{% endraw %} + +Open `_config.yml` and add a `title` property for your site's name. + +```yaml +title: ACME Website +``` + +Any properties you add in your `_config.yml` file are accessible through the `site` namespace. Similarly, any properties in your page's front matter are accessible through the `page` namespace. Use dot notation after `site` or `page` to access the value. + +Stop your Jekyll server with <kbd>Ctrl</kbd> + <kbd>C</kbd> and restart it. Verify that the `title` tags are populating correctly. + +{: .note .info} +Every time you modify your config file, you have to restart Jekyll for the changes to take effect. When you modify other files, Jekyll automatically picks up the changes when it rebuilds. + +If you have other variables to populate in your site, rinse and repeat. + +## 7. Show posts on a page + +It's common to show a list of posts on the homepage. First, let's create some posts so that we have something to showcase. + +Add some posts in a `_posts` folder following the standard `YYYY-MM-DD-title.md` post format: + + * `2017-01-02-my-first-post.md` + * `2017-01-15-my-second-post.md` + * `2017-02-08-my-third-post.md` + +In each post, add some basic content: + +```markdown +--- +title: My First Post +layout: default +--- + +Some sample content... +``` + +Now let's create a layout that will display the posts. Create a new file in `_layouts` called `home.html` and add the following logic: + +{% raw %} +```liquid +--- +layout: default +--- + +{{ content }} +<ul class="myposts"> +{% for post in site.posts %} + <li><a href="{{ post.url }}">{{ post.title}}</a> + <span class="postDate">{{ post.date | date: "%b %-d, %Y" }}</span> + </li> +{% endfor %} +</ul> +``` +{% endraw %} + +Create a file called `blog.md` in your root directory and specify the `home` layout: + +```yaml +--- +title: Blog +layout: home +--- +``` + +In this case, contents of `blog.md` will be pushed into the {% raw %}`{{ content }}`{% endraw %} tag in the `home` layout. Then the `home` layout will be pushed into the {% raw %}`{{ content }}`{% endraw %} tag of the `default` layout. + +### How layouts work + +When a layout specifies another layout, it means the content of the first layout will be stuffed into the {% raw %}`{{ content }}`{% endraw %} tag of the second layout. As an analogy, think of Russian dolls that fit into each other. Each layout fits into another layout that it specifies. + +The following diagram shows how layouts work in Jekyll: + +<img src="../../img/jekylllayoutconcept.png" alt="Concept of Jekyll layouts" /> + +{: .image-description} +In this example, the content from a Markdown document `document.md` that specifies `layout: docs` gets pushed into the {% raw %}`{{ content }}`{% endraw %} tag of the layout file `docs.html`. Because the `docs` layout itself specifies `layout: page`, the content from `docs.html` gets pushed into the {% raw %}`{{ content }}`{% endraw %} tag in the layout file `page.html`. Finally because the `page` layout specifies `layout: default`, the content from `page.html` gets pushed into the {% raw %}`{{ content }}`{% endraw %} tag of the layout file `default.html`. + +You don't need multiple layouts. You could just use one: `default`. You have options for how you design your site. In general, it's common to define one layout for pages and another layout for posts, but for both of these layouts to inherit the `default` template (which usually defines the top and bottom parts of the site). + +In your browser, go to `blog.html` and see the list of posts. +Note that you don't have to use the method described here. You could have simply added the `for` loop to any page, such as `index.md`, to display these posts. But given that you may have more complex logic for other features, it can be helpful to store your logic in templates separate from the page area where you frequently type your content. + +{: .note .info} +At minimum, a layout should contain {% raw %}`{{ content }}`{% endraw %}, which acts as a receiver for the *content* to be rendered. + +### For loops + +By the way, let's pause here to look at the `for` loop logic a little more closely. [For loops in Liquid](https://shopify.github.io/liquid/tags/iteration/) are one of the most commonly used Liquid tags. *For loops* let you iterate through content in your Jekyll site and build out a result. The `for` loop also has [certain properties available](https://help.shopify.com/themes/liquid/objects/for-loops) (like first or last iteration) based on the loop's position in the loop as well. + +We've only scratched the surface of what you can do with `for` loops in retrieving posts. For example, if you wanted to display posts from a specific category, you could do so by adding a `categories` property to your post's front matter and then look in those categories. Further, you could limit the number of results by adding a `limit` property. Here's an example: + +{% raw %} +```liquid +<ul class="myposts"> +{% for post in site.categories.podcasts limit:3 %} + <li><a href="{{ post.url }}">{{ post.title}}</a> + <span class="postDate">{{ post.date | date: "%b %-d, %Y" }}</span> + </li> +{% endfor %} +</ul> +``` +{% endraw %} + +This loop would get the latest three posts that have a category called `podcasts` in the front matter. + +## 8. Configure navigation + +Now that you've configured posts, let's configure page navigation. Most websites have some navigation either in the sidebar or header area. + +In this tutorial, we'll assume you've got a simple list of pages you want to generate. If you only have a handful of pages, you could list them by using a `for` loop to iterate through the `site.pages` object and then order them by a front matter property. + +Identify the part of your code where the list of pages appears. Usually this is a `<ul>` element with various child `<li>` elements. Replace the code with the following: + +{% raw %} +```liquid +<ul> + {% assign mypages = site.pages | sort: "order" %} + {% for page in mypages %} + <li><a href="{{ page.url | absolute_url }}">{{ page.title }}</a></li> + {% endfor %} +</ul> +``` +{% endraw %} + +This example assumes each page would have front matter containing both a `title` and `order` property like this: + +```yaml +--- +title: My page +order: 2 +--- +``` + +Here the `order` property will define how the pages get sorted, with `1` appearing first in the list. + +You could also iterate through a list of pages that you maintain in a separate data file. This might be more appropriate if you have a lot of pages, or you have other properties about the pages you want to store. + +To manage page links this way, create a folder in your Jekyll project called `_data`. In this folder, create a file called e.g. `navigation.yml` with this content: + +```yaml +- title: Sample page 1 + url: /page-1-permalink/ + +- title: Sample page 2 + url: /page-2-permalink/ + +- title: Sample page 3 + url: /page-3-permalink/ +``` + +{: .note .info} +If you never wrote any YAML before, you'll get quickly familiar with it. Take a look at [what you can do with YAML](https://learnxinyminutes.com/docs/yaml/). + +You can store additional properties for each item in this data file as desired. Arrange the list items in the order you want them to appear. + +To print the list of pages from the data file, use code like this: + +{% raw %} +```liquid +<ul> + {% for link in site.data.navigation %} + <li><a href="{{ link.url }}">{{ link.title }}</a></li> + {% endfor %} +</ul> +``` +{% endraw %} + +If you have more sophisticated requirements around navigation, such as when building a documentation site, see the [detailed tutorial on navigation](/tutorials/navigation/). + +## 9. Simplify your site with includes + +Let's suppose your `default.html` file is massive and hard to work with. You can break up your layout by putting some of the HTML code in *include* files. + +Add a folder called `_includes` in your root directory. In that folder, add a file there called `sidebar.html`. + +Remove your sidebar code from your `default.html` layout and insert it into the `sidebar.html` file. + +Where the sidebar code previously existed in `default.html`, pull in your "include" like this: + +{% raw %} +```liquid +{% include sidebar.html %} +``` +{% endraw %} + +You can break up other elements of your theme like this, such as your header or footer. Then you can apply these common elements to other layout files. This way you won't have duplicate code. + +## 10. RSS feed + +Your Jekyll site needs an RSS feed. Here's the [basic RSS feed syntax](http://www.w3schools.com/xml/xml_rss.asp). To create an RSS file in Jekyll, create a file called `feed.xml` in your root directory and add the following: + +{% raw %} +```liquid +--- +layout: null +--- + +<?xml version="1.0" encoding="UTF-8" ?> +<rss version="2.0"> + + <channel> + <title>{{ site.title }}</title> + <link>{{ site.url }}</link> + <atom:link href="{{ page.url | prepend: site.url }}" rel="self" type="application/rss+xml" /> + <description>{{ site.description }}</description> + <lastBuildDate>{{ site.time | date_to_rfc822 }}</lastBuildDate> + {% for post in site.posts %} + <item> + <title>{{ post.title }}</title> + <link> + {{ post.url | prepend: site.url }} + </link> + <description> + {{ post.content | escape | truncate: '400' }} + </description> + <pubDate>{{ post.date | date_to_rfc822 }}</pubDate> + <guid> + {{ post.url | prepend: site.url }} + </guid> + </item> + {% endfor %} + </channel> +</rss> +``` +{% endraw %} + +Make sure your `_config.yml` file has properties for `title`, `url`, and `description`. + +This code uses a `for` loop to look through your last 20 posts. The content from the posts gets escaped and truncated to the last 400 characters using [Liquid filters](https://help.shopify.com/themes/liquid/filters). + +In your `default.html` layout, look for a reference to the RSS or Atom feed in your header, and replace it with a reference to the file you just created. For example: + +{% raw %} +```liquid +<link rel="alternate" type="application/rss+xml" href="{{ site.url }}/feed.xml" title="{{ site.title }}"> +``` +{% endraw %} + +You can also auto-generate your posts feed by adding a gem called [`jekyll-feed`](https://help.github.com/articles/atom-rss-feeds-for-github-pages/). This gem will also work on GitHub Pages. + +## 11. Add a sitemap + +Finally, add a [site map](https://www.sitemaps.org/protocol.html). Create a `sitemap.xml` file in your root directory and add this code: + +{% raw %} +```liquid +--- +layout: null +search: exclude +--- + +<?xml version="1.0" encoding="UTF-8"?> +<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"> + + {% for page in site.pages %} + <url> + <loc>{{page.url}}</loc> + <lastmod>{{site.time | date: '%Y-%m-%d' }}</lastmod> + <changefreq>daily</changefreq> + <priority>0.5</priority> + </url> + {% endfor %} + + {% for post in site.posts %} + <url> + <loc>{{post.url}}</loc> + <lastmod>{{site.time | date: '%Y-%m-%d' }}</lastmod> + <changefreq>daily</changefreq> + <priority>0.5</priority> + </url> + {% endfor %} + +</urlset> +``` +{% endraw %} + +Again, we're using a `for` loop here to iterate through all posts and pages to add them to the sitemap. + +You can also auto-generate your sitemap by adding a gem called [`jekyll-sitemap`](https://help.github.com/articles/sitemaps-for-github-pages/). This gem will also work on GitHub Pages. + +## 12. Add external services + +For other services you might need (such as contact forms, search, comments, and more), [look for third-party services](https://serverless.css-tricks.com/services/major). We listed some [integrations on our resources page](/resources/#integrations) but in todays's world of SaaS and APis the list is endless. + +Your Jekyll pages consist of HTML, CSS, and JavaScript, so pretty much any code you need to embed will work without a problem. + +As you integrate code for these services, note that **if a page in your Jekyll site doesn't have front matter tags, Jekyll won't process any of the content in that page.** The page will just be passed to the `_site` folder when you build your site. + +If you do want Jekyll to process some page content (for example, to populate a variable that you define in your site's config file), just add front matter tags to the page. If you don't want any layout applied to the page, specify `layout: null` like this: + +```yaml +--- +layout: null +--- +``` + +## 13. Conclusion + +Although websites can implement more sophisticated features and functionality, we've covered the basics in this tutorial. You now have a fully functional Jekyll site. + +To deploy your site, consider using [GitHub Pages](https://pages.github.com/), [Netlify](https://www.netlify.com/), [Vercel](https://vercel.com), [Render](https://render.com), [Amazon AWS S3](https://aws.amazon.com/s3/) using the [s3_website plugin](https://github.com/laurilehmijoki/s3_website), or just FTP your files to your web server. + +You can also package your layouts, includes and assets into a Ruby `gem` and [make it a Jekyll theme](/docs/themes/). + +## Additional resources + +Here are some additional tutorials on creating Jekyll sites: + + * [Convert a static site to Jekyll](http://jekyll.tips/jekyll-casts/converting-a-static-site-to-jekyll/) + * [Building a Jekyll Site – Part 1 of 3: Converting a Static Website To Jekyll](https://css-tricks.com/building-a-jekyll-site-part-1-of-3/) diff --git a/docs/_tutorials/csv-to-table.md b/docs/_tutorials/csv-to-table.md new file mode 100644 index 0000000..7b7c315 --- /dev/null +++ b/docs/_tutorials/csv-to-table.md @@ -0,0 +1,213 @@ +--- +title: Tabulate CSV Data +author: MichaelCurrin +date: 2020-04-01 20:30:00 +0200 +--- + +This tutorial shows how to use Jekyll to read a CSV and render the data as an HTML table. + +This approach will: + +- use the CSV's first row as the HTML table header. +- use remaining rows for the body of the table. +- preserve the order of the columns from the original CSV. +- be flexible enough to work with _any_ valid CSV that is referenced. + +There is no need to specify what the names of the columns are, or how many columns there are. +The trick to this tutorial is that, when we iterate over the row data, we pick up the _first row_ +and unpack that so we can get the header names. + +Follow the steps below to convert a sample CSV of authors into an HTML table. + + +## 1. Create a CSV + +Create a CSV file in your [Data files]({{ '/docs/datafiles/' | relative_url }}) directory so +that Jekyll will pick it up. A sample path and CSV data are shown below: + +`_data/authors.csv` + +``` +First name,Last name,Age,Location +John,Doe,35,United States +Jane,Doe,29,France +Jack,Hill,25,Australia +``` + +That data file will now be available in Jekyll like this: + +{% raw %} +```liquid +{{ site.data.authors }} +``` +{% endraw %} + + +## 2. Add a table + +Choose an HTML or markdown file where you want your table to be shown. + +For example: `table_test.md` + +```yaml +--- +title: Table test +--- +``` + +### Inspect a row + +Grab the first row and see what it looks like using the `inspect` filter. + +{% raw %} +```liquid +{% assign row = site.data.authors[0] %} +{{ row | inspect }} +``` +{% endraw %} + + +The result will be a _hash_ (an object consisting of key-value pairs) which looks like this: + +```ruby +{ + "First name"=>"John", + "Last name"=>"Doe", + "Age"=>"35", + "Location"=>"United States" +} +``` + +Note that Jekyll _does_ in fact preserve the order here, based on the original CSV. + + +### Unpack a row + +A simple solution would be to hardcode the field names when looking up the row values by key. + +{% raw %} +```liquid +{{ row["First name"] }} +{{ row["Last name"] }} +``` +{% endraw %} + +But we prefer a solution that will work for _any_ CSV, without specifying the column names upfront. +So we iterate over the `row` object using a `for` loop: + +{% raw %} +```liquid +{% assign row = site.data.authors[0] %} +{% for pair in row %} + {{ pair | inspect }} +{% endfor %} +``` +{% endraw %} + +This produces the following. Note the first item in each pair is the _key_ and the second will be +the _value_. + +``` +["First name", "John"] +["Last name", "Doe"] +["Age", "35"] +["Location", "United States"] +``` + +### Create a table header row + +Here we make a table with a single table row (`tr`), made up of table header (`th`) tags. We find +the header name by getting the first element (at index `0`) from `pair`. We ignore the second +element as we don't need the value yet. + +{% raw %} +```liquid +<table> + {% for row in site.data.authors %} + {% if forloop.first %} + <tr> + {% for pair in row %} + <th>{{ pair[0] }}</th> + {% endfor %} + </tr> + {% endif %} + {% endfor %} +</table> +{% endraw %} +``` + +For now, we do not display any content from the second row onwards. We achieve this by using +`forloop.first`, since this will return true for the _first_ row and false otherwise. + + +### Add table data rows + +In this section we add the data rows to the table. Now, we use the second element of `pair` +to find the value. + +For convenience, we render using the `tablerow` tag - this works like a `for` loop, but the inner +data will be rendered with `tr` and `td` HTML tags for us. Unfortunately, there is no equivalent for +the header row, so we must write that out in full, as in the previous section. + +{% raw %} +```liquid +--- +title: Table test +--- + +<table> + {% for row in site.data.authors %} + {% if forloop.first %} + <tr> + {% for pair in row %} + <th>{{ pair[0] }}</th> + {% endfor %} + </tr> + {% endif %} + + {% tablerow pair in row %} + {{ pair[1] }} + {% endtablerow %} + {% endfor %} +</table> +``` +{% endraw %} + + +With the code above, the complete table would look like this: + +<table> + <tr> + <th>First name</th> + <th>Last name</th> + <th>Age</th> + <th>Location</th> + </tr> + <tr> + <td>John</td> + <td>Doe</td> + <td>35</td> + <td>United States</td> + </tr> + <tr> + <td>Jane</td> + <td>Doe</td> + <td>29</td> + <td>France</td> + </tr> + <tr> + <td>Jack</td> + <td>Hill</td> + <td>25</td> + <td>Australia</td> + </tr> +</table> + +That's it - you can now turn a CSV into an HTML table using Jekyll. + +## Next steps + +- Change the field names in the CSV. +- Choose a different CSV. +- Add CSS styling to your table. +- Render the table using a JSON or YAML input file. diff --git a/docs/_tutorials/custom-404-page.md b/docs/_tutorials/custom-404-page.md new file mode 100644 index 0000000..62cc8d4 --- /dev/null +++ b/docs/_tutorials/custom-404-page.md @@ -0,0 +1,72 @@ +--- +title: Custom 404 Page +author: ashmaroli +date: 2017-03-11 17:23:24 +0530 +--- + +You can easily serve custom 404 error pages with Jekyll to replace the default **Error 404 -- File Not Found** page displayed when one tries to access a broken link on your site. + +## On GitHub Pages + +Any `404.html` at the **root of your `_site` directory** will be served automatically by GitHub Pages and the local WEBrick development server. + +Simply add a `404.md` or `404.html` at the root of your site's source directory and include front matter data to use the theme's base layout. + +If you plan to organize your files under subdirectories, the error page should have the following Front Matter Data, set: `permalink: /404.html`. This is to ensure that the compiled `404.html` resides at the root of your processed site, where it'll be picked by the server. + +```markdown +--- +# example 404.md + +layout: default +permalink: /404.html +--- + +# 404 + +Page not found! :( +``` + +## Hosting on Apache Web Servers + +Apache Web Servers load a configuration file named [`.htaccess`](http://www.htaccess-guide.com/) that modifies the functionality of these servers. + +Simply add the following to your `.htaccess` file. + +```apache +ErrorDocument 404 /404.html +``` + +With an `.htaccess` file, you have the freedom to place your error page within a subdirectory. + +```apache +ErrorDocument 404 /error_pages/404.html +``` + +Where the path is relative to your site's domain. + +More info on configuring Apache Error Pages can found in [official documentation](https://httpd.apache.org/docs/current/mod/core.html#errordocument). + +## Hosting on Nginx server + +The procedure is just as simple as configuring Apache servers, but slightly different. + +The nginx configuration file depends on the system in which it is installed. In most systems, it is the `nginx.conf` file, which is usually located inside `/etc/nginx/` or `/etc/nginx/conf/`. However, in other systems like Ubuntu, you would have to look for a `default` nginx configuration file, containing server related information, which is usually located inside `/etc/nginx/sites-available/` or `/etc/nginx/sites-enabled/`. Add the following to your nginx configuration file, _i.e._ either to `nginx.conf` file or to `default` file: + +```nginx +server { + error_page 404 /404.html; + location = /404.html { + internal; + } +} +``` + +If the `server` block already exists, only add the code inside the `server` block given above. +The `location` directive prevents users from directly browsing the 404.html page. + +More info on nginx error page can be found on [nginx official documentation](http://nginx.org/en/docs/http/ngx_http_core_module.html#error_page). + +<p class="note warning"> + Proceed with caution while editing the configuration file. +</p> diff --git a/docs/_tutorials/index.md b/docs/_tutorials/index.md new file mode 100644 index 0000000..430b1c7 --- /dev/null +++ b/docs/_tutorials/index.md @@ -0,0 +1,32 @@ +--- +title: Tutorials +permalink: /tutorials/home/ +redirect_from: /tutorials/index.html +--- + +In contrast to [Docs]({{ '/docs/home/' | relative_url }}), Tutorials provide more detailed, narrative instruction that cover a variety of Jekyll topics and scenarios. Tutorials might contain the following: + +* Step-by-step processes through particular scenarios or challenges +* Full walk-throughs using sample data, showing inputs and results from the sample data +* Detailed explanation about the pros and cons for different Jekyll strategies +* End-to-end instruction in developing a complete feature on a Jekyll site +* Instruction that combines various techniques from across the docs + +In short, tutorials aren't the core reference information in docs. They walk users through processes from beginning to end. + +{: .note .info} +The Tutorials section is new, so there aren't many tutorials yet. You can add a tutorial here to help populate this section. + +Some of these techniques might even guide you through a supporting tool, script, service, or other hack used with your Jekyll site. Feel free to include tutorials involving external services with Jekyll as well. However, note that Jekyll in no way endorses any third-party tools mentioned in tutorials. + +## How to contribute a tutorial + +We welcome your tutorial contributions. To add your tutorial: + +1. Fork the Jekyll project by clicking the **Fork** button in the upper-right corner of the [jekyll/jekyll project GitHub repo](https://github.com/jekyll/jekyll/). +2. Add your tutorial in the `_tutorials` collection. +3. Make sure your tutorial has the same front matter items as other tutorial items. +5. Add a reference to your tutorial filename in `_data/tutorials.yml`. This allows your tutorial to appear in the Tutorials sidebar. +6. Follow the regular git workflow to submit the pull request. + +When you submit your pull request, the Jekyll documentation team will review your contribution and either merge it or suggest edits. diff --git a/docs/_tutorials/navigation.md b/docs/_tutorials/navigation.md new file mode 100644 index 0000000..5b7cbb3 --- /dev/null +++ b/docs/_tutorials/navigation.md @@ -0,0 +1,672 @@ +--- +title: Navigation +author: tomjoht +date: 2017-01-24 15:38:17 -0800 +--- + +If your Jekyll site has a lot of pages, you might want to create navigation for the pages. Instead of hard-coding navigation links, you can programmatically retrieve a list of pages to build the navigation for your site. + +Although there's already information about [interacting with data files]({% link _docs/datafiles.md %}) in other Jekyll docs, this tutorial dives into building more robust navigation for your site. + +There are two primary ways of retrieving pages on a Jekyll site: + +* **Retrieve pages listed in a YAML data source**. Store the page data in a YAML (or JSON or CSV) file in the `_data` folder, loop through the YAML properties, and insert the values into your theme. +* **Retrieve pages by looping through the page front matter**. Look through the front matter of your pages to identify certain properties, return those pages, and insert the pages' front matter values into your theme. + +The examples that follow start with a basic navigation scenario and add more sophisticated elements to demonstrate different ways of returning the pages. In every scenario, you'll see 3 elements: + +* YAML +* Liquid +* Result + +The YAML file in the `_data` directory is called `samplelist.yml`. + +The scenarios are as follows: + +* TOC +{:toc} + +## Scenario 1: Basic List + +You want to return a basic list of pages. + +**YAML** + +```yaml +docs_list_title: ACME Documentation +docs: + +- title: Introduction + url: introduction.html + +- title: Configuration + url: configuration.html + +- title: Deployment + url: deployment.html +``` + +**Liquid** + +{% raw %} +```liquid +<h2>{{ site.data.samplelist.docs_list_title }}</h2> +<ul> + {% for item in site.data.samplelist.docs %} + <li><a href="{{ item.url }}">{{ item.title }}</a></li> + {% endfor %} +</ul> +``` +{% endraw %} + +**Result** +<div class="highlight result" data-proofer-ignore> + <h2>ACME Documentation</h2> + <ul> + <li><a href="#">Introduction</a></li> + <li><a href="#">Configuration</a></li> + <li><a href="#">Deployment</a></li> + </ul> +</div> + +{: .note .info} +For the results in these fictitious samples, `#` is manually substituted for the actual link value (to avoid 404 errors.) + +When you use a `for` loop, you choose how you want to refer to the items you're looping through. The variable you choose (in this case, `item`) becomes how you access the properties of each item in the list. Dot notation is used to get a property of the item (for example, `item.url`). + +The YAML content has two main types of formats that are relevant here: + +* mapping +* list + +`docs_list_title: ACME Documentation` is a mapping. You access the value with `site.data.samplelist.docs_list_title`. + +`docs:` is a list. The list begins each item with a hyphen. Unlike with mappings, you usually don't access list properties directly as you do with mappings. If you want to access a specific item in the list, you must identify the position in the list you want, following typical array notation. For example, `site.data.samplelist.docs[0]` would access the first item in the list. However, this is rarely done. + +With lists, you usually use `for` loops to cycle through the list of items and do something with each item. With navigation menus, you usually insert each list item into `li` tags based on the navigation structure you're using in your HTML theme. + +Each hyphen (`-`) indicates another item in the list. This example just has two properties with each list item: `title` and `url`. You can include as many properties as you want for each item. The order of properties at each position in the list doesn't matter. + +## Scenario 2: Sorted list + +Suppose you wanted to sort the list by the `title`. To do this, convert the reference to the `docs` collection to a variable, and then apply Liquid's `sort` filter to the variable: + +**Liquid** + +{% raw %} +```liquid +{% assign doclist = site.data.samplelist.docs | sort: 'title' %} +<ol> +{% for item in doclist %} + <li><a href="{{ item.url }}">{{ item.title }}</a></li> +{% endfor %} +</ol> +``` +{% endraw %} + +**Result** + +<div class="highlight result" data-proofer-ignore> + <ol> + <li><a href="#">Configuration</a></li> + <li><a href="#">Deployment</a></li> + <li><a href="#">Introduction</a></li> + </ol> +</div> + +The items now appear in alphabetical order. The `sort` property in the Liquid filter applies to the `title`, which is an actual property in the list. If `title` weren't a property, we would need to sort by another property. + +See the [Liquid array filter](https://help.shopify.com/themes/liquid/filters/array-filters) for more filter options. Note that you can't simply use this syntax: + +{% raw %} +```liquid +{% for item in site.data.samplelist.docs | sort: "title" %}{% endfor %} +``` +{% endraw %} + +You have to convert `site.data.samplelist.docs` to a variable first using either `assign` or `capture` tags. + +## Scenario 3: Two-level navigation list + +Suppose you want a more robust list that incorporates multiple sections of heading titles and subitems. To do this, add an additional level to each list item to store this information: + +**YAML** + +```yaml +toc: + - title: Group 1 + subfolderitems: + - page: Thing 1 + url: /thing1.html + - page: Thing 2 + url: /thing2.html + - page: Thing 3 + url: /thing3.html + - title: Group 2 + subfolderitems: + - page: Piece 1 + url: /piece1.html + - page: Piece 2 + url: /piece2.html + - page: Piece 3 + url: /piece3.html + - title: Group 3 + subfolderitems: + - page: Widget 1 + url: /widget1.html + - page: Widget 2 + url: /widget2.html + - page: Widget 3 + url: /widget3.html +``` + +**Liquid** + +{% raw %} +```liquid +{% for item in site.data.samplelist.toc %} + <h3>{{ item.title }}</h3> + <ul> + {% for entry in item.subfolderitems %} + <li><a href="{{ entry.url }}">{{ entry.page }}</a></li> + {% endfor %} + </ul> + {% endfor %} +``` +{% endraw %} + +**Result** +<div class="highlight result" data-proofer-ignore> + <h3>Group 1</h3> + <ul> + <li><a href="#">Thing 1</a></li> + <li><a href="#">Thing 2</a></li> + <li><a href="#">Thing 3</a></li> + </ul> + + <h3>Group 2</h3> + <ul> + <li><a href="#">Piece 1</a></li> + <li><a href="#">Piece 2</a></li> + <li><a href="#">Piece 3</a></li> + </ul> + + <h3>Group 3</h3> + <ul> + <li><a href="#">Widget 1</a></li> + <li><a href="#">Widget 2</a></li> + <li><a href="#">Widget 3</a></li> + </ul> +</div> + +In this example, `Group 1` is the first list item. Within that list item, its subpages are included as a property that itself contains a list (`subfolderitems`). + +The Liquid code looks through the first level with `for item in site.data.samplelist.toc`, and then looks through the second-level property with `for entry in item.subfolderitems`. Just as `item` is an arbitrary name for the items we're looping through, so is `entry`. + +## Scenario 4: Three-level navigation list + +Building on the previous section, let's add one more level of depth (`subsubfolderitems`) to the list. The formatting will get more complex here, but the principles are the same. + +**YAML** + +```yaml +toc2: + - title: Group 1 + subfolderitems: + - page: Thing 1 + url: /thing1.html + - page: Thing 2 + url: /thing2.html + subsubfolderitems: + - page: Subthing 1 + url: /subthing1.html + - page: Subthing 2 + url: /subthing2.html + - page: Thing 3 + url: /thing3.html + - title: Group 2 + subfolderitems: + - page: Piece 1 + url: /piece1.html + - page: Piece 2 + url: /piece2.html + - page: Piece 3 + url: /piece3.html + subsubfolderitems: + - page: Subpiece 1 + url: /subpiece1.html + - page: Subpiece2 + url: /subpiece2.html + - title: Group 3 + subfolderitems: + - page: Widget 1 + url: /widget1.html + subsubfolderitems: + - page: Subwidget 1 + url: /subwidget1.html + - page: Subwidget 2 + url: /subwidget2.html + - page: Widget 2 + url: /widget2.html + - page: Widget 3 + url: /widget3.html +``` + +**Liquid** + +{% raw %} +```liquid +<div> +{% if site.data.samplelist.toc2[0] %} + {% for item in site.data.samplelist.toc2 %} + <h3>{{ item.title }}</h3> + {% if item.subfolderitems[0] %} + <ul> + {% for entry in item.subfolderitems %} + <li><a href="{{ entry.url }}">{{ entry.page }}</a> + {% if entry.subsubfolderitems[0] %} + <ul> + {% for subentry in entry.subsubfolderitems %} + <li><a href="{{ subentry.url }}">{{ subentry.page }}</a></li> + {% endfor %} + </ul> + {% endif %} + </li> + {% endfor %} + </ul> + {% endif %} + {% endfor %} +{% endif %} +</div> +``` +{% endraw %} + +**Result** + +<div class="highlight result" data-proofer-ignore> + <div> + <h3>Group 1</h3> + <ul> + <li><a href="#">Thing 1</a></li> + <li><a href="#">Thing 2</a></li> + <ul> + <li><a href="#">Subthing 1</a></li> + <li><a href="#">Subthing 2</a></li> + </ul> + <li><a href="#">Thing 3</a></li> + </ul> + <h3>Group 2</h3> + <ul> + <li><a href="#">Piece 1</a></li> + <li><a href="#">Piece 2</a></li> + <li><a href="#">Piece 3</a></li> + <ul> + <li><a href="#">Subpiece 1</a></li> + <li><a href="#">Subpiece2</a></li> + </ul> + </ul> + <h3>Group 3</h3> + <ul> + <li><a href="#">Widget 1</a></li> + <ul> + <li><a href="#">Subwidget 1</a></li> + <li><a href="#">Subwidget 2</a></li> + </ul> + <li><a href="#">Widget 2</a></li> + <li><a href="#">Widget 3</a></li> + </ul> + </div> +</div> + +In this example, `if site.data.samplelist.toc2[0]` is used to ensure that the YAML level actually contains items. If there isn't anything at the `[0]` position, we can skip looking in this level. + +<div class="note"> + <h5>ProTip: Line up <code>for</code> loops and <code>if</code> statements</h5> + <p>To keep the code clear, line up the beginning and ending Liquid tags, such as the <code>for</code> loops and <code>if</code> statements. This way you know when the open tags have been closed. If the code will appear in a Markdown page, keep the opening and closing HTML tags flush against the left edge so that the Markdown filter won't treat the content as a code sample. If necessary, you can wrap the entire code sample in a <code>div</code> tag to ensure the code has HTML tags that bookend the code.</p> +</div> + +## Scenario 5: Using a page variable to select the YAML list + +Suppose your sidebar will differ based on various documentation sets. You might have 3 different products on your site, and so you want 3 different sidebars — each unique for that product. + +You can store the name of the sidebar list in your page front matter and then pass that value into the list dynamically. + +**Page front matter** + +```yaml +--- +title: My page +sidebar: toc +--- +``` + +**Liquid** + +{% raw %} +```liquid +<ul> + {% for item in site.data.samplelist[page.sidebar] %} + <li><a href="{{ item.url }}">{{ item.title }}</a></li> + {% endfor %} +</ul> +``` +{% endraw %} + +**Result** + +<div class="highlight result" data-proofer-ignore> + <ul> + <li><a href="#">Introduction</a></li> + <li><a href="#">Configuration</a></li> + <li><a href="#">Deployment</a></li> + </ul> +</div> + +In this scenario, we want to pass values from the page's front matter into a `for` loop that contains a variable. When the assigned variable isn't a string but rather a data reference, you must use brackets (instead of curly braces) to refer to the front matter's value. + +For more information, see [Expressions and Variables](https://github.com/Shopify/liquid/wiki/Liquid-for-Designers#expressions-and-variables) in Liquid's documentation. Brackets are used in places where dot notation can't be used. You can also read more details in this [Stack Overflow answer](http://stackoverflow.com/questions/4968406/javascript-property-access-dot-notation-vs-brackets/4968448#4968448). + +## Scenario 6: Applying the active class for the current page + +In addition to inserting items from the YAML data file into your list, you also usually want to highlight the current link if the user is viewing that page. You do this by inserting an `active` class for items that match the current page URL. + +**CSS** + +```css +.result li.active a { + color: lightgray; + cursor: default; +} +``` + +**Liquid** + +{% raw %} +```liquid +{% for item in site.data.samplelist.docs %} + <li class="{% if item.url == page.url %}active{% endif %}"> + <a href="{{ item.url }}">{{ item.title }}</a> + </li> +{% endfor %} +``` +{% endraw %} + +**Result** + +<style> +.result li.active a { + color: lightgray; + cursor: default; + } +</style> + +<div class="highlight result" data-proofer-ignore> + <ul> + <li class=""><a href="#">Introduction</a></li> + <li class=""><a href="#">Configuration</a></li> + <li class="active"><a href="#">Deployment</a></li> + </ul> +</div> + +In this case, assume `Deployment` is the current page. + +To make sure the `item.url` (stored in the YAML file) matches the `page.url`, it can be helpful to print the {% raw %}`{{ page.url }}`{% endraw %} to the page. + +## Scenario 7: Including items conditionally + +You might want to include items conditionally in your list. For example, maybe you have multiple site outputs and only want to include the sidebar item for certain outputs. You can add properties in each list item and then use those properties to conditionally include the content. + +**YAML** + +```yaml +docs2_list_title: ACME Documentation +docs2: + +- title: Introduction + url: introduction.html + version: 1 + +- title: Configuration + url: configuration.html + version: 1 + +- title: Deployment + url: deployment.html + version: 2 +``` + +**Liquid** + +{% raw %} +```liquid + <ul> + {% for item in site.data.samplelist.docs2 %} + {% if item.version == 1 %} + <li><a href="{{ item.url }}">{{ item.title }}</a></li> + {% endif %} + {% endfor %} +</ul> +``` +{% endraw %} + +**Result** + +<div class="highlight result" data-proofer-ignore> + <ul> + <li><a href="#">Introduction</a></li> + <li><a href="#">Configuration</a></li> + </ul> +</div> + +The `Deployment` page is excluded because its `version` is `2`. + +## Scenario 8: Retrieving items based on front matter properties + +If you don't want to store your navigation items in a YAML file in your `_data` folder, you can use `for` loops to look through the front matter of each page or collection and get the content based on properties in the front matter. + +In this scenario, suppose we have a collection called `_docs`. Collections are often better than pages because they allow you to narrow the list of what you're looping through. (Try to avoid scenarios where you loop through large numbers of items, since it will increase your build time. [Collections]({% link _docs/collections.md %}) help you narrow the scope.) + +In our scenario, there are 6 docs in the `docs` collection: Sample 1, Sample 2, Topic 1, Topic 2, Widget 1, and Widget 2. + +Each doc in the collection contains at least 3 properties in the front matter: + +* `title` +* `category` +* `order` + +The front matter for each page is as follows (consolidated here for brevity): + +```yaml +--- +Title: Sample 1 +category: getting-started +order: 1 +--- + +--- +Title: Sample 2 +category: getting-started +order: 2 +--- + +--- +Title: Topic 1 +category: configuration +order: 1 +--- + +--- +Title: Topic 2 +category: configuration +order: 2 +--- + +--- +Title: Widget 1 +category: deployment +order: 1 +--- + +--- +Title: Widget 2 +category: deployment +order: 2 +--- +``` + +Note that even though `category` is used in the doc front matter, `category` is not a built-in variable like it is with posts. In other words, you cannot look directly inside `category` with `site.docs.category`. + +If you wanted to simply get all docs in the collection for a specific category, you could use a `for` loop with an `if` condition to check for a specific category: + +{% raw %} +```liquid +<h3>Getting Started</h3> +<ul> + {% for doc in site.docs %} + {% if doc.category == "getting-started" %} + <li><a href="{{ doc.url }}">{{ doc.title }}</a></li> + {% endif %} + {% endfor %} +</ul> +``` +{% endraw %} + +The result would be as follows: + +<div class="highlight result" data-proofer-ignore> + <h3>Getting Started</h3> + <ul> + <li><a href="#">Sample1</a></li> + <li><a href="#">Sample2</a></li> + </ul> +</div> + +This might be useful if you're setting up a knowledge base and have dozens of topics in each category, with each category displaying on its own page. + +But let's say you want to sort the items by category and group them under the category name, without hard-coding the category names. To achieve this, you could use two filters: + +* `group_by` +* `sort` + +Here's the code for getting lists of pages grouped under their corresponding category headers: + +**Liquid** + +{% raw %} +```liquid +{% assign mydocs = site.docs | group_by: 'category' %} +{% for cat in mydocs %} +<h2>{{ cat.name | capitalize }}</h2> + <ul> + {% assign items = cat.items | sort: 'order' %} + {% for item in items %} + <li><a href="{{ item.url }}">{{ item.title }}</a></li> + {% endfor %} + </ul> +{% endfor %} +``` +{% endraw %} + +**Result** + +<div class="highlight result" data-proofer-ignore> + <h2>Getting-started</h2> + <ul> + <li><a href="#">Sample2</a></li> + <li><a href="#">Sample1</a></li> + </ul> + <h2>Configuration</h2> + <ul> + <li><a href="#">Topic2</a></li> + <li><a href="#">Topic1</a></li> + </ul> + <h2>Deployment</h2> + <ul> + <li><a href="#">Widget2</a></li> + <li><a href="#">Widget1</a></li> + </ul> +</div> + +Let's walk through the code. First, we assign a variable (`mydocs`) to the collection content (`site.docs`). + +The `group_by` filter groups the collection content by `category`. More specifically, the `group_by` filter converts `mydocs` into an array with `name`, `items`, and `size` properties, somewhat like this: + +```json +[ + {"name": "getting-started", "items": [Sample 1, Sample 2],"size": 2}, + {"name": "configuration", "items": [Topic 1, Topic 2], "size": 2}, + {"name": "deployment", "items": [Widget 1, Widget 2], "size": 2} +] +``` + +Using `for cat in mydocs`, we look through each item in the `mydocs` array and print the category `name`. + +After getting the category name, we assign the variable `items` for the docs and use the `sort` filter to arrange the docs by their `order` property. The dot notation `cat.items` is used because we're accessing the content in the `items` array. The `sort` filter orders the items by their numbers in ascending order. + +The `for item in items` loop looks through each `item` and gets the `title` and `url` to form the list item link. + +For more details on the `group_by` filter, see [Jekyll's Templates documentation](https://jekyllrb.com/docs/templates/) as well as [this Siteleaf tutorial](https://www.siteleaf.com/blog/advanced-liquid-group-by/). For more details on the `sort` filter, see [sort](https://shopify.github.io/liquid/filters/sort/) in Liquid's documentation. + +Whether you use properties in your doc's front matter to retrieve your pages or a YAML data file, in both cases you can programmatically build a more robust navigation for your site. + +## Scenario 9: Nested tree navigation with recursion + +Suppose you want a nested tree navigation of any depth. We can achieve this by recursively looping through our tree of navigation links. + +**YAML** + +```yaml +nav: + - title: Deployment + url: deployment.html + subnav: + - title: Heroku + url: heroku.html + subnav: + - title: Jekyll on Heroku + url: jekyll-on-heroku.html + - title: Help + url: help.html +``` + +**Liquid** + +First, we'll create an include that we can use for rendering the navigation tree. This file would be `_includes/nav.html` + +{% raw %} +```liquid +<ul> + {% for item in include.nav %} + <li><a href="{{ item.url }}">{{ item.title }}</a></li> + + {% if item.subnav %} + {% include nav.html nav=item.subnav %} + {% endif %} + {% endfor %} +</ul> +``` +{% endraw %} + +To render this in your layout or pages, you would simply include the template and pass in the `nav` parameter. In this case, we'll use the `page.nav` to grab it from the yaml frontmatter. + +{% raw %} +```liquid +{% include nav.html nav=page.nav %} +``` +{% endraw %} + +Our include will use this first, then look through each item for a `subnav` property to recursively render the nested lists. + +**Result** +<div class="highlight result" data-proofer-ignore> + <ul> + <li><a href="#">Deployment</a></li> + <ul> + <li><a href="#">Heroku</a></li> + <ul> + <li><a href="#">Jekyll On Heroku</a></li> + </ul> + </ul> + <li><a href="#">Help</a></li> + </ul> +</div> diff --git a/docs/_tutorials/orderofinterpretation.md b/docs/_tutorials/orderofinterpretation.md new file mode 100644 index 0000000..b3ec922 --- /dev/null +++ b/docs/_tutorials/orderofinterpretation.md @@ -0,0 +1,159 @@ +--- +title: Order of interpretation +author: tomjoht +date: 2017-01-29 21:45:03 -0800 +--- + +Jekyll's main job is to convert your raw text files into a static website. It does this by rendering Liquid, Markdown, and other transforms as it generates the static HTML output. + +In this conversion process, it's important to understand Jekyll's order of interpretation. By "order of interpretation," we mean what gets rendered, in what order, and what rules get applied in converting content. + +If an element isn't converting, you can troubleshoot the problem by analyzing the order of interpretation. + +## Order of interpretations + +Jekyll converts your site in the following order: + +1. **Site variables**. Jekyll looks across your files and populates [site variables]({% link _docs/variables.md %}), such as `site`, `page`, `post`, and collection objects. (From these objects, Jekyll determines the values for permalinks, tags, categories, and other details.) + +2. **Liquid**. Jekyll processes any [Liquid](https://github.com/Shopify/liquid) formatting in pages that contain [front matter]({% link _docs/front-matter.md %}). You can identify Liquid as follows: + * **Liquid tags** start with {% raw %}`{%`{% endraw %} and end with a {% raw %}`%}`{% endraw %}. For example: {% raw %}`{% highlight %}`{% endraw %} or {% raw %}`{% seo %}`{% endraw %}. Tags can define blocks or be inline. Block-defining tags will also come with a corresponding end tag — for example, {% raw %}`{% endhighlight %}`{% endraw %}. + * **Liquid variables** start and end with double curly braces. For example: {% raw %}`{{ site.myvariable }}`{% endraw %} or {% raw %}`{{ content }}`{% endraw %}. + * **Liquid filters** start with a pipe character (`|`) and can only be used within **Liquid variables** after the variable string. For example: the `relative_url` filter in {% raw %}`{{ "css/main.css" | relative_url }}`{% endraw %}. + +3. **Markdown**. Jekyll converts Markdown to HTML using the Markdown filter specified in your config file. Files must have a Markdown file extension and front matter in order for Jekyll to convert them. + +4. **Layout**. Jekyll pushes content into the layouts specified by the page's front matter (or as specified in the config file). The content from each page gets pushed into the {% raw %}`{{ content }}`{% endraw %} tags within the layouts. + +5. **Files**. Jekyll writes the generated content into files in the [directory structure]({% link _docs/structure.md %}) in `_site`. Pages, posts, and collections get structured based on their [permalink]({% link _docs/permalinks.md %}) setting. Directories that begin with `_` (such as `_includes` and `_data`) are usually hidden in the output. + +## Scenarios where incorrect configurations create problems + +For the most part, you don't have to think about the order of interpretation when building your Jekyll site. These details only become important to know when something isn't rendering. + +The following scenarios highlight potential problems you might encounter. These problems come from misunderstanding the order of interpretation and can be easily fixed. + +### Variable on page not rendered because variable is assigned in layout + +In your layout file (`_layouts/default.html`), suppose you have a variable assigned: + +{% raw %} +```liquid +{% assign myvar = "joe" %} +``` +{% endraw %} + +On a page that uses the layout, you reference that variable: + +{% raw %} +```liquid +{{ myvar }} +``` +{% endraw %} + +The variable won't render because the page's order of interpretation is to render Liquid first and later process the Layout. When the Liquid rendering happens, the variable assignment isn't available. + +To make the code work, you could put the variable assignment into the page's front matter. + +### Markdown in include file not processed + +Suppose you have a Markdown file at `_includes/mycontent.md`. In the Markdown file, you have some Markdown formatting: + +```markdown +This is a list: +* first item +* second item +``` + +You include the file into an HTML file as follows: + +{% raw %} +```liquid +{% include mycontent.md %} +``` +{% endraw %} + +The Markdown is not processed because first the Liquid (`include` tag) gets processed, inserting `mycontent.md` into the HTML file. *Then* the Markdown would get processed. + +But because the content is included into an *HTML* page, the Markdown isn't rendered. The Markdown filter processes content only in Markdown files. + +To make the code work, use HTML formatting in includes that are inserted into HTML files. + +Note that `highlight` tags don't require Markdown to process. Suppose your include contains the following: + +{% raw %} +```liquid +{% highlight javascript %} +console.log('alert'); +{% endhighlight %} +``` +{% endraw %} + +The `highlight` tag *is* Liquid. (Liquid passes the content to Rouge for syntax highlighting.) As a result, this code will actually convert to HTML with syntax highlighting. Jekyll does not need the Markdown filter to process `highlight` tags. + +### Liquid mixed with JavaScript isn't rendered + +Suppose you try to mix Liquid's `assign` tag with JavaScript, like this: + +{% raw %} +```javascript +<button onclick="someFunction()">Click me</button> + +<p id="intro"></p> + +<script> +{% assign someContent = "This is some content" %} +function someFunction() { + document.getElementById("intro").innerHTML = someContent; +} +</script> +``` +{% endraw %} + +This won't work because the `assign` tag is only available during the Liquid rendering phase of the site. In this JavaScript example, the script executes when a user clicks a button ("Click me") on the HTML page. At that time, the Liquid logic is no longer available, so the `assign` tag wouldn't return anything. + +However, you can use Jekyll's site variables or Liquid to *populate* a script that is executed at a later time. For example, suppose you have the following property in your front matter: `someContent: "This is some content"`. You could do this: + +{% raw %} +```javascript +<button onclick="someFunction()">Click me</button> + +<p id="intro"></p> + +<script> + +function someFunction() { + document.getElementById("intro").innerHTML = "{{ page.someContent }}"; +} +</script> +``` +{% endraw %} + +When Jekyll builds the site, this `someContent` property populates the script's values, converting {% raw %}`{{ page.someContent }}`{% endraw %} to `"This is some content"`. + +The key to remember is that Liquid renders when Jekyll builds your site. Liquid is not available at run-time in the browser when a user executes an event. + +## Note about using Liquid in YAML + +There's one more detail to remember: Liquid does not render when embedded in YAML files or front matter. (This isn't related to order of interpretation, but it's worth mentioning because it's a common question about element rendering.) + +For example, suppose you have a `highlight` tag in your `_data/mydata.yml` file: + +{% raw %} +```liquid +myvalue: > + {% highlight javascript %} + console.log('alert'); + {% endhighlight %} +``` +{% endraw %} + +On a page, you try to insert the value: + +{% raw %} +```liquid +{{ site.data.mydata.myvalue }} +``` +{% endraw %} + +This would render only as a string rather than a code sample with syntax highlighting. To make the code render, consider using an include instead. diff --git a/docs/_tutorials/using-jekyll-with-bundler.md b/docs/_tutorials/using-jekyll-with-bundler.md new file mode 100644 index 0000000..c11df30 --- /dev/null +++ b/docs/_tutorials/using-jekyll-with-bundler.md @@ -0,0 +1,125 @@ +--- +title: Using Jekyll with Bundler +author: mkasberg +date: 2018-03-06 21:33:25 -0700 +--- + +> Bundler provides a consistent environment for Ruby projects by tracking and +> installing the exact gems and versions that are needed. + +[Bundler](https://bundler.io) can be a great tool to use with Jekyll. Because it +tracks dependencies on a per-project basis, it is particularly useful if you +need to run different versions of Jekyll in different projects. + +In addition, because it can (optionally) install dependencies in the project +folder, it can help you avoid permissions issues you might otherwise run into. +The usual way to use Jekyll is to install Jekyll to the system's default gem +installation directory and then run `jekyll new`. In this tutorial, we'll show +you how to create a new Jekyll project using Bundler and without installing gems +outside the project directory. + +<div class="note info"> + <h5>This is not the simplest way to start using Jekyll</h5> + <p> + This tutorial helps you get Jekyll set up using Bundler, and optionally + without any system-wide gem installations. If prefer installing the jekyll + command to your default gem installation directory, you might want the + <a href="{% link _docs/index.md %}">Quickstart</a>. + </p> +</div> + +## Before You Begin + +To complete this tutorial, you'll need to have +[Ruby](https://www.ruby-lang.org/en/) and [Bundler](https://bundler.io/) +installed. You can find the installation instructions on their websites. + +## Initialize Bundler + +The first thing to do is create a new directory for your project and run +`bundle init`. This creates a new Bundler project (by creating an empty +Gemfile). + +```sh +mkdir my-jekyll-website +cd my-jekyll-website +bundle init +``` + +## Configure Bundler Install Path + +This step is optional. In this step, we're going to configure Bundler to install +gems in the `./vendor/bundle/` project subdirectory. The advantage of doing this +is that bundler will install gems within your project folder instead of the +location used by `gem install`. This can help you avoid permissions errors you +might otherwise get during gem installation, depending how you installed Ruby. +If you skip this step, Bundler will install your dependencies to the location +used by `gem install`. + + +```sh +bundle config set --local path 'vendor/bundle' +``` + +<div class="note info"> + <h5>Bundler Config is Persistent</h5> + <p> + This step is only required once per project. Bundler saves your config in + <code>./.bundle/config</code>, so future gems will be installed to the same + location. + </p> +</div> + +## Add Jekyll + +Now, we're going to use Bundler to add Jekyll as a dependency of our new +project. This command will add the Jekyll gem to our Gemfile and install it to +the `./vendor/bundle/` folder (or your default gem installation directory if you +didn't set a custom path). + +```sh +bundle add jekyll +``` + +## Create A Jekyll Scaffold + +Now that Jekyll is installed, we can use it to create the scaffolding for our +site. We need the `--force` parameter because our folder isn't empty - it +already has some Bundler files in it. We run the `bundle install` separately +because Jekyll gets confused if the Gemfile already exists. + +```sh +bundle exec jekyll new --force --skip-bundle . +bundle install +``` + +## Serve the Site + +Your new website is ready! You can serve the website with +`bundle exec jekyll serve` and visit it at +[http://127.0.0.1:4000](http://127.0.0.1:4000). From here, you're ready to +continue developing the site on your own. All of the normal Jekyll commands are +available to you, but you should prefix them with `bundle exec` so that Bundler +runs the version of Jekyll that is installed in your project folder. + +## Commit to Source Control + +If you're storing your new site in version control, you'll want to ignore the +`./vendor/` and `./.bundle/` folders since they contain user- or +platform-specific information. New users will be able to install the correct +dependencies based on `Gemfile` and `Gemfile.lock`, which should both be checked +in. You can use this `.gitignore` to get started, if you want. + +**.gitignore** + +``` +# Ignore metadata generated by Jekyll +_site/ +.sass-cache/ +.jekyll-cache/ +.jekyll-metadata + +# Ignore folders generated by Bundler +.bundle/ +vendor/ +``` diff --git a/docs/_tutorials/video-walkthroughs.md b/docs/_tutorials/video-walkthroughs.md new file mode 100644 index 0000000..521bd5e --- /dev/null +++ b/docs/_tutorials/video-walkthroughs.md @@ -0,0 +1,33 @@ +--- +title: Video Walkthroughs +author: giraffeacademy +date: 2017-10-02 15:20:08 -0400 +--- + +[Giraffe Academy](https://www.youtube.com/c/GiraffeAcademy) has a series of videos that will walk you through the basics of using Jekyll. In this series you'll learn everything from installing Jekyll on your computer and setting up your first site, to using more complex features like variables, layouts and conditionals. + +<div class="videoWrapper" > + <iframe src="https://www.youtube.com/embed/T1itpPvFWHI?rel=0" frameborder="0" allowfullscreen></iframe> +</div> + +## List of Lessons + +1. [Introduction to Jekyll (see above)](https://www.youtube.com/watch?v=T1itpPvFWHI&list=PLLAZ4kZ9dFpOPV5C5Ay0pHaa0RJFhcmcB&index=1) +2. [Mac Installation](https://www.youtube.com/watch?v=WhrU9m82Wm8&list=PLLAZ4kZ9dFpOPV5C5Ay0pHaa0RJFhcmcB&index=2) +3. [Windows Installation](https://www.youtube.com/watch?v=LfP7Y9Ja6Qc&list=PLLAZ4kZ9dFpOPV5C5Ay0pHaa0RJFhcmcB&index=3) +4. [Creating a Site](https://www.youtube.com/watch?v=pxua_1vyFck&index=4&list=PLLAZ4kZ9dFpOPV5C5Ay0pHaa0RJFhcmcB) +5. [Front Matter](https://www.youtube.com/watch?v=ZtEbGztktvc&index=5&list=PLLAZ4kZ9dFpOPV5C5Ay0pHaa0RJFhcmcB) +6. [Writing Posts](https://www.youtube.com/watch?v=gsYqPL9EFwQ&list=PLLAZ4kZ9dFpOPV5C5Ay0pHaa0RJFhcmcB&index=6) +7. [Working With Drafts](https://www.youtube.com/watch?v=X8jXkW3k2Jg&index=7&list=PLLAZ4kZ9dFpOPV5C5Ay0pHaa0RJFhcmcB) +8. [Creating Pages](https://www.youtube.com/watch?v=1na-IWfv08M&index=8&list=PLLAZ4kZ9dFpOPV5C5Ay0pHaa0RJFhcmcB) +9. [Permalinks](https://www.youtube.com/watch?v=938jDG_YPdc&list=PLLAZ4kZ9dFpOPV5C5Ay0pHaa0RJFhcmcB&index=9) +10. [Front Matter Defaults](https://www.youtube.com/watch?v=CLCaJJ1zUHU&index=10&list=PLLAZ4kZ9dFpOPV5C5Ay0pHaa0RJFhcmcB) +11. [Themes](https://www.youtube.com/watch?v=NoRS2D-cyko&list=PLLAZ4kZ9dFpOPV5C5Ay0pHaa0RJFhcmcB&index=11) +12. [Layouts](https://www.youtube.com/watch?v=bDQsGdCWv4I&list=PLLAZ4kZ9dFpOPV5C5Ay0pHaa0RJFhcmcB&index=12) +13. [Variables](https://www.youtube.com/watch?v=nLJBF2KiOZw&index=13&list=PLLAZ4kZ9dFpOPV5C5Ay0pHaa0RJFhcmcB) +14. [Includes](https://www.youtube.com/watch?v=HfcJeRby2a8&index=14&list=PLLAZ4kZ9dFpOPV5C5Ay0pHaa0RJFhcmcB) +15. [Looping Through Posts](https://www.youtube.com/watch?v=6N1X5XffuUA&index=15&list=PLLAZ4kZ9dFpOPV5C5Ay0pHaa0RJFhcmcB) +16. [Conditionals](https://www.youtube.com/watch?v=iNZBEki_x6o&list=PLLAZ4kZ9dFpOPV5C5Ay0pHaa0RJFhcmcB&index=16) +17. [Data Files](https://www.youtube.com/watch?v=M6b0KmLB-pM&list=PLLAZ4kZ9dFpOPV5C5Ay0pHaa0RJFhcmcB&index=17) +18. [Static Files](https://www.youtube.com/watch?v=knWjmVlVpso&index=18&list=PLLAZ4kZ9dFpOPV5C5Ay0pHaa0RJFhcmcB) +19. [Hosting on GitHub Pages](https://www.youtube.com/watch?v=fqFjuX4VZmU&list=PLLAZ4kZ9dFpOPV5C5Ay0pHaa0RJFhcmcB&index=19) diff --git a/docs/css/screen.scss b/docs/css/screen.scss new file mode 100644 index 0000000..766f964 --- /dev/null +++ b/docs/css/screen.scss @@ -0,0 +1,11 @@ +--- +--- + +@import "mixins"; +@import "normalize"; +@import "gridism"; +@import "pygments"; +@import "font-awesome"; +@import "fonts"; +@import "docsearch"; +@import "style"; diff --git a/docs/favicon.ico b/docs/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..e8b4e38170f1496f486405df504c478661906eb7 GIT binary patch literal 5430 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 literal 0 HcmV?d00001 diff --git a/docs/fonts/FontAwesome.eot b/docs/fonts/FontAwesome.eot new file mode 100644 index 0000000000000000000000000000000000000000..4f0b1de2bd3b048212f62503d9463d277b987758 GIT binary patch literal 1804 zcmd;KXJCk6V_;xpU}8W8j0_VPK_Wf@AQBmag&7za#3u(@A&W!UVhnB!`3!jsB@B)X z<qW9|#SHljxeTcc3=Dh>K@6!3=?tX|ISh#mMGOoKVhmvnsSHI7#SEEXJqiql40;R( zU>RJdFn}D(z`)JWz`)F4;2*4S#4p72nSp^}4+8^3LUL|m!H2&eJ~1#b$}li6#H1${ z7cej|2rw`(RxmIyaHQu{rbP?iTg|}0*ucQR6qAvfn8NLoewcxQ`3?gEgIPvKY9f09 z^Kk|S77+#p29=E5k_ui)22KVB77GRj28o>f<V40WhBgKU7ErkG<tA1XFmN+8Gcd3u zFfcGE<R#{&wodsf%)r3Xz`($`tsuX+gn=0pGHf9I3JlE59E=kfSQ!|YW-^^&U|{Hn z(2TDcj2SsW>X0y_01^kxbYNikaFF@H{{svT%m)}8pkV=)0@)5`fNTR{83snM%`yzk z44^2JVPFC4;9=lkU}9ilU}RAE;PBz#|G)qLgY_YJjQ=0=AK`a_>H|xH2}TCS|Nj~I z!KQ%)K#9iO5@a~hVk{ty%t_2i4D1Xt42ld!3=H;+>g=M%qQ;CSYRXD%?2LSjq9S6( zMrP)Wc8sR#;*83sj3R9OjOylgjK)S{@{DqfqUTN8OgwEFwb@yjS(*NwVq#)vV`61y zG-YCvfA>%I9di;7XZ*W(E@?h~X|8ro9#4}t6I)LvAvSg<kZM*Y5QEW-iIpi2q?~aH zpEOteyErZ$9<DYnX@0O188{g@nb$F|V^C)BU;xFXiJCs69wW$wa*U!PY|2V%>R^|G z9As={X3uEH$Ou=%5Ag+5&Rmbt)Wpo#NKD+05#kv>Mr9?id8Q_2=IY2ikU21Kim<VZ z!#&OHz`;?@A;Qfg!pSGCATBDy&&kQbQOY60%`L(a!ol$`55yDYg77#v%GpJsYQ@B4 z_&B+k7^K8Q#BCH6ZN$sOC8Z^d#qAUoZN<yQC6UA>q$G?aY?UD5j11f&9OdksT%3F| zVqyx?e4HY@+#(!h92^|%A?zZI^WYo~VUQ!rIoLTk`DH}KAxi({D%yw#OGrpcl!@CY zD%pt}OGrzKmrK|vD%gq#i%WvUZ55R4B#b1aBqYitY(U`-N=%Fl3|4`Y#pC&HzA|uw z6AZ%@2f-eYG>itv3<EeNgM`6l0ytkVK-4p@LrRel6>Jb02nST!F()xFFmDItZ3YGg zE(Q?>Iff!eJ!qa{V`zjL1yTypgdq7CVCFM2a6@%4FfcK&GqgbMWoF=JaDuW~7`Pc` zLfNbg+zg6PHX8#sLo$@j#URD73zXwnI2jlj#2C1tY$gUihHfaEnL&)f7s_T~5Mx*Z zWwSDfG3Y?qYz$%yc~CYNg9*bGxBR>k$MV$T{M=ORC39G6QE_H|o`RvC0rujUpa}#O z1f`~z<|G!Omj48GffOjDr{<*=C6=V7C?r)XcqZrj=I7_>!9AkDkj{|GkO!_r5*bPu lQW;Vh6c~~iDj5_QJQ<Q1@)>*?^1)RLC|*IOA|ehkSOBW{{cZpN literal 0 HcmV?d00001 diff --git a/docs/fonts/FontAwesome.svg b/docs/fonts/FontAwesome.svg new file mode 100644 index 0000000..32ad8fb --- /dev/null +++ b/docs/fonts/FontAwesome.svg @@ -0,0 +1,12 @@ +<?xml version="1.0" standalone="no"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" > +<svg xmlns="http://www.w3.org/2000/svg"> +<metadata>Generated by IcoMoon</metadata> +<defs> +<font id="FontAwesome" horiz-adv-x="1024"> +<font-face units-per-em="1024" ascent="960" descent="-64" /> +<missing-glyph horiz-adv-x="1024" /> +<glyph unicode=" " horiz-adv-x="512" d="" /> +<glyph unicode="" glyph-name="pencil" horiz-adv-x="866" d="M207.429 73.143l52 52-134.286 134.286-52-52v-61.143h73.143v-73.143h61.143zM506.286 603.428c0 7.429-5.143 12.571-12.571 12.571-3.429 0-6.857-1.143-9.714-4l-309.714-309.714c-2.857-2.857-4-6.286-4-9.714 0-7.429 5.143-12.571 12.571-12.571 3.429 0 6.857 1.143 9.714 4l309.714 309.714c2.857 2.857 4 6.286 4 9.714zM475.429 713.143l237.714-237.714-475.429-475.429h-237.714v237.714zM865.714 658.286c0-19.429-8-38.286-21.143-51.429l-94.857-94.857-237.714 237.714 94.857 94.286c13.143 13.714 32 21.714 51.429 21.714s38.286-8 52-21.714l134.286-133.714c13.143-13.714 21.143-32.571 21.143-52z" /> +<glyph unicode="" glyph-name="chain, link" horiz-adv-x="951" d="M832 256c0 14.857-5.714 28.571-16 38.857l-118.857 118.857c-10.286 10.286-24.571 16-38.857 16-16.571 0-29.714-6.286-41.143-18.286 18.857-18.857 41.143-34.857 41.143-64 0-30.286-24.571-54.857-54.857-54.857-29.143 0-45.143 22.286-64 41.143-12-11.429-18.857-24.571-18.857-41.714 0-14.286 5.714-28.571 16-38.857l117.714-118.286c10.286-10.286 24.571-15.429 38.857-15.429s28.571 5.143 38.857 14.857l84 83.429c10.286 10.286 16 24 16 38.286zM430.286 658.857c0 14.286-5.714 28.571-16 38.857l-117.714 118.286c-10.286 10.286-24.571 16-38.857 16s-28.571-5.714-38.857-15.429l-84-83.429c-10.286-10.286-16-24-16-38.286 0-14.857 5.714-28.571 16-38.857l118.857-118.857c10.286-10.286 24.571-15.429 38.857-15.429 16.571 0 29.714 5.714 41.143 17.714-18.857 18.857-41.143 34.857-41.143 64 0 30.286 24.571 54.857 54.857 54.857 29.143 0 45.143-22.286 64-41.143 12 11.429 18.857 24.571 18.857 41.714zM941.714 256c0-43.429-17.714-85.714-48.571-116l-84-83.429c-30.857-30.857-72.571-47.429-116-47.429-44 0-85.714 17.143-116.571 48.571l-117.714 118.286c-30.857 30.857-47.429 72.571-47.429 116 0 45.143 18.286 88 50.286 119.429l-50.286 50.286c-31.429-32-73.714-50.286-118.857-50.286-43.429 0-85.714 17.143-116.571 48l-118.857 118.857c-31.429 31.429-48 72.571-48 116.571 0 43.429 17.714 85.714 48.571 116l84 83.429c30.857 30.857 72.571 47.429 116 47.429 44 0 85.714-17.143 116.571-48.571l117.714-118.286c30.857-30.857 47.429-72.571 47.429-116 0-45.143-18.286-88-50.286-119.429l50.286-50.286c31.429 32 73.714 50.286 118.857 50.286 43.429 0 85.714-17.143 116.571-48l118.857-118.857c31.429-31.429 48-72.571 48-116.571z" /> +</font></defs></svg> \ No newline at end of file diff --git a/docs/fonts/FontAwesome.ttf b/docs/fonts/FontAwesome.ttf new file mode 100644 index 0000000000000000000000000000000000000000..47e2443e99084e51e746397225bb4d6dabc43b16 GIT binary patch literal 1624 zcmZQzWME+6W@unwW-#y%);Ho8;`z+Lz_5pbfgvF|H?iQu-w&S{7#L+37#L#G6N?KN z7#IW?7#J%U7#KLxb1KuKh3~CqU|?)uU|@>LNKH)P_DMg?z`%Tmfq}s+BO^7Dy@2^R z0|Sc)0|SFfMs7(3uOtH}0|Sc%0|SFZPJVJCV;Dml0|QF{0|NtJZem3N12;o60|QF} z0|SFXUSe))>y)p;3=Av{3=E9h3i69f7?>Ft7#P?<`V|<MnK>9IFt9Q(FwJB-!@$7M z51|=fGZ-^+g47{lMgb%anCZa4@Zliyf&T{>9GDL<I504P!vZV?vK`C-*#^Qg42)o# zWf+*js$>{gz&dysI2f21SQr=?6h1h7IQakX|Nmfp2p;4A$NWe5U7-5Fl3;?7f${%; z27a(<U;zdu1_pCWP;?M2#sbpFoWz{Oz|J7UpvYjvz+lg)&Ms;!YRqV&rmV!q&dA3o zDk5fVWM<B2$7rf9&Zun4D8k0isBUh@Xlx`V&nU+zdfueX#M72ho1K-JmFeFpCMI?^ zCRS!fQzj<)cmGu1F(>hG#=nc_lIG)==4$8U@ib{OvGrsUVq<3lsb*yYF&NF5Sef!b z${CmNNpr=&i{s+q;cDZO<_9~Gfs=ugc^&gQ24w~h22fm@sOdB6F@jtu$0#bormUo< z4t6QXLB>XA_KbFnjBqvl5MMy$%=H*eP0Wmq#Ki3wA)eu5R8|6;XKG?*u8zzDnFI5t z2phXN+|$es9315wBHTP8oP5#>;-WJAoSYmSr5qyM+#(zy9321hKs-?{2#<rKoLv;E zR!mHWkCTguK}tMC+(uE+M!Za1Qd+`T+)h!^R=iwX5=mS_O2SCORtX}`$iOYaQO?fE z#mOflCZ-_G$0@?gEy7X8!NI{E!Y;x%56<Bb205afgPntuUq)0MqV!*`qK$a4goLC- znYfLjlAXA*gtVl1xrB|Pf~|P4xFks2Rzb;5!bn0&LZVE<1{Cg~#Kg$JU==u7Jf7d? zD+4z;!7yBL5bOa-!)S2KFo07sNEn<C!1;m!qMmsjQi_DAV1vj&I7|!-49rOk49wd> zd7FWOfr~+eL5`t_Q4gA@*cck2MuC(<G$BYn2AKJb4BSv13=B*R><leXdzl%y8JwVO z76xvHnNT(>12=;rl+DJ#&5#Ubb1_IU>;mOD7ET661~CS1D4U6akD(jNW@Zp$@P)Ej z7{nNsK-sJeVhlP^HXDN&LmrgP#bCm4#VtRt#IZcJI6pU)!HprGA&;Si!I7byA(f$+ zA)g_aAvG+us5mn}Pr*>nfFX<_m7$2Cm?0CaP=UdaL65<Jpa}#O1f`~z<|Gy|1TmyC zq%)K<<S-;M6cN+~QlOBYnwMIXSdyBekW{JQnVj#NpP#1(_lN>RIzuW$9@y213?&Sy k3@Ho>3`q=?3<?aM49N`n489EcV84Lk6;vuB;t+!c0B4-*4*&oF literal 0 HcmV?d00001 diff --git a/docs/fonts/FontAwesome.woff b/docs/fonts/FontAwesome.woff new file mode 100644 index 0000000000000000000000000000000000000000..bec5c4fe77ce01bf6d1dfe7240cc78b10c9eb535 GIT binary patch literal 1700 zcmXT-cXMN4WME)mU|Yh#4W=U?d=MKI`v>bAF)%Q4FfcGAfH1!h&*$Xa!~zBe#ta4q zh8Ph3@b|-~^u%J2*bxQ>29R117D&&jOk-eRJj1}i6a&K1!uM8Zq$Z{?FtBJaFff>b zFt<<o;f##bL<R;HkohVM3=Asl1<c1Ya!V>07+6{u7#Ji#m{*d4GbcY8q;3ua0|Orj zGlns=<tA1XFfg#JVPIfT0Ap^3=Dft*R0al?GYkxj+d#N=%2(ln{NfS@1~!m+5OvH9 z3=GW79E=kfSQ!|YW-^^&U|{Hn(2TDcj2Ssm!-0WO08N0wfq~(}LFNPh4=^||9{}-C z!VqK&0|VF=1_lNh21YPjhJhKZN``?2tb>PvgMo>Gg@KVl;e*46ga7~j{|~kX!DIaY znEwdB3sfIi5=<~MF#i9~zz;SJEWp6Tz+i3(j#e-M7Q`>c0#d`A#GJ&y&LG2}$Y8|4 zV9%(|E@~`l%xI#fti;C7$j2xuB4%u4X3l8GXsRyGsBFq8!p6_2Zf?hDY$PVnD90#z z-lWaM)0R=2ot2rD>E9_PCU!O^R%S+1CMNlJ|5V>GC-HE`zl-OR=Hr*<YUkwfG-)%j z^<)xaV`l=XW@Q2~7|obinesr&8JF-$bH%@l<Kp4rYU7gT2Ro60lYx_Y9rHQ{Wd;w1 z1_lO36E%HCJw}iV<rqap*p!vj)WI$VImp<^%%0JX5ut`3;tQypxgMjbiJ7sHn7ADy z#4~)1%1U7KOij$p)scB1b70;SVPhAEdz#sSgQJ{7gquf%lTTVfTvUdilaqs^ltYA@ zTZAKogX3Qwh$qSg;c;-3vx`F2iiyeaadI&+NQsAt+bAm9h?j{=N=q1v+bJsAikFK^ zB8f{#Nf=4kDnY~<8Ms9_%Go)&IQeA6#1y3YI7N85ML5bhI5^lt*hLuU!8shlAV-vQ zuyb(o%ZQ3Yl>W<Av=I-MkdTxp6Sq-RvJ*F!kd_oLm#|S(uoVv$mjsF1Dk#}W7)eM; zNR&y~fWjS=ju;sjtO6&C$Mf5KW#9%U7=|kjf;}K<7!8gYP`+mZi83&N(j+)vFhJBZ zuLFrd<scLrL<YhEX=6@eU|`<P0LgV+3?dA23`LB35VIKA7#g8Qfs{ftAxJ(3nE8z0 z(hTHk1_mYuc7_(Hz03^U3{Frs3j;U9OemX`ftx`Q%4TEWW=MvzxfrAvc7bvn3nv33 zgBSxhl+DDz$IuOBGc$-W_(It%3}OsRplntKF$Ntdn~gz?ArH#tVlZL2;+CIR;#i(q zoS&P@;Kq>8kjGHM;K)$UkjhZZkk63IkQ$a+RGgWgr(mdOz!1ie%232m%#aCIsK8*z zpvPc9&;)`Cf>P5<a}tXff*4X6(iut_au^aBiU{fgDNsmH%}XsxEJ;mKNUBuuOwRYs z&(G6?dqjaDogtMW5A5nhh7yKUh7<+`h9rhc1_cIBhGd3(249AJuwOv&3Mv^9afrbJ E0KiP}mH+?% literal 0 HcmV?d00001 diff --git a/docs/fonts/lato-v14-latin-300.woff b/docs/fonts/lato-v14-latin-300.woff new file mode 100644 index 0000000000000000000000000000000000000000..ab45ab76bac3d81c9103c6b662a6497845aacd9b GIT binary patch literal 29852 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 XJQDfJc7ZF`x~(&X^cZ%{5sUx;5T=St literal 0 HcmV?d00001 diff --git a/docs/fonts/lato-v14-latin-300.woff2 b/docs/fonts/lato-v14-latin-300.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..136337fdc2cae59ecbc3e94aa67703a5856d73b3 GIT binary patch literal 23208 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 Ctoon; literal 0 HcmV?d00001 diff --git a/docs/fonts/lato-v14-latin-300italic.woff b/docs/fonts/lato-v14-latin-300italic.woff new file mode 100644 index 0000000000000000000000000000000000000000..0287996f125d3f881bcecc742185eba99696f700 GIT binary patch literal 22312 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 literal 0 HcmV?d00001 diff --git a/docs/fonts/lato-v14-latin-300italic.woff2 b/docs/fonts/lato-v14-latin-300italic.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..fd1977b68a9a6b2b877fa39abcadaac6eb7f2e14 GIT binary patch literal 17528 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�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 literal 0 HcmV?d00001 diff --git a/docs/fonts/lato-v14-latin-700.woff b/docs/fonts/lato-v14-latin-700.woff new file mode 100644 index 0000000000000000000000000000000000000000..1d9d75bc6a53cbca1159c91012f116dea6193bc3 GIT binary patch literal 27848 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 M%Vur?7A9>50B&dTga7~l literal 0 HcmV?d00001 diff --git a/docs/fonts/lato-v14-latin-700.woff2 b/docs/fonts/lato-v14-latin-700.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..d88f1af8c8a003bbfe27f0f8ed091b696dc6090d GIT binary patch literal 22820 zcmXT-cQayOWME)mh*V(^0MSpMFfc?Mhlqjrs91WLkW2wv0z;z>haP8dsvr+n)1Q#0 zhZ@|?4h)QDOe)N6MJx)etqklf&OF}IaaUDWO+Qz-otY!#=MgE_4TVX}Y&+-mb|gGz zXq*u}`S1Tsch_S+MwiNxHM%BOZ!(B<N!!(Aq*=ABx3N}k@B4})D-LX)UlywMSd}Sw zpKflZcl1oX#9t>q#u>DWpE;TKT3^hmdUmTgUzg>@qp~F*c&6NPu51qe`K!71f>YFv zug$w3Ub*^Dn}@||+V<URnM8|b%(p1~+w##@OGT#puJpC~;`~?^Gri=Lxhl0C9^dy{ zdViYqUdY8o>#|T(@%nwv?%shP%NU+LbD5xQkfj{9rm?s8Mn{~g^j?-@ZaEj4d&N|| zSfmXYlDoQ+1k9eT-LYujn;*Tm&d>Vh*k+>rBABK6{Fi^{Z~LA8{8xU9wQ>GGy?2}% zo2Ce#Q{`6SSYf{+JJ3aY)~=<th3xloW$JHiEZTX8pU>M#dnWhOu#8)NHcNNqTQtk> z`ujaSZIZ);0@igF`i<(Z?$%9Rb*JifmbA=?)%Jhv^Doz5w<~#nrep0!d9ON;U!T}R zII8r1P04+w=R5Tu`-gPv|GVz|e_;Qhvw8U<;YW*a99w&0RepZ$+b6T;XsUh`KD$-U zaoMIF(@x5&ziqxS`CFrdTuR7o4&!ul+xAmA7tS)>+Na|CG}UOnulmh@%O_l!6TUby z!S>p$xfkcnJa0d7_EvS9gsyXUHhX1VS@~mK`<jSVCuTA+@9wKTvadZ%^;@UER>&uZ z90i81KmY1Wb0hL1@@M?YJFL^cR^;ZVU9~wTmY!xN9e(MX#3z5@vC+M^`#85<^MCvL zRfpBw+D=!B{tnS`>OHc2iExPGHr|}ee?1l5wxu+P{+=Ol@sifYRXaTYzly(=^33bh z!JN_{&ZRo%BeX64<h$HmCa}SV-)U7?W2bz*#lN@Nam5WirM;=Qzb#f5t59N86^l^% z|K4}2*Z*u2jQ}g>zHhHqY`qlqagHzNB%}1)Ee|6v|NX_xf47(I0Goo4Z?8+^C66UK zn&)R;{qmm8l-Idq%FL!MewU=4YxEUQxfUTjxA2x_cG2V5WgnV8EV}SZ;mo^7+|!r; zzyDhM@9oewwt^g$9}LE=lYZ|oxwh$tXXcwtQ@kEFHyvy&;y!b;waw{$;g1u_9UUyW zp_@&F3l~Q$_Bi)s|1KS0WdWw@`%L|+(T6;5=kJp}wk69@<)-i4-M`Q0%wJ(*t)Zcz zu<XpA@2BTv9xXb#GxLALqEe3Y4cwQWs5&t!=5D;fBV$vfbYSXdvvq8T#O{gQS=+7k zrOWkoIP)7GrmkPQvlI;e$KBfgYwIU_Va2=CcIvO*-Tr*aH>0X4MM}#;(}Utz+e_1L zt+{!wVQSRrUrCjI|Gqvb(6YNIr?}YtWmAt$`1#gFC5{!^u>rDC^Y$6pxZXd-@zX$S zvBv8Bu=_V1QaW`O{wZ*;T-2zh^x$q#Tx!tA)|xpM`t0$6x;u+nge1B=p4r76KDd9y zjN@5lEiOwY`LUV5GuZIq4~v2#w@_D;mQRaE;G&})qUv!>Vr8vnE0){756!QVzT@OA zX`FiQl7Hmp)bkTJf3JNLyw0Zb^HcqR4FM08j{e&3ax!S?=`Hu%X6J6b>$m%D`GrJx z+44)784(pOAtgRJF*Qy%*X{Dt3E$^*)NAVMkhNiZ-41*5YGl63=vaHqVe{u{FIT80 z#IkQ`Dq?e}eEcUjan}Sf-O|u~NAeXEG*onyq+Iw~4+@?L(q9_t79lflg7Nd`dNI3J zuF$-=O2*mK&uyR9Y|o|6Z?`tDO)YVB&wul2$-2ypHoN1s>)F1fF|cg2aN}N<r+aeO zt#j)RFSH7O|6KXJ`m0-yAAY<RE}X2QqN%EzT)X>lZ-sMW0gF)Y!ABojQgma^>pEX~ zQu4&}VOMb0vQ42O(bn7bdiUO2@@wjQvq$UXZ^$>uZFaP8V`Pf%iLZAE@Q83R`Td5u zy-&uvQ0fRjSF`ej3?8v|b%Eet+Q%Pkk=ym+Meo|l>y}4^SsYbSne7yHtja86_O_hw z)3zT=|9yYebLl(mYv;b)wD@zGL+`zFH@+E)uQ<eT%kJ_wwmrIg$`o~@PHp;l)$o#! z=w{8+-`qckS%0(0I9K<w;OcYxKUSG*t{<5)Wzw{b^J>>Y-QdCsa)UGDEr}e<4=r;V zi^cUOFOB`)_I9mopzFKJdFktSUJJN><F~r!U(dwIuyd=fEZDSU*{sVZ-CHu=uCQL4 zosd6$^<|zHmoFQBy1{dTA!`1w%Ct{sqwgDSn8_NwWsBbJsE=EsR$pwD++Oh0U2=7H z^xvDhmNCy?{jJGa;{U~a|GHqC-Y09<ALD1&0Y&1h8LwNGOxe=&_04R{f)^(?3NIJX z;OTSVQ*~<A<mU4~G4sSu?&&9Y?Rdvq{5oon|GA(z%`5SRUQ(xm0(0Nq*<})EZ}i-; zZPJVSRN1Yp3C!2zyBoaN9B!vS+HmPn_~~~ds`1{YU+dqT-}=cWY|;?}p2Hi2H|rM} z$XXS>Ig%-$D#GTja8W{s&tE~!{aO9OBh4bJCu>*wAHDMHWmDh12#57-7i>y-Z$J9i z)pK!S=Z2KEdp8=(uG_rh$B|hML0(bEssz@9oV&;nlmOQpetmDBp!)GMi<hnYc-BM9 z^0wRSecV=6$&7!c91^;3r`Eg4Pm6k=JiRy|z3^kFX(QO<3Uz@JGL0_D8!T)TBjvB} ztyT8()9atL>)1vs&+n5bx@ShoMt`rK-~acb`n-zW@s1mp?%w3J`sy<Edy_xkc)t%4 zuT_igeT#knz0W@=$o3kGm3hMfF~tCwv5ggX=7sI-2Kna_15<QQ=uJ&EMNsnIdxD=! zn~$mUSVCciQ~vKG^R*_gy2NmzMEUHQ7~8w2Kd$BiXS7>$fBUOXb}%~eJ+z{I&$O$i zFYI`}WS+G8_+O3JNAU88o(D2t<#MKM+#?<zS*{?ne-qyY$E!1UeX9tme7*Wuo^J#= zq|7>eqql9hzOv>_Y5q)^dFreyR;-yM6?ffY+ESyO=MNrDo*W*0{YJ2g>Xe+X9c>Ih zSXo+ITwQ{b!k@2KVeGdLO=UcJGyGDhgYWe@%OrCjeti9%oq0iBbnTzmmV*W?3>R7# zF|e-S42hWYJ@kk4o4E`H%zTXP%g$|%|5ZBo)yfLyPrdV{HY}L1q2q=8zc%aNZG4dA z=Ewlf5DT{$rXGCAY3Y1Ec85>ck6q?A|J-;Eh{`bWF|z-X6um0U@W$C)sBenuX%o%U z?w%{sO>`3GZZUpa74}*tIqu!PTfguAdvwF%edX)rj9`avx9(wDZlSu!M|;nTQ-4%C zZ6%+qo@nxOdrEHh{nPIAD=H?du8(%um>N=-;jQ(VFTz(ez+pa{z>TCoMvRV%iGmju z?BMC*p;yhfykD2)Cv}{jp%(P6^wyc4OMBn^-FxiWjJeTYK2877IbSV*lim7Y0e!92 zF-()4yt=r;vR5~5k#Ptwy&!$M<nP%tF%I7&<c%E*nH4r1T{|_oUA9zA{Qi%H3{6gl zlooXs3QY>SWOQx2HRD@jei=ySD1>K@8PR<wo;<M83|zJHU}^8;gDN7DKf_uxI`yoo zl4maQU2ptC=C9o9iPxXpn6&j?xb)FJi<sxHY{U0H%RU2+{AW*pS?kZ8KHWiptJTSI zg0OwQH<S1M=JSy|7r9)y_~1aYeDp4r^qIv8_YUvfyDjhj@1@tZWR4fgELUYsem+-$ zDf^Zxhu{v$b;du<*W|WL)1BEaELglI@=E6Vj~)vb8C=SpnVeS>);2R=`qkkZjkjMp zFoZDfwRmQz{59SnemQ$ocJ`K=j0-p!{zfjJ9)9cC`HjksOjqio6+Etm?YCdL?Ra{F zGDE^vYyO5AyfuuM=O@=4RbbZ>lXC8Ru(v4a=CM^_Qx~0ovHRD0iIx@xK@St5{vNkY zk5+%pTrFA<m8|^miFJj--iaG51MR(6J^BCp!@M1*r~Kdk@6TWF?@LsUww#r?_UXLl zvyYnsD~|mtf1I&6>TbBTQl<9tCwFUZz5DdfzIgdb(S%c>mg0{dr7u*-P2;Zn?=;nJ z<)`V_7lr<l{bQuV{zE{-OI%m{{)P17U70z5)=041G#qo(`n^y6*@^o?Gfb-PNUp9> z{;4*1<7OY8zkXdC1iC`+#__maytqeYj$Ee2r=E(94F_IW{0KGiS;yxh{lrG`X<^RW z*;jZEE-{hq%}9?=j9SxMc-1K8=ojb2T9Lh1qQB30w^-;!1YgosMd7=x6aO%odx$U^ zTQ{bZUX{?uU6}7N<&4LKo{R$_%kos}58n^JVy(O6Rr}J6tJA-|-qtuRR;?iB$@N0x zwD?1*-rX)q`xOM;!VL~_sZ<AT_1XRC+{|A-i9Y^3GLxreaIHKOc68xH@f%Aw{XO{l zua?*AfD26<4eyEiFq8zInXI`|>%{&aix+yN%}i=#QL~=3UvCB@|B_A3HklQyhj?oD zOSRNnBs}O2IlU(3`5DuQ63Op^U%V>1xMsvgZ<xHP?a0Ze^J1sGIV8xFImy8LTHU{E zQ`apq)ZFFjzTv)=`-`=lA-}E!iOe~cR;kZq7VpqrCzfW-^6CW(-;a8A`_KH!`==ar zTQqHwSxSN5ff*;{Hu_s#)K}Txm&~{9%w>+pAuDDqi;MlD(&`qX9J;7uJ?{dqOP-;s zJvU$Ssb<}LqP#%SVS20J(bNo22dl4r^R%3e^F(|3mrQ*zA<|Gp=k=oG>DM{Gbgq?a zU1i#{__46;fi-~#o-KIjqa${jLGE75)J0Qg9ohNtQB42EsW-#Vot>g?6xKFFqgO-t zn!$3BotZ9gH+IDze<Ua8m>^}F`~2`>w&19ju`*{R-FdZr4E|m=_jRlg-5~zr6YuP{ zyyK<m+rG=Fs-#bDP37JzJ=4DA{3?@etiPv)b8CyXpAug!c*SJPrjh^^4?|aJfryIL zvo1RH8tJ`$BeJe9R`c+u#PhWY+M!FMZPq($z7Y*s*nawCM0VMpbwQJ_Z|V!3DW$G! zoVzmm!pV<PYvb9yleR7F`g&2~R?EhXu1)DPHoPrqJz4go>+PwjJGVD+-uCDBd*dLt z!D;QL(|(&ypL<?VrdV92SzM<2d0zG&V{?ri7iUR|9-E|gZI8+S_pX<VEsylpL^a$< zzWdkT;YZ4?U!UKpv^8yG^0w0Z-}+*g$kM<X&zE1y=W*s9caLn%x)8|sf6<o<8#qpJ z%V~3d@Y>7X`dDt}<+4XJX0Cs*;^5rsQ~&;LFK||HVGONRDR&ojV63=)$zacgBqphb zz56!b6=<9q;GxqXk>j*R?%$$gf?pmlc|B`WR?G3-ehf3JID%@eJXUkdUJl@yt+7_| zrba|*&%4H5EDT9TUKeb{4E{~=uI97+tFpe4>GZ#K4Ra>gizIOD@ngO*@j!7Y(}IaF zJv|wi!j|<M`e3@Q$0yN$x7?e%`pHoXSNZ(^SMcq%5QjsdMUk6R#MZ^EQ4SpyoCh_h z$OoyjH6+I81n*9XmRiZYU(~4V#<T;g-I6b8FFh+;EM>*~^U+(YPiw+`XO}M7x?N?$ zg;f`#->=l$owK+sE%{iubaOoGBcWvH?H4pN9y3c`?^w9=ouK;+v0a*{c-dNgJsztD z|FYn^>##p3sW7pU{bPdk3af_d#my5W*35`m@NxR9S<~9PE^X<`)YV|W(_YJ65Z8F| z&)L_nWll)W;Z#^3vCr+e<7Sb`2Y%a{xTKqIUAt$|>_TtPSJAxhDg}4E@eA4&!8S+S z-s|A1-+7MlwOe~-&qecQ-hK0SNoddMmKKiN55n1t3X(Xw70txGv$yGn@1As~)aAL! z>2||^VisNeN?nSRQqp_(ev{Sm`+s6$Rq@7J#S{4_BY!$iGIn4%UK(De^Fw#>cl&Ku z_nWZC&RDd2wfahdlHw~D&7a>Dn$Gdz!?i_M<9&a*DKAVao^V3fDaF8l`C;FXBfgS# z4;FTG$|-GD@Y!&o!KvBo&GwDQu1MA|Jp8c4X5KTa--@+c4t<&W;8)Gejwy~RvDO`~ zKV$vBP5pD+`bUq()g117H*&YexvktCyXmWTu!qvYGlz`U8eX@NmHadzRQAZR)i2F5 z{bN|p^%`EN2+2@!Uz)v?U;Wi4JFnGSJkzY^WZ0>lu<owEZ+kLcfrF`oiAy>zk+Yfk z>glR>myHW0Lt+wcUGm&LX{&Mi=6|*yqqQC8R$ukW`Ya%C#8`7CIx|O#<ALSFIg=Lr z-}>HZ(qg-AbA`B-9uF(Z#Lkt-Ufol1a_Uzmb*<-~5+_gG{={7R)a3rBcOGZkO7gmk zPbckV{&8%(^lI69Yq^;#zA^sYVsGTv`Jr)HR$cL0g&Q)goxk7SnH6WhWJ>wRWgl*E z-__!IY1kop{FqlxcgMt2%h+Z&D$Sm4WHj&icg4s^Zr_!~zs1f!^X#$EI^gs{{l4qV z-wL^*K9Yt1{U7~*KP6+$;YZ&iFKMOepI<v)XpgK}#PrmJq-CNZivrJ`U3=qY!7`)g zAL=<XEI79%&k?<npT+ic$ycr)_6`L~0;?pZMosxvuDxIXioj~QQ|DW|tkpJh{++Y= z%e5roX@75AXzAq9Jic7QcmluUmW$8r6+{m53BOItO3wZ(o7b1=wqS*1^z1cGrM-@m zr(T|PAa27It}9hDuY|eRtjPRxAna)M1JQ5Jt64iQMT9>%GjWTBWa|2!rHAY6kBNHR zmHMu)thgoj@lnwNj|6r{4k4r6JX2yOWL?=Dz2(^EJuBA<1<&34<I)a2iA4Xly?sxk zcC)+x{Tt=^jCFDUrd*i=+P3fgw?{4A7^dod=YdOdR7uf-Nha(wOKx~Z@3<J3!4Suo zILFd>=fT|?mpOU=E!o<XbnI2norh0k49#{v_4;t^MAv1X+mFwe_49u^BEI09?xvZQ zzvM0I9kdMIUoM_<rS<0F>f-HZ=FB*zJd;7maMq4xn-|Nb@AkSie_MgqZ+q>UxY$KA zSlf9Rc>|ZeWDK<OYt^wjesZ_0-y_#8qO<QD+fk%xHhF<;&iMyC+UJfL`=+@(Fpr8Y zOE5cqeEAs_<B*#T>UL%u-1V+#{VR~vH`00bT!m?|{pVLLNlTNfoi<7BZY`gi`6+Nw z)78qdAE_UL<{kOSp1ppz+pT1?k{Jo2m(0IO+DRPw^6l8qm!(${#4rA9<~(BMxW&wk z`Db3ht;?bvGQ4{Z%}koUq4l<uLWa`SyW14}8jjzbx{SwGo-J#?==+O~@_*Mv8<*cQ ziZ5OwCAPcv@rQLAm3M`9ojE(*JSsEhe){BH3l7Eqo%U$;Zhz^gEKjSIXSB5iKCB3o zKGkaS{{CI@soka>#canT{?6I$d$c0>@{ea4!nwNY(}dlRFeVx;kS)HpI_WlJx^Qiv z4r8C~7u)-JE-dFaPjV7JK3nF3o5}Gb3rs3K4=Y-+np$k*Ikc_TD0Rb=sJNr2^dqL` ze8_53zW?xy)u*S^5`$EyYQ379JT1$?eVwPazM#2i598dKoLrj}%(j%>&waem>|J+U z@o~=j^?SC@S2$97u=n!oqtAWjOx*956{>s8cUAVxFMKD$>f^4~P3~H#Q`r2swt4@v z(`$e6p5C&&p{AIJ&+4;x%)w{JWS+;gCT8Yr%}bNnzb9kD$F%SnHM5fK*QmTGKQZlA z*~+(zC2!|#QRJF8Ym063%de#>JwIv|M%Zsi{d?>!>y-z*#tKT;Ex)`I-6zU<ch1N1 zjd#w;<w@PyrkJ(1C$HeY&XIGf(>OK0GFQ&3?EkskSzImI$k(j7C}i!tZ>#IR^KD4g zSJU!ZobLJiZ1YWf`x^$qGLPcln5`ERNd9JP8WH;@$5iC#38{=DH>DD`%P&-$!SAu+ zDF4;7nG%=Q2F8gld?ewsMMb*&ic%n-nWSFjBGIlbms7Vzu%^sUeLhib+T2Oi*_{g# zD*IiO4HiClI_b>fRqH3dUHGx3WzmYuKM$*zX)dkZKV|Ql*|{le81C!~T6{Rt?#Jpn zGmaIEd)dUFzYJ77YPA2gwtm+1y{@($`Jus$k^5@8qg8vwZg!h`YR&q6<!i=49&6>N z8)og<yxsrG%G@{`Hn)@VKR4d6VcvDKpv~VXNvxvbZhr3myVKnLm)`rFmAhRdAZzM% z=CgsvB-yNEH7`nb%(%|g?R9g0MysIqY~v7(nO}ISFJFijsac(q`MqGti!;+a4A+GT zWD6WI5?jACw^e%moogB8$){C3`%Ioac{p`$toPe%jZb_6Hk@>-l#|$6w8>?~f{PX= z^JCm~n^(*aH;gqYzx=pFZ0^22-|bGcGK)?KSIB*TWKZJ9h<9!*->s6nGK8BNR&vet z)|@l*#?1p?ZKX5!Nw@J^7=G_ixpTZ@;jw*-SMKon5)iRq#`2OfyWbP!g4LhbRM+j; z`zlgx^~_ruJ2Glcq_2q(|L&nx+FR`KhSRU2c3;U-hP}pGJ1_jbttqP-cWm+M#OiaK zDlB>r<rO)cFWSgyzw+1V>u=6p$dy!jae?dg)B|ePb7!<_KAmsP@w95rX5M$4vfd9q zzs)?hxqr#S?lnE0e*|<NT@-a)WFe*))Z?bg{jYPE$oAV4j>Y*+d-k`f@!Y}ZiL;e; z-m}{U-Mv?D_BZk1?PHJRqy+^lShp?9d=|R(*uR4eL9dUzRS;?MdR8&zQ`nX-7U8S~ zJGR^|eVW(bbgk{~Dbw9Mw{wM-E_KZ-F5>gr7`1xqoNb<t*WJFJyvF-8ykd4hZrLM~ zzVcVzJyTcCPrBKiud~zeo5ksMYM$O#lZBp~x*}J5E%n`pW>cmq0UtbPXRP~n=l2B3 zNJGoF>fc+vTF<|jv1{*DuRn_}EO+_QdBgRH_AO57drAkLizhv5pK0-7t%|9q+Ofiq z_tmAG4O9R6oaDU}`}sop&a@~6-D&T4dKPEhQ%~gkEh(Np@ub+ZWzxrmC!R2R5TbH$ z-{BaQn3m+tJ2WOmG#{T9J+tZEzLKpbW?w}&zYp87PpB;P|BPqa#&!9-e?7F0{XOfC ziRq?o2RclqDjAEm>vSeQ%RTU%twTP>XvWRdl_HuBd{UOpG8y{%`MQrqEKW2?Y*3q0 zb>4+1HYGK%x2>^Yazd-Jz@pPuUMkK$omWl<_RN@@vU1yGnU@mYEDUd7%&YsgpYhAW zYI6_El$r0o&pfy#Z7Q?(p;9gvC6nV}XKq&=+W&>mo_qhls>f$f-)>yae}3;-!{3$b z&;5$lt2sW=s%~fJV`iRb){?L17`$uxr?``Maj;{-4Bc+N=F2m-8CY~UF^8AcGi<0? zu<;9n^^SX1`j^iMtuDFEdL@T7>(NV%<lG{`eLrQ>maM;-^UZbE8MR*vU!16ByqDxy zwNBSZCBa<dVD`j2^W)C%obsJ{k?GTpWQpDPa~3=6xgPym`&C<PouQIogu*G!j~YiG zPpgi5SO0KnVaeyH{YDR0buS84osoIJbz32)_>I+Dc`vtC>9{f#I!sw?k$hhD$M*xS zt7~6u_nO`v8Pm5Rr@42x`lIY12i|uQ#}`|#aXI$pgz(XC(x;FAIJ%8nde5N;Ue|-G zt{>d8P2y;8t-|!9P48J>e2w`aloiRf+b&pk`^6A@J-w~vG4CY#!u9eG<QFgbEw(W1 z<=k#9x%~(D_x)PnHsyKe#C>f~mrULB<b&AKRcb8<S94BW=@d07*x4%O)vN%y^=2XK z_+DDf65<t`zDa;1OUy$?mQ!ejCI`!A2^|SNOR2^?$B&rGCp)I4<gJW!$e6zHO~;#? zI*jMpxsTS*_$(5&nQ7Isl)H(RpF9><$O>iJ-_KpV@pXo)R+~e6?AJw!g8TjF&)=S* zd13wWzSldiW$PxG|JU_)@B8(juxQ1fNoM-qf0Pz$ms)IE7+^BttV&q-XI@SFl}!(p z#w1o$H<tNsIjtGMqSq`pyN_q$*LMFaTRk=Qq-8t(w)6Mo6rH|x@2XhW9QJ@GF^0|Y z^Zw8HC-ZUJLdK7Jsh{N6`p5dr@BZ?^V)>n=v03j`eRO1npZBcdej!)+eC_dj(#LKv z6eWnSe51Yny4;x*|K=N)KF;R8uDy7x^b`RhDN&Z23%V}l1?blp%{$8`Yv9{#dGqmh zh0fcW3MnQQy#K;HSa()F;YwWAuprv>irL+5@80a1_Hx=nmXlHvx~8I?>(90<Y)<^e zQ9Ef`zGKC|Z726UKHPsc>wbZrwRd&W!KbViaqT(#*UWl<W6OKa^`A8kY`a_Z<D!>; z((^kON?&8FDxJdO?6hCcol+rZsl}*XTekL%Xx_G<59^g*dro&Tz4hS3hB*m|z7Lju zea);NnR>3JHTtO5pZ~wk`Wag!a=bhB<LBCQ3Jz;;7$oodaP1pI5o_M*DS6%{p^CM= zQC{yn)?AjpzRA^oszcMo|Dt(M_om2~-Mdm!SCzL#Y@)bpt9n?c!og2mJC_%nX1R5A z?@i$u^S7;AdBADzCuaYbZc3+1H_8f`EmDY@BQ@>HoBbKD7b%^b*0*VI;I2m(1vaZK z6<?>tWy8|GCAUo7;Jf$4iJV#gE3BNP99WlcN)}t}r6$6#Szld9i}Rem?u8RQTK@M^ z>~C$IP_CId_xdje)2r-J&o3|9x%TA8lTRwH3+Jt=Fx)O|Isg1z(=$4~yCubsEl}56 zI>AKFQ`CB9=(8DuTLY_>tp6*re^My3=>*Y@iiett>gOw{`MvW_ekvT<`9f*d%FSDS z*B|E<I_rM!K6k~9i8dl(p+{tOPd)YDvQ<VbEX42g?C3~#?N5{PmrAEj-y5NEDc*YS zx^HsPGvBt(>2-Z};YQ?-mKiekeAo4@wdAHBPRlefG>oj%;WMkAaWzeoM{JElFn7+n zwzr3Gs4bkS{Cn@6J=>&a=%pGP>K@&*^`<Yc@LCqzjR{+nW?szJ+u-uLWLics<6`rj zPWDj_IjSZ;CaIsVZ4)_vWXic?nitpCUUoXGYdUqRuIaOY`&+JimAtuiLqMV0Nx z2W8b29UPop&(HEJG%e{+xua=5YeRof*Z~=>3kAn#iTi%?_ENv`mAB_^)w1O?1B8MP z2%WiUJmE<D=7Zac&1=qjJaFN0bM+5ca7g*-jS4F-!MNc289kv7g<g34{AF}=?YBQ~ z1@9SuZ+F(+;rqqw_O)8&C6#|!G<IAlSRhdOjAzq@f(-@|CW4pt9Ng!eG0U)W_A=io z?$?9nHcx&WVD}+<$yclD*(N+OQp>(+e_N8O!+UC~tBFcihiZ>XA<yitX%%|X#dkBZ z^Tj=uTvTpN{8F(<MP~O~Zss@QQ(wd?6(9V!#ol%8x&HwX#g*UFBHCC4)MwomQuhiF zeC*oM^g|;eH6*YqO*DPx{+O6It*2LQ5EAsaRyxSSXsX10bJC88s$KiP$}HaXH16Cp z*;UVFXBY2(vvH4iq*7k7ZmZ_TD;WxZB)q1bXIcJs?vg3@e=jzC5|Q|0=bg@dqPxF} zFFrl(R@%I|2LGird+Z~wnNIjoDWUZ9(Q%uDpMT2B#!F0Fp)xaLOF`dM-Pi1YuFR9r zIg&2&$hZIEb%R_n{`EiCPA|J}&E~zoPIi*v?VU0YbmXVc<ITQw?8_<5g3vuCv(J6t zNr(u2vo$wWU0BM|^!A+=t23+}Eg~%9f0nlv#qymg{hHN%T0FJX&ho(0<(2y`ho^`z z_WX8dT&?Dxw!0@!i)Eh8%cw1>Ut;5;R_)oh?&js!%JvI*CQsZxH~97;tDIG-vMaOX z(pD;^^fAe|?f=~HXp+tIV;k;0ykVIBH?6)#^>==mnT++S@`C#beikuXE2M8f_<Z*G zhThn%77sVfY}|Y&EAHFZQ`ZkZ+gaDJx#y2rl6noFUfII>zh7JW<^*n2*1zTN^pA=E z&hzK97u_ywe)ox&@91%Vv7J46mv+yw;M9}3pB${<FL?dMhkLd15*C^v&yQ)`Ik`4- zvHYr>g!vg6($8)DPP6R@iCz`UprfZcO=$YD#|{kK2AO$rxo13sugmSzw7I|XdZk2- z+_NyBj#Cb9#ScYK-ky70QFjlE&57cd)AunQ+N{3mP-to2p=bZi_jA^LEM<^+>gl(- zobAS|n!LmJ&(tqru<qWhSF`a9>#<#R%;(A)=eU@jd#qQypP^vdbukW2j#*B%Ke;=d z|5b6`6&Lhri;82O^R(%=(Yk2m61!!W-`00mx9)#kG2!F)0BN>8CaKHho^-8Hcy?03 zXo=LBMds?SfAuEXez=kOeA3z3jjEfMH`~Q*JvWbSo8@#lYg6fd-e*4!hw9$G!FE{U z_LWHmH*)GkeY%e`>^%3VzW>BEdHcDHI>o2+40dO|NZwiXZQZ=K>^Jv{ub<=Y+886I zrmiQo_}QiIi%p+xBd_OY{83|RZuMRBVA8(&CRaQ8JR5b%Ig3B<dwL-(>qh)>_OQ7A zT>U=zfRCJKgZ6qVec?)ZeNf(@kI!+s?5#iAY1=zb71*%3Nd{hu-<tLO*ovu#qx+g2 zIMn<aJNkXvzBo$$o8qy|>O{|K^%=@nUryxhk~#Km*)irlS!L2o%#Iw86Eg3$|L{T5 zgU9d(cQ3DNW#Gh^(hZWK&(r$?PAlKuI_XE~)nj+xd|GDNIib3({PL&o`-;y!SNpFw zclX=~Z_iNnZA+h6JU3ClAG`Zuex!f@S)SY(?X!yKm}=%Ieg5=zvCM&OcMiU}$(I(U z<MQP0w>1u_4Vt2F%`cX3$eq?-bl~M<_8HZsdQrae0Snd_ioYqdUTJ9S=4}vOea9?w zQ_PV{hrHw;`?C(Zf8mw5@#*5@C-Eo6Q#Ns*l#~DeYeoInFOLM(Z(ORIw>x8*-G@A_ zW6y5gtGBaomUS0qP73>!ac!dfJ^NG6lboIBI!~0>j!&NT{?E4q4MKklr8F<;S8PZX z`m5G$pLuao_{7qC`OjL|F39gbx%R{B?NZtr48L0xd0SZDvst_rn8SKe<JsJOIUGw^ ztrAw}iT>d9IH$sxx|;vw|K{}z3}w~!*1f;qP;)aTc=Del-A$Q%MO%vQdc<(7tz|Je z#PZCKRbrn0-wWKz)is-z=4c-c;cKq6WLm|dT;9Dve6jG&BQ<3uIg#rhrJh;$bK}pK zmYN%dUbtx-QqM|Gcy#22`O!%kzK$<8_0-2duK)I-tVjITH)kWJ9U8kd4tc$2*l~J3 z>&7jPfpuBt-q(fd_FR-G-?S)yv5C3In}2q@%N4!rZXJK@wrNMlw?~h<Wd6VKsC1~2 z%GwyixSC(M+u+)_4O1ElIZr6d9Q(B7f@w-rTmD`Z$-N!(*75apK0J8GHLgQlrZQek zmp}fE(7dfvgYO@-z8c{-J2#@pAt?FI$@GnD%1nMtbwB4`HpRr@spPy{hp%mIo1e3$ z!#eYg^wej|uDASmsbSmk=Y3sa;-T%`r;C{uDTy+ET$x%Sexx&LOK6YX({(o`!c!N$ z*J<78ck)h__`3Lwi_FYqy__wpc4}nr{IoA7igVu~)jb8XE??B$cVl7tIg>I!&ojcV zrl#wbhBwy+ZJx9tte<n<nV%w@v#d2H1y&cC>=m*6-jXZJ)5n#v^ZzF?*Nrmj&pv-w z&~oHaezJQ0snyS>Tq-T#(d=4lx<prfxw~{-LhbYwGOKf5$*kHFnmAF*u6yH(Kx^6T zRks8q)H*J-d^9piiqPtmefjkq>mx0`313sXlCrxmg-^24mh9MIKS?%Zo8>n1@X5>z zcDr4Dr?XAW<!~JPvq>}WiFXw$wfd|KIpK2SQMXS1>TRMA^y>R&zD&*znyq~KGt=~C z-|OC;^*Mif!Bn@MwwCYr-aqzibDPkF>|<;JC(a$@Q(2Y0LAX;&LR#kIz5M3nMXOyl z{R&`Bd$zMyJB_1V`M&kre(SwXd6l;N<~V(`{PZyV%b$|_>>s~wTe#@R`T3Xb{pU8X z+m>7V$z17E82j3tPi-qLAAL73%UpeS)`M`pLmva#+*p*dUvUe(6pe|K(v+5&bj@jE z_Q~2;_s;LN%(T?3n8nX<ivQi;OS9*%@#6{(l)krb;U3A@rTLK}f_jIWqjtYMpJcXM zn*UR@)eiOStoWJJ)>TR^{wT12Yj^pkZEQwsWj}OnT%IvIt2{zy>to;U*JZgkgSHpD zdn+B#%j`9u@pz)-amkY!UT?#;ec{}CUw!ecU)jb5PgfjTyr%QP?(Nq9_@x_`oDYA$ zaYa%NTgRex_fIzo$4c%i%|2gr;NAk!NBZ^s-?rS+IsD&~{aS1KI*qW-JGJQnAM^AI zT?z}e^fD)g`uKIqh3(wA{X~SE<DV%z0<QS+dGQ7QKK0k~V@pqkV?^z~B!v*^WyRqO zg*?lZuPR^PvuD>8oB1aWxSwsjccYNci}B=_Pyve)CoyfsB{tSu?M+S_oRhQo-0mHH z|JAk~OcyLY&ZTi2?Jl~txs!7a0~_1__wT<h5_t5WV*9lGu&)!=Z0od|t$HYJ;<M*s zJKI#WCu<!#<uK*SvJ*Xj*8l&cXL-Nw{PL^Mgr=;w?JfKm^6gudR_*!ZErE9GPaC{u z2OC%xTRt-|IJ@Dl_1pD^F<w*34NbICvoBOmC^_(8R#_%*o91^*7O$;8Wq&^JDLKhK z@mJpagGcQD_J5fXv-JHv2lFrYy_VmVzxi@c`OM>ommH70U><P%qV=w6=a2t!udDc< zTld=7?Z7!1=}$i`N~J&U{j=`)jr3I8yyL3n%J*OU1pZi7D)8jZ<!AZswV&?R-Q4%4 zraWa~<<o?iB^nF$6@x^?pDZZ~wN7p8==jQ2SzeG9(fQfUeZyYn_W6o^1y?Lnt+bjy zD5O<~hm@qVcXV<Wg;=Fpy;5@d!_(Jnd!ul2abZMS`35JqHR7!nsk>Iqy>itrT_&Az zUy|9kBMB2G&)TX|y*}x=udh;D^cfogS<{THL+wXDn_EpkGjV+YYv^zHoUfbvPFU#u zE#Fwb*CR~o2>0o>Wy-AqPo_TJIbY~t<6_%`>;A_&Je%oz!n|b5469G^44W<2GbZU5 zM=9&(_TG<=^Rd5krPyNWv1^sF3eTQC`>1iTQczh_v9?x*<Hz)GkDsp)6k~Tg#h~~v zIqk=C7tN1R0!IsXPJi?I*#rBxv(9owJPBy-t}Iu-*Yu8GqiU+;#<hD2+4l$CdNKd- zv($CxI=BCNkXE?HP)N&u``%w)6Z*>c<)7koE4atEPdh%~!M`6Fecv~3jjP{lC6iG8 zX8vpC#0h8CHJW}kT^-h&negz6&WQuFOt)@b9nOEy<YA&f>7S&ntHXJ-7Ua&fV`g4k zx;p%9k`WJo^1_(~_V!{jyy*}BKGaSyTy|)0)AcQ3&3oi0=_RW1-K&*)t-5B-7m=fP z>auS3?|bxjBFihk6J9U8HvE5cc~?f{mgL+jS;ozLoBwTIIpuFX|LVIoY|ps8e;5S4 zXs*##d-CHc`?HqLn<=R|lM)Iy-Cya!>+K-B$k$4Y_4xfZ=_7WAC%Mwr{15kBz!xpB zfP3nO=1mflp8N`VvsR;7Lve@C<ZI%yQg0l6*K<Z~O)#r8^X{IFHx5))dPr`3_WhOV zy8gKdLAqjWTYV=UdT=tcEI>lfd?#1gfuM8EjWcv}4Bq5g=+2zGK}Mi5U1h0z!7&!M zyBf<E+2zeP>$eRH&rW<cCHH}L#-<(?^X<~vc0oy3E2Q|R`lm1a{McgUjSCaDPk!*C zy8P3M_RVHnx96X-ayfh2+UM?+tM}$@<*fcxWSyxjGmSs?uG_1H-|ie;^;=rEK2103 z%l2pe+jU<3?l$H){OH}JO=d5nlA>-2Z1jx^QG3TH@sIV#g!zwZZ)`bV@q|s@HK9rT z9hbwx2u;DgH=LyBe+XCkBDj73K{@TNd7)b-3M)UIQ6Z((?8We?{hybVsMY+Y9ZHq| zuUaXrwGE0<xx!ns>-ACD`}6Pbj+^(*McuP-&%DR3vR3<Mu8VKZ^W-!6yL3t1`uqRW zs|#CeSI?}=*t6DgUCvoIk-Js<?7tYD+?s#z*HXKO%tc&xr`GYltQOhP9h0$_;l`>j zyf1hBoEoLKHEwCnf=@qtL$(^NnRl9Rb?|Db!$1DC8BFbp+TPqKUH6%({r9&tiT3Xc z_x|lWytn4Hv$x`lcTcvQ-LiD9Z}!a0lOLDG?6;~7WvL62TkBx^-B*RZ!npeBbTL!^ z(|cwmuCM*_L1X*nYpV}#*~IB!w~eD&Yq!m<H>u{6<}#P8jR?H+<Yt`Q)W$t8PajeZ zxMcn9=*Of-o<?<Bq@-l#u_aA#cBxZxX|sOkfAo3R`-Dr?JYU*k)fY|*`0~U;BK^Z* zlisZ%4Knf?4OgFpl(oogGEcaAMLn8Fa@(fwhd)QvpE~lXMt#3!#q@_A{U;AyDANgG z-?Mt@wzhp6)RyILjM>Rra651RgQt@Bo{8lwo#~aK9W-T2=(0m=4OxG7rR<N}$MNj> z`KGNue;$++oMGQwdp7C8GPxV}M>1mr_wPKs^Bz<C0a0f;9myHm3l<doKkfLCQO3P# zxxwGBPW}G2tUQ5l%FCx)<?VQI^JA!6^3lA#<?ndT`}j<rr2Ug=lcw%XpC_EFIx|l? zsxjKHF!GJ^eX>8{Qqa?)D6YxeSyDb{<1Q>H;wkBxe!u?Ly_Z#mQayjGR;;o0i1UBr zAIO((!Th*_S#<ie>gh}7Z!hv(cIM_`)3TDM8mFp@1p;PN)UwyR1nu0sD3c+suJE~o z`r1ifpG<c<?6Z3P`90q!a=efG!D0S;@mk*JTGyt&yC)`I6&GdaegB)N<#YFhxa%(u z{l9imZ^Dw0t@SIi1U1cd7kz7fA=%`@HQQy{FaNg*?siW6LP9S>l-j$5{+@mwtW$pU zPm|4_?~{);23@<la>Bp);c~4!E@uyR-nw7)_Z)k&{-l4~TReiI!k5baYM6FA*4+Et z_UkupsUE0bef{jRKT)z*4Cjcq$q9FUbG$j>aL=c=pD$mtv1i>~U${cucF&*9#pml7 zd(->kzunASUKe2hax+VT>Ff+`xs4GkHM?gAO$rXSzrAE>p~cy=S5BndWVe_%`Hn4P zl6~UA_d)S{9=0U6ZWA;W%&GY3o#s5LY0>1q)6?&LdUNl0{l0BHDjJ(57ON@0xFOy0 z=ZEUScI`ecv*(ky#~m~hoVjGyoc6U3)^01~7Wls9^YrtI6T&}OJ$jiO8a?CZPRGzG z88w$ICYyw@xc~g*DJil~!A0M%J74Qegp`Kr>ARBy|C}_sRR8vUqvMb8pr!}`=_S+H zR-e4|!@$y7OW~!i{g;nbOs5z;zjS6~%12gz{hZ63?_d47@Lu)4bGFH%i&rS-#Y^^V zo&S8=yFGh$?{1m;YO@Dl+4|K>mw(C3V5`~4z4gZiHM9R|Z2|I0-+F{+6=?1`pp;jV zlbV0+^pDujhCg5ani9d8Bz$Q3KDU`AVOgtUCN_$@avi(GJ@cVDvkRMNM^d+NgyPB% z^#)PjdLwIHR|kKcC!n8d;4bl@cwfQNS95gNIX=2Gcg}68t^3ai+X>COGriBjR$h2o zNxMU4y+`K0chTFclRqBL>-f%eh@bDq1v~kNQOw>~cgpp6^WBS;(|O6xH2<8ZXZO^v zUqWBl{?wN6pXJ`S^osbKL=$t4Z_=S#beH{F`=@Em*EJDV9_wX2dTsWe_?O(B;mZ*2 z8j-lT-euW-@3z3qn~WuPUcyI@Pd8{O`onm1`|8W#s_wO)&)Rywnqc*Ao@M^++qEw* z%1aAHtWkb;^VZkTQi-h{#z87^mK9UaC7r+aBlp!q|7@{?Qw5tIZ`v0<Z|g$-lC=1Z zKhrCVyua6_zjpX;=iTFd?eVJorC~1KN(bcRj#T8Ze~SIrxo+;4r<1g6Qrz#^?5w!; z>+PxSt97j>tgBlXzweXt$sa3y9(?$qZCqD1@9L>k{jV=)vI|^4E9n09ZT61Ho6UDj z1S+rOm`T>oKADpKR&@1;?-zd+tO|X%P9=!R_TOFZM<=Hqp0?=l&HVz&o6<ke{<Q7$ z>s=A*+1WSLUt5=b{Q8ys^CIKJPcOGxbX^PCcQ{E?-aK^r`&&txw;i|M)Q--7zW)K^ z+P~2?fvhvv81kj}q@FOzEx-AZ<Bv{Wqiko7;Jj%Em_0o|%#Ki;+4A)NL>6UMuY~0W zf3`XNX?FO}UvbF#|AA`W3A#cp+J&VmPJ(X23p{UFzk05)gEz6@u+p;QEJ^cMzpuHm zqkG5fj^ArM_j2uU)ZgvKz4iJG)u&c>L#73|#D#vFeOT$+)BDo)$`=zZCg=-DX1|~K z?J$3&RHbFTQLxFW$B&*G_xdNMWSsn~uxa_ChdYey|D0SA8`^$ny7k@{AKtq*R?lY^ z^|ksNw6baIzEfM~tqkBXo@}JQY{KSip`n`D+w1~auO`3Gzh`?)$LLu=LVQH+jd!_h z$yr;r+bz2h?DjEVjc4|hiDu`e-mcuY&eJjRd?vH=#C39$ni`f~zOek7!{rw%-v9Df z(&zicvS8lq$=?s<I_dYV=h>Bf*i2YoHvZJdl9+(x3mdqfYyDcqr&jhgHKrgt;!5bw z3%5`5vaa~k`u<1SEVGA9+3Q(D((ZUTUo~{&R(!mD{XeDiw=>QrO_|bo&9L^>{<>%8 zAMP*OyI5$lbNEYbx5%ZZdiy+W8O%R(9_U=R`S6s`tJ0nF>T#deRxD=?tI2mfv@rO& zA5*5n{I&ne9~_9*y|6}vU*d_PD^u&G75$0^1*=NTS+o|OX?J{XyuRja`c22gvXH2R z2kP0j+f5nV+2>hQzMJ5(O`Ju2PNCfsp^c%(IZpDHu1<O(5v?4<w|D<<&eZ)0tb#}N zo;x#lIK40uneE}QaT=%M5eB;@GDlZ&eb_Dk;{Uz=gs=QH-i=Xaj*WV67utEo-}Dfg z#%O2WoD=_N-OZbRK6-K@+m9HTn@gH4{FuP_VdbhUhWU%L?Fw^C-TwEjn-?D*JFUJ< zvEz+@K>-UVN4WEYLb=fTt9_=YuigvTD*9;lg-q=bgD>qeUVX|xI4<r#d^JqPc|~-e zN&ySI;JR%|`do%iYmPC>DODAJ+5Lk5;_2e|KTkdS$Z8+-<=Kn!hffP0S@bjBzuGt3 zdLOs0zuM2VxU<_Qu{PVsfTS<VA3E*4$=~L{^A~3?zCUzY_p0kb@rI}O{wB@uuWy%j z`|_gl0bkX%$R9KR{JHzLRrjRM|2H|c=e6u_pN`j%W?FGe`1Z_;`Wf5)dByB>eG=y) zf7ILWp<4C%#Czqxiz_Dl(edN{)B2_*f&avLky|Ij8Ll_<THnkm4(5nHwD@7;#QUB* z-an5#&YXDq1mjHRfbhKqsts}5XUOm_xUQ#mo$<>Vj<vTV-uek%KikqZeX?%LH|8n# z%0J&?-sGA(vG!BgkD@;t4>`o2xBFPQ(ePs6#47<!_EMbfyLR5X)l%0jX#Rh)-&^Yk z+lA6Q9NF)rtP|!6u3_77c#-M_+n_Bx&W#RNOO||RV_K_w>#ab2<->0~Z|~lpb4y&z z_`&H3d^xrM-xtoj{Qg=L`@f#)#%tQ&UOjyvt{|bPUn*&%<jsTvXCsD}Y2qH21&zfS zzt6qzI;TARNoCWlgF;Ld*A_W7uwIb!`?4eA<L;faKcw%mD9zEoaU%C+NkhrHTiceY ztkzmptskzpby-7+!KqDZx_6{g=6?$|Xq%sE&m;Ke{fzEuhqX^I^M5bzf8k#D=h>%6 zCjPmxQ(eW{gDUPUdM3qw-7NgZyO}#OH%879ULn3+HY3++U3;n+NAlKHS1&S1u6{CI zF#hnD;}%*Er#~?);po&V?hm&XPE--y=`9+(I&^CApTHo7|Gb%J9Qp$H>FLF5R{ivy z$v$a!`<y?2L+5;2ZfI}(*YFhY&KL(K%Y;qFdM7NIRIj8fsW4qKd#9x*s}#MDEuUqh z;PjlkTE)2!u0Jli_de|WwaOP=KDRbo{xTPtH&aK(h412}j`nLyQxrlxx9x39^tkz1 zsd(b{rwfb@D)p{%by_iZ<*lo+*#}=;jWu)nt2)(zTehOtw8bY;on?1VjdAeqzU<_4 zd%qhks+M^g;4HScYG0kezn<x${#Em>l&3xUY+HZfT4h$?(hn~F+oLu1ZvDk;^tbfk zS&wi1pFS}gng8T8?zq5kSGkB^a8k$7iL2(H$<SI*ch^Tp;YpZLx#p%@w|1voR+`12 z(cG79ru_Vvf6ST_kHRko&h*`t+IYO??vZVu<Ssb-&SOxC6xwm<M#QnJdlGAkjxFP{ zD2eRzF-%%-@!DGE^Hpcgsm2@l+V*LFy0LY}y{a`XX*=B1_==te>$=TgXN+GOD!>}Y z$va_EpY#8Ij%wDe-`+iv67qY%>|(^R#dTR@!_tdNcb-=LN_c+l2Tv@^OGV+s5mVd~ zcQ4p-F7}CbROlr;MfR7qy<eFO3pa4bH>+GIVKHHw)X4pSb;I1u$#W%?&tDZ?ul;FG z?W*H8+s-a`e)m8!O2N-FYTN!h%?pb2ZilGl?F(XTnsn{}qrydos%Q2ZJOy6H+FMs8 zeEauRspgZ4V@G)Fw{2JM=(ekE`ITW^JUNrWfalkvR9|;?jUSeW#f35?Gwu{5C*QHS z^lQR&jhQRi4m^Iy>Mqq{?m8)Fub$C{m3r(|^5>0D^4*=_Z*;S<pz=k?{FJQDIV#Q3 zIf>g%-F^uusP&nj5O>;sJ?w0!h~d_29@SiXj%c4bu=P$&eW_uu^Ze!eHq1QebN}Y- z<vGDC11FjrW#8U<Dyy>fVPc(9;4~>#kJ7!*Cmk%9@6gX1B<g$kji&SUlk-yFys}?@ z-rGoJWvJfH?GKu7O3ZSa8kqie@zjVZe3DO7f)(@U2MDlvIoy=&E$8S?m}1d#@?DnQ zgSjjwe|O3$YM%GG9UFUW;TN4MiJHO!wcfL9m&Kj_6eU%6;BwDnkCP@T%m;4C_ZWDx z-HznF5~7?R-oRz9Y_;L%zwpABm*1v|9h(2~QuOwNf;X%GPu#KC?Afk9i(uiv)k{6Q z6Zc>Bn|3llgY&(k!H*svhgtlOCoSlhq#*tApI0D5?6%dGTPz}S`cGO-wd70bVR*~h z{2-@$^QkjeN|v6=GI@|Z%OE58K(W58US`wNU%Q{$ewSINUTkF?JZ;z8((AMP_+Bk^ zRu|yjKF?$Mvu`&f1G0qmxPubHTMqeZKe#O_Gs{CVbhAR!JhpiC>sKe`hALWw-C|v{ z^U&(E7iD-7vTxTP%8aZy?Bry*OLGN(B<CScw)=@s8>cX4^)2;!W~|sc=UKnwj9pfK z69r7YR@wObyqvzk^>UoGbAtFKiv_QjS_%}puif!~;hDQ4nhQiP{ozjI_Gnm{5Fq!6 zanbE5*S;OK?OQ!*!Yo#c>D#XDRh{N&xhh#g(x}1Bbo+y@53k=}+qt&;%(k=>53)|Q z$nNNx)nh+dX-;m&R}rB~JBFwWMhpc?Cz<*k8rmw8Qe&()PZ#jj%BkB|+t?f5$MNsy z>mN_Qq#C}yb1!cGv}IjPv)>BaU-X}`Bk|87d5c%y{9W9{KQ((DYjjHf<l-J_e$!bc z?{URNSL5p`693oESewdu?sU#tUFln_8@%_W?M?}kW0RDTFQ}O*Z+_${@8hKl<W6L& zz2;DByfH=azrENm2Ir*<+ppS{nK$$-65_F)?#Pk2w|)M`JKXXI6?)t4bq>k0Nzd8! zL5ThE0+yEN)lVlloRKX#dhX@aNETxosYRJ46HhSP%a^ZU=ZFy8=clO{uxrEFuNBe7 zjDp9V)RZ0^SQT)CT`{s=@YlTGjNY48Pd9Vk*nc<1OZdPyjpr*)SbezZc#%msrER77 z)mh6g=g!=mvG4SsO{<)n74$WmmV_;R^6kbB`8T;`I`;yOXL@d08up`luTB2xrEF$Q zyiD#<s(qpdv@hk}W2^Ud^LEboX0>FKSjWcb7UurW={0k=9SURcKFTpAo8ijZNgbZt z^ZTwePiF2iO1-{MWtV7P-E_In<yNkK$Ik8eXw+MK@QhtZMV9a1E4`NI9yY02mhRSB zZL;apr_h5EB?r_RRZ})?_EyXnpV3ofSR{XA&8=U&FSs5xbRSenkw5ZO;jE(Siy4nE zsvXmKBf4ZuDZ|4fG3DECE-0=w?9){^agaNvy(z_Vx44SNnaSD=8(UR&y%Ok1Vt(ak zY<f-QYE;q&J~dN)#+~dLX31YKO<Ubnc_^w^Ql)dd$%F!FOJmRH!rLdU{?U0yw@>+E zV|VS9M}-f({n=S^-EK~M?BcMU>1dnHOsO@iq$E~6{h+ZvQQdu~i`c5_l$yIS=`ovl zHm_veaO%Ny=9OY6qi<$Tl4+I{5aRAm{PRn_>3mJTmf@ma_2OCLg>Eg&GnMWr**4Bm zddD;U%fny>rHgKB7cG77q+oIU)WMTa_HujYHbjW7ODcJ6b!?8?M@=UU-S~U9iN-6l zPw3isW&d8Zr&c6~!A_N1f7Rbs-pQ6K7mE4aWGbcZ%K2|PvWR78yT)2ECk=O&Lr?eV z#iu3+T>6|gl_B^jhvy#ulJBwexboIT8?uHMcmF=);duFHk>EqE&4Q(`)n<mzvH2gR zz;^7*Bg4~&{#jHD%wrR}`~05h?Y_Cwif2ZI9^zKN?73P(sq(Um(w&Q5%Otp51Q_+- z$5|Dat~Rgf->k4ZJ1^z#&xk`=ZHIK#@|Px-$`&cAFTWS{T{5#T;INrYNOGUf#CZ?J z*=A+P?*H=Pyq`rwho<J#3(qQkCCw=OV5GH8=~ko60@gDjj*PpkuXP;w?dN#kXpYAE zo||18^*(Jn^5PrQynq(Y`4U!#*O-YcpZUANza(Mpj*WX>zdII_c1-F86XP^K%|)-* zp5a_0y29n?llztXD;LdPKiPt7#=}IO&j)R1g!#|vJH;jzeB<bnu!kzov_c;z1;6LH z;y%kZ;n}Ph@dCdcY$<G->|O%Bik}-Q9~~-E@BYmn@~$lJ)|u3{JJDL#i)PPQP|7lM z!6~0ROLD`vCALTF?GvzwYm>CmRG8+_wdhddxf>=2ZBma;mpD)$`e$MBr;Xc992dv4 zEO)yYrntLsn}~nEK)|jj#yk6VxlU309rl8+Xpi${CM6}#)E_e9!Rt#BUtZ$o_{hbu z?hC_%gLl4dyE5UM&6-^Wvu24{@wX>Aa$Vp(Zq1Rz#4EJ@{~15qQ%9nE76qSpc$7mp zg12?HguI#5g57q<n6qo!5?04P`?hMg)+P-BnVhYXWvojL&q_tth0J?>``G)X2AAjj zk9JGdeZMaB`n!Jica;rQ#)s@mUtfBy;h+4dc>j`V-`2${O?-FCXYEs`9g#t&n|>G` zTcMzq72a@amqg^&=S{N{wFI-bZ~DBub5m~iv?*2|is}!1?e8s_vGgUoLtReKxnp(n z_^!pLS&6UOC#qD*R-#jyd(2P$m?y89$=^_&hSsdECwELfJF91Fe7Jkonlo9NH}g+i zS@-?4v%XBvZZVvewtNzjqE%i~U0CzFRH$;@**6TH3F@C6G#Jukd*5!IDCtxtzTlq1 z<n#pd;wL;E>?I1v`r|tc6n4#FeIQg3Dcrc!t}AR?3`=uQ3Ez@a-$ebs{kZ71u9Q`< zJe+%0^-YFfQ*%#DlIm^dC>JzXnwWd()Z~IG3lA5y>P%R(x#`LZlLcAq3j?lju}Dfb zbZ+4gcoAp#D1UEG+`g*5)~{Trug%U{wfqA6>6pt7i=R6#ye8Iu<8NHf+-bt5*IzgJ zJ)5kz&uHuXmq%(_Z8S_yZ0Oz<D0VN%^y9HrU;b)HC7io+S1`e{cO~07H9Z4Q$+8bO ze|%;Tkb2nHpTgK|_lx7;1jeMb!JSoqj>Iju7tnC2s$KR(cHNH?jXi36Tux2rZMt87 zchCKO@n@>$PT)Mh=*W&s=f8N|+-$pR@wNTCZe{!~J#+G}!KH-!&&TRw&&j(-*=k8O zPI6uzut2+z_rgIfKgpLS-z8tEw>>_=wL!$)Z=u8#Q=eYDvsbsAeALX_utRYAp}wP8 z54ESQp7BZ}<ELu)8S7Q&r*&I4WzR2`&JTOc*T1oJ_wkp<jF&!7TFiYsDVyU%td*2g z4cEi(&aHEJyY#>Nbn_fC^E@&6&IO;Hi%U6}oEEf4uung};P=s+^{SQ=oyz`j)JF%X z_cZY?diG`OtpeSI30&IiCUdk*C@ehU82%$W%`HLu?-JV<xAO;i_4-f#J=-dj!ke(* zU@+e*<q07_S$XrPKA69Dp6s*@w>lgAZ%r+ZD48B}I9xZBMSt@ADQ22F78as;%R{b* z+;083L(gEEsn=RDmcx<V-D+*DiU*?)UaW4n__*}{8zbo>7xk_t1x}b#W+f$|5v}*3 zEBpyl&4h|s1y0`I=RIeAu&lh||1!N#8UpjqUU|0ZdcMU4mfM>)9G-OHvDEF^y>IVq zoL;hCLMVXqkyL_$qX+LLIj7R2O^R6st<R4r7wc+I^?u`PTCdf~vR^R0QO5S!KU0HS zN0ucW^GY{)5WeHQ<t4xCOPqe*wU`mMC{E~OO)tA`gW`tW^N%Xz7GEx5jac?wu`Frp z#p@GiXK9AEC#W8NY}q9>QBO`<_2{JUA)Av~)f?~Es_^gyDaKwm{3fuv`FaQ2-2!8e zyT(#!Q8R6MVx@JJ?(JUVe1msaeog3lnZPSNVxf-|Zv7N;VlD~OoZ2>RvbyE=-7G== z{T{shT@7j7o2C4lW^Aec>9Wf@=$Gfww&pqm{-zC!+Y21{D<53pp10I)&GWl*j)!tK z<mgLm3+}nIGP-ZYV;;7peSLR?cAc3&;no4}DLz8n-^_k=O;y+2`7KPh!q4&Wp(*YU zf}@lr!#^1}GkHyWwrobAj?!aeg9pF3&YM@UPq=;0XS3wg`(=BQ%bxI`aCXprp!AW$ zFjM~a#cGd`j8AvZ9Dcj@<R_>0S8T4qEL@Dy&WE<IUew&u@@rDz_g^;(H7Ck=&bb!T zA+qUzjY;S{uY69vyLa{99^PbSp>uMxWZT~(UuT3KoF(^5YLR%G*w3{t-N&|VPFA=4 zCcHVVGU3aFMb}F7Yh=%MuClo}m3igMr41J=Jf2#ASB%~$WxkL3OV^7JUlsgsRs2zX zqq~;hHdfR%V=BY1%V&$0F>IIk`CvtOaG#Bhz&S6uxHYoPuNRy6?^5yjdtjH?#Fu>w ze)u?Oy?<VJ>0a9!4~{MFYhUbozEDWixm^9%w|nX9oH(Y%Kfl;FX%7SYhu2-VKb?E5 zbWHNe`Z)q`df)y#n!as?VfjOC<Hg(NT6z0?vYquU^Uv`^3H1e14Av7%Pae@K-@<=w z&vNfgPp*E_D^-oNU%g|l<AF&IOmB|8`oMC)`!I`4@#nu!J&U^U?eP{p!M4^$;Vain z`y*3+tVk*{zO5m8_P^#)udfE25e)j)w+*ykE;iY->;9tWb?c+8=2gvJ_4aa;s^7+= z4?pm)IJHRKbCc{Mp^HZ<=kqMcT{ktQB-gOG*LKdzN68)&S$@CriMS)`XR>69=&QCQ z-mmi_489ld+pwH}Et}c3&58x0*C$_8xHMO`=HIsBqZ?kH>}mZi|LIEgdsDva)2%nY zoLTfo$we@ad4lofWg?w5EdM8^?kK<W!)y=Rd1c8vF>2M7o6p5gULL4XyT3BA;q(g8 z8S~?Ey<V0*dGTa1$M4Sb^Wp=pesO$JusTe!toOB&w9?k=YyG~y3VIsNZBUrC=fdZv zAA#@Lxt0ViwoEST?kaiZ`mZj%eQW8@b;nPqoaw7Me0Zs2u&(w+o`~5m8v6DqaYyi( z{7##?VcI|T%H#j{-}$-Y;*?V#-P&EcXX^ebGWfN2*;dwD)!XNsf2h;#-Z!bkB0q9k zsKLbesqZehhp2vFz9izjyi6sa>X8iN4Anc~tRBIis^?~Yw%Y&Hs@>?p<YV{UZ|>c9 zRw#Pi&QCx6{@-3^tLCQ7yM5!NhhD7G@$M%stXO9Heb2G`R-X@x9xR)Ya=_~4B;Pb` zm6iSnShrQ2NN1|ITR;78-p3Uy-*rB+o^Vc>bBDWS;z?bPjiMJLf?nACbnsZ1>+$Jb z%!GhXHGD7o<~pBfxGS?&d(G7c(jh!w7+w@cI)9kKy(y(JruJR=r80B>yQ%e&&$=HP zOv}3a_rf*Jr#$bSKSoR6u@#TI-OifvJ~dsRW7g5QYw`@f_Ey)M!)Gf?bGR2Q>yo+m zhwa8Jmj_C0&NcHs?wk1XUHtFP$FDuq#s5n*NOsLX+Ml?;tgbZEdhyp8v$iGesBQP= zKeMhkE%)62Y1^KESn+#>*j_;<)3mAVwGSA$8f@e%?)IkgCoDf(<{<R3UFh#e+t0sG zZ@qF$F#cHmGVcSmVUJ(GE7>lnwKwhADaHf;zB~NcoOnt#b;ss+2fntJ+ohyPgq`}q zW*(QunP2&DUEG5DA0M|ZxnVl<FzX5LTI*%=yFc$P&6~Z;OX=WD&F#O=&#E?k@pTsK z{#k7k?sT`E{68sl%a_9NX%>IG7#>}ju<ejcWyh26OBtuh$e$EsS?TEdY0m4Z>+FAd z?O>U5$0xBda$@S1<9s2yvDX4Nyy2+5zG8(;%WdXGO1q?LuTMCzPO-lxX`Q5>*~zP` zpWhcq7na%dKIzL8hh^s~dl-JbnV~oBl(&K3x88rD9S+6Yy4!Z^e*eF`C`$9EMA?+g zGwvRT0-`pxNbD;~uy`$8P_}&Sf!Grzhadd5+F;1`W9ywCdXYuSSu-Opc<gJ7+OjYq zJbi<j`kIi0`zQQnn`xbF`I+_5_v0g_u9S;?XL3`$GcC5y?3_76#c_eNMBoI`|Mh=5 zMgBbRU12kAMdkG^&0qgTzIph(bYt523BHF<J-k#Nb^P2U%f5##=Ca8>+iEX=3W+Tj zoEKU<$?s}etnXey;dKUE%?|2|N37DHD^q3j!{dSarss@0*^VoemUhmuoKvTFhw%wh z*<DHdvnx26|48qX^8a@;_7S5k6B}=OQO}m7G~KuL68&w>b-$lI`!zrJi^}4FqNmBf zEGBY`?fv!i==oEY-behG9V}kC^V8&8Q>+)C4qEoICR^){w(sH2<x9S-&(ql1aCg(K zPNklhfH^rGr#bujzN@lj_Nv$B=Rf=ZKW~5Rxo+OQ$DSVL^ndh!`JXy%@8}o*ntm3z z-1#)~*rUzAKIFGl%S7mkpT7F&h(YgVgLx4X__{r}^c*oOySPq&-`*I$<tNhK*RDOx zVR0dT)rE$+PdAAc8^zoTJIwOtRd#>WImhYCUwymvz-eEWxA{Ka?(+N0B|WG2Yi+vv ze&wXqi}XJ|IbJ_~t>nEGUqnicri2NFnfT1*mK4r2br3(!ko2K7zbEotzE|@6MJ#^X zww#q%dP=)LqT`IvNh5VZTh*JXa~50Os;)cTEYch^Z<XoYD9OCAgBxR+uYBNr7&rU% z$K#ylwjTSu=KYYepRtkY<muqQpFaIj_6Zkcy>ru2{)?gQr9A@c-%3yYlV~{S#dnXM zSxYx39p!rExY^b0ozc0s>tA=wZjAUg=bm}(od*Y454*m9@;X)gU6I}K6!!BQE6Vy* zHy5v&FgJPW8=2Lq33K1?R=sL>Yob(1Q*7@N{*d%5wTop0H*3}l#?7f){aZ`LcX43B zthUvcvsVO~gfyqNF)g;+e9rpTg~|$<i!Y_~In~7<zdy`pTF!k##NFoa3&~37go0;L z)~rfrvsGC%W4?6W4vd?0GB3@yB6!aOukNcx=bwqrnfu$UbI0O-9;xfGD~}#~yE-^{ zvdHQ@g|1%<t!-9@i?DM>y;;w>t~synn|>_g?)XE~CQQC2V;C@h8^65Wr1y>{A)nOK z|6NGSIK1m?aIl)3yGX~ckNK-_yDci2cT|Z(^6^HKmHFP9=Ib7RTv!@AJyOZym+juv zfLo@sl4II8PQ2k17pYp7+?lQ6pcGoJFRNTED5!Tw%Yom;HeOO(Y3sJ-M`T1-?L7A= z!++<Bx!+58=XO57_dB!y!S(oW>-pD*cVFC4TwPqc&o6oAXSuwuKIfNQ^Pai!VC&K= z`A^uyw`J{3y3=&lJ4<A7!sDu_?2s?oHzhUtwbC-3pWfX$iBJ8!L0?r=SFpg#$9);I zoGx2${3kl?lug%Br}xv(9$?y`l7DsC`iVOy^t=4kxEN+P&$;nC<3Eo1b9N{n)l8~d zswE)tRj%yr@0(V)c4V>6=hkKRY;Ha#GjCd*=fsNM*}qmB9(@|VFQIGBo%h*Z%#kX~ zTi!i;F_C%F@!yM=iq_l?ui87AZD#)Yho=Q!+*c4h^unXC{>aqZ+s?c^pLc6*?SkyY zC)peP((X>Wnw_Z_!#v6E07Jy8<h7xS+XCOzMhKPOVH3Q~|Nfqo3E$n{DF@{GEO%FD z&i^;X{@=pxV`2?D(_|7Ze?N2nQH`EX+LC4NQ&``#X#PI$xT^0>(8i~S-HL>gxut&a zX7Dzj4Zbe(MCkb9-Rqf{^ix!1eBbjOK3ef{568VS%k^8?F2&DX7`1ojI=fwORpxwj ze)pp5P5pg`P`?FhJRUmgyuMQU=;^}lBsGIa9Z!?vs=o^Fi~AG1@=Ia$4+ods&Z}oz z{tCXvYHBCyWbyspniXDW79J?y?r<vply;TP90m54@0Vtp1ZwL?{t*(5dg$R`Eq+m1 z?yqoo%wmIMEDJ?6eYn#u-IHzgj^>b7Jrwb#u<g#oi2~;Tq_nEk*GEr0$X4IwVzXo7 zwPgZVN>~1NGAs%B-6*6ORKC<=;>u0kQel^O>p5F>*s&d6&?w6u-6f%{wCA{kuIns! z37)O*)f*nSoluOkZcg~{D6Z_@i#xXSu8IBO-McM*b^7bWFB4yEQkfuo^S0cJTGK_- zgWj|A#0o2myJg1g%sIBaB<se<jI|NpMQ=5}Wto_MVA5J?Nw!#HrX`AXyve3}QeJR` z6qh~{ytjMR$Gf&SWM{|<q)Tv5s5mxT@1XjQrI*8!-a0o;=?;tF6_Lt+=iT8DZ0mLW zV(*KVqsr%dXIsaaFwKwpyxdr4>K>6jI@1`w%~oON%9hPxn}4!XgKzqRZ}D~w%f7RQ zGBR#g6!z+Ca-Vk3tznsho3YcAtP4N<_kJ-E_S_qL{NLfep8E+ef}De6<kGHuee^G^ z;`hy-{uM7onH2>d?JWD=#PIgA*q=wQGZ$?3V9nEAdxb~(@|#fBd-YSF-@3BTdW-%m z(S6>B_V0;{yHI~Jyxo31_q`ulwnB=|o_8L6tzx!Yy;9&`cFCLD+4=1E8*hpKid)V( zC7d(d?&DM0o$o@}r9PZk_|@!-u<6sq;#ZcK)qFdkI{*6b9d$D0k5~Mf&u>v_cfCaX zylKjTymPH~E}RFLB>MKUe-O~H@4MoeImiB6;s3h_tv51gU$}B>>g08rG3qaaVk@R7 zDSK~tS~T-`#?2@BdF^wWnQXbX>{}hZZO^6SGLJ=9JUn;Wgl%*GQi;irY)ia$8D7+8 zxT(sXRbkG^`&ozgg5lwfQ|>VrWv_X8N6Vw;__j=|n6M<BBdj`S8Kc;f4lOq8V_Yk} zye!)?+WFw>?h_kITzJg(rCqrDL+cn9=MT@~i&M=KqgK!V!mgY%F<bN4icd$lr}f<K z`z|N;G)33-%1rknz5Nm~^LqbQNWT=aKVbCMtXg}Ld)KL$fFs$D)FPDl+urC;R1-7T z7ZGBx{gMCU%l|)(JAVFmG@1WUi{Zzgt(#gl&v&VdGVfH`xorQ($ur%VcpI4h*G*zj ziRy6qaZvi%#rpcI4;>74tSDo=uz!b{Qq-l+``?Xz7j)d7df}CRR8(7Hj2NF#pjF^C z&Ly>ybKC5>pPaoHa8qe&llVlQn?mn(^U6=1e{nH0Z(Dym|2vVxcT{x>e(Kz}cvODQ zcICu%u?G*$JG)5xra9Y(=##f}o_uI#m6IwKN_fVYoj%cZPMXt!V@==vF6{B%z#H}O ziQDU2UQe^v3EwyBd92fasOZ+qC1nl_U(7Z}`2}gcoYAbZLRD{x_>TSuqVA8TT)xGj zC9RaMv~aQD8XN6Q+a<dMraNssb)w~sDI3S;1~seF%npXV*KXWiS8@H`w`W{urZJkw z+&Sd0Zn<mqrdJE5Cs@p@Q+vid^RC8yKY>JB=Swj<l5>~5-ImW{BB5lrV^e#=cLAoy z8{V|7WxUF^^j_7jH*0?O#uZQe_txD+YHv@+L*X?$^}GIgRHihiF$yXvOLttjqjP=R z9cz_C4?2^q49{F(YZS-`H=IA?!kz1`eSyWI9vRs!XIvw0>bP%@@t5S`wrQNT;!<aq zUTMdT+pWe=W`4_Tsmv3)-uvmu*_79xU)(MU+qA%Mg7Q**35Cx~PcHXSul+4mr@t(( z=EJ{#u`1hy?>w4&vpam9_MVxk(|=F9`}!0^-~svWo5t<BvwnFjVz^N$m)y+6BsRN$ z6K8-WLs)qbixzvyjwgO?7hCsER$Z<2?ukV7+Fj*b|3AL+S)6R!xH4p>l<1dFqAOaS z_-iu+tw>O8nt01uaY9kmdY>g;rc1m;FKR4oRtZ`<p)F8XWm;6#&XTvTQ6A5nmNJP> zdTQ2I6l&18J@<)5sFIb@M7NDGwFM@Ey<SVzW=dOrxzm@r=inM4*L0=Gi=sgvl9ea9 z+)O?4y4ILw)=P6Mw_lgF{5U>!xcIug(0b*h6|#4AXU2<bBAeRSJ?!5LFa7cG(7_c+ z3aJ^XA=g5pUE(dO+*C~;S5&llhpyz1-Dp11#QW>1YXMsn922wFUOR2~*Dd9uli((9 z(NFoaRV`fROZO`lEfD-$`=7yK;z5sH+u1rdzO=Zs*<<%D?&_oSW@hK@I9@CFVS>BO zvjY>O=Y23)>;9~J);}w=y-#J|2iLYl1)l0(RU2EKssG|+cVlO3Z*zCMzk`R1&O{Yk zkE*O3-|3Fi^i*PH_228<G%=lT9JSRlX>0l|-K)ua4QHR8=KL;e;~{ptOVY~LbHDU| z@454?{@>%v_cz<Wn{)r|B<I!h&#~@2XZ7AoYXaB#r?vkM?tV4v?x(%UGo$|%1UF8P zd!4i(d_}}WA!V<r+QA1`c&Q3+7GHIBRp4^Ry^@Q?GOsMTshuxll72{T``@>Hd%w*z zdOPK>+T3f2?Th;?W6J&*zpl^?*vA#()T*^<v3jq}%kZ^v*S6{IeS1bOWAlqRxn~xu zH)`;Q`1@SdjSl%T<JPfd0h<#pAKUx%<<EIy=i}?vrm1Y2a`gO;(x;oIa;JaQU}Mp# z*uT<%`SEPF&EL{2yWX1o-C>dTb6v}k^EHzVncv@Nb=$r}c&Fbi<AZUR^R(;xZR;Mq Ii<m720H00_{{R30 literal 0 HcmV?d00001 diff --git a/docs/fonts/lato-v14-latin-700italic.woff b/docs/fonts/lato-v14-latin-700italic.woff new file mode 100644 index 0000000000000000000000000000000000000000..f79d9a994ac631f14a502c7e9bb4fe78765afd86 GIT binary patch literal 29692 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 Vq#e15MT5;X$YnFP01J~g0|4GVs$>8F literal 0 HcmV?d00001 diff --git a/docs/fonts/lato-v14-latin-700italic.woff2 b/docs/fonts/lato-v14-latin-700italic.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..c351d88d811d42400cf8a9c5000d1877eea5e320 GIT binary patch literal 24240 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πIBlA_E7)^^llh&#_3AeDHcA>FlRm9{vuUF0uj6kdG&2-T z`kLJ~hQ%A}7gapTkP%y4*ZkO<E9RLzZ{10+fBTd+{jhw*y~K#?^|KD20^Yxxb}MF2 zyX-8&8g_8QtcT2(-lQlquJ3yOP3iXwj;2+c((cU{SF|deZ>Nwn|3J0M!<)=1JEyuj zR{c7)?mMr$<st8^O<acRKj+P0dA@t~UB2~t`se!jx4&MPy{cy7u|^Ar{d<DHB+Ng2 z(5mR~604au(YdcK25+xhwmGcu>&Eb`K>w?UJ{lPY%!oHQzx?N{LW_?zYmchMW$$3< zD>{F<gOl+MYcu1m{^!?h!sS@H`cE9#`>gzcNXVqV%XUs5FN$6}&?>I|;)&DuO98(E zk0z%{?|q@U?#VKZ=gm{Xu1d_U_OkEUWgsyz;HgJ;fb01eJMJ3%o@n+%KI_8HwNJe3 zUa9Fll9siVU$fm*?RH=G>h$xn`Zla=o7%-CR$lLVeBvPI^;)?J?4F;qIXLfJ{3`nR zvi~`znSu>0;*pM*H~LyJ`Sot&p859d`{EsZyISvXJutnN`N8Koch>q`TfYA1rR7f> z4%(gS)Mm(bKg{yJ`Ot)Q^A=biU@zEUpI^aSZ~kc7x4#kYo|z}FI`KZ@722YAQOdN+ za?6wKja!Q3gBt&sPU-o+S1|QrM&Xeg6H{UsqCUqw{?7UO`<%90-mfMfz94=fcRjCY z$7g=i*Svzx=d7y)|9$0gT2>zTtvG1&;h*z4Sq|{Oc=bHaMLs`k$B+Gnn+;-F9y~ri zZ)^Nl_5YEJXHA<wH!}Sx^XCl*7#PFW|I=r<cBi&+?ev&0%RlX|3FmY8zNXOL!7eqU zz&hG@!~6Y}hisHHPc1yEmmchy7xcT<?n<a{*CUlIzLUL^KU8j=zPdB#K%Ub@F89QZ zeFc3_c4)MB2CNEey6v3*^+WTqO4my&FXnEYxP~wA?48DmO!pr3tXkmZ>~+BM!{3}u zO}{OCw!L9qtDP;kY?a<EE}6*m<LCeGowK0olZoG=Wmko`U&X#(vG>&KIsc}5*?iix zXvw$5Sw@quR~y+jM&v9AsQRHS7?IjwXx4qw=t1x0y}QqRoXjx&$M)po^*WEZU$=G! z|FU?je{~Y`B+<`qSI=Lz|2*SLl5x-RC7(6kR$O0xJlWSn()T&XWXBCUQ-3*{Dn5R< zf5EIR4(5BLf_BB$o;k2wU1Pe&u{S@y_ayqJ_?~)u?AI0Nqa2qK&zunaYMJ7a+UN7* zT+)W6iK={-Uw_BVyKLJQdN%l*euc@N^bMEu4cBX(-k0+t)BJ-Thi^rjrGxqv(Smb# zRn~OrM1MWaA@r#2l+ZlUo2h}O2}xmBRes;mGH}#eq4V&-B?s{n5e_?8HbjU@F4B}< z`D?MXmbc*PIG+h32RT;1&)ISy;Ii<ehwC!dwi@jDu=Ds9*<*7Hm(@SvwRU)K@GH_( za{l`V#&WaVDnj!zD#aD9ON0ujgz%}Xxe@X8A?v)ns587jZq_&i+9Vjw*ugL_W?o@? zgQ?J&<}Ox+GKN3hJqGOcDLw{Al!Xs9iOzI!bx&c7+o)}%eDa6=a*LNsEjD_E&bp!z zw!%YJM@wBwXIh4#-!Yr#DThv7{?c4p#iKQ~aK(eBrGe|eWUsi|qdKGeX>nn_`Wm;L zeix_sev)2RV=CoPls3~#G>ZFrp~iImWE<XgtEnbJQjQP)_<f4vj9M?cRD`=L=%$b5 z*@&vpq=g$or?m;1_+E-x9g(`~NXH(|t6r|x6m26SMca*DYTb&B41CJ;nS*ha_B8ff z{-fKfj;uNw5p2?Tgn#QIuh$jJxOkYSCT*ReEY2P+7q>Ewmv7;&oEcNaJ2-Z{YwT3_ zy0XNt>A|Y-wy240CsLJaPh8UwarJjQEc-?O!q>K`qW^dVI3+$Z$`mIY`}FaX!Ec|F zCzp#dajChiFeu>g(8zdbu%Y0Cykq|)i)H6(N_Yh2-S`(u{A&F2O6+)gsDp=#kCT_1 zpQER%ud}!Nf5)#IQpJ3dr~YgFT5xX8WwWxr8SI;PeYHsIonEDDnsH@{uA@`;?_F!Y zToiw6{Ht#HpGVFwFaP^+wcOa}1?%%Sla6OfPWS#axBTK&{?+MqOAqvZuG|{E@P6s; zx67it_I#Y}>LsEby3VJyS1Yu>#V%sE&TEs5FSlyW&f1c3`PBwrt1Zz93%B3<c}>>% z+m6@i-`{LK@7O-)*~R%6(|14Sey}^Cf#2_kqJ*GulABJ4=*Ee1|MK#DzDk~qYP=Q| zs%8D@bJL2sUsvm^eT+X=@LXoylON|Dr>%dQTzcf$^R;W{zUG$T`MFTL!Y|_9{iF+g zaiu3S3ul$_CWu9QD#&t2{y%6PDVhAeF;uIvU+J&=rH=_dZoT}^UY(nlJAGPJP1&W` J{_>|Q3IQ$bVi*7b literal 0 HcmV?d00001 diff --git a/docs/fonts/lato-v14-latin-900.woff b/docs/fonts/lato-v14-latin-900.woff new file mode 100644 index 0000000000000000000000000000000000000000..6f251d9e794936790e7151b2a9ec46602ef03a08 GIT binary patch literal 27260 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 literal 0 HcmV?d00001 diff --git a/docs/fonts/lato-v14-latin-900.woff2 b/docs/fonts/lato-v14-latin-900.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..a5abe367ec6abd8b325d256772618fd0406236f6 GIT binary patch literal 22352 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 TnYX-HIMri!;j*&w*)B`~`W$M( literal 0 HcmV?d00001 diff --git a/docs/fonts/lato-v14-latin-900italic.woff b/docs/fonts/lato-v14-latin-900italic.woff new file mode 100644 index 0000000000000000000000000000000000000000..6922b8fec9812fcad266750f78533bc30c86a377 GIT binary patch literal 28688 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 Cgo5G# literal 0 HcmV?d00001 diff --git a/docs/fonts/lato-v14-latin-900italic.woff2 b/docs/fonts/lato-v14-latin-900italic.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..0d0f984e7c5d9d003f6ffa1307bd320731e21533 GIT binary patch literal 23524 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 literal 0 HcmV?d00001 diff --git a/docs/fonts/lato-v14-latin-italic.woff b/docs/fonts/lato-v14-latin-italic.woff new file mode 100644 index 0000000000000000000000000000000000000000..169e8cf544bd50855dcaa919a277a109f98d1704 GIT binary patch literal 29600 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 NK`xuQ1z4E082}wcPMiP$ literal 0 HcmV?d00001 diff --git a/docs/fonts/lato-v14-latin-italic.woff2 b/docs/fonts/lato-v14-latin-italic.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..2688dc6f7acf1416d43fe1f16592efb18eaf7b9b GIT binary patch literal 24192 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%0DSERj2eDEQ0 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 literal 0 HcmV?d00001 diff --git a/docs/fonts/lato-v14-latin-regular.woff b/docs/fonts/lato-v14-latin-regular.woff new file mode 100644 index 0000000000000000000000000000000000000000..97ab144d9fd0b9cb0dd0fdee1b192a0f4af9a3e9 GIT binary patch literal 28412 zcmXT-cXMN4WME)m$os<}0HR+VVPJ%afOyE*J-|Phfq^lHfq`Qe1A~y){>I~#?!lo> z3=CWv3=9mx3=9kfeU^RI{=xc23=CW~3=9m>3=9kjl`@Hy$+?LI3=CX17#JAZ7#JA# zoK?;LlU!D!z`($rz`($u&A`B*$M#)faauuoE&~I14+8^p0|Nuw)1s&%hxEka0tN<t z9tH*m2o_?HNYANEV_@KyU|@)7VqiG@Bw|uCdq!$v3Ijv>90mpkGZ6kW=aX_qMrtAh zL;4j41_pTs1_l-WZR}+kxg`}04C!AO7?^k&7?_L7ANzgJ$xlvXV8~)&U|>7|!qZt# zv*jjM6fiJkA7Nl%Pyk`Z8|**x5_3}-7_y%*Ffdv$Ffh%q`WWI^kY8NFz>q7$z`!_# zfq}`H_e|K&f}+#{28O&13=9n3AiRuys(8gMpT5mix{M;N{}(6NY~Q^t>GrnV+iA1e zXI9;^$QQVhdFay~7o(1Sx$9cl1v(aU<Oh0&3ZDx3e~Cx_1GA0IgU0K3gx){-aEPbt zHDiBr-5x)l&2Oaa4HnK@p`O%JwJ7#Q_AS>3K88mYlq`^#XZzAF=gjUU68mJ2esKS3 zzU23T$PD)zGyZ7)X_ruDyQ)#@HkWgM$l?}8?Q*S$-3eT!hZ+)Y*?%>y^)+1ce_6(> z>_wU7M;@%cVX<E6?rx>}O?iv%ojB9I=dA2T!7IYE_r6|xOUTAYz`?=A&DHt2to_fU z^8Y@~&q(S}ZFHIBp*Gdz(n*EMI-5=^PFL#ETs+4sYsKPuY*Lxa=JHv+T-2_)Y-Va7 z>$HfpxxU+O)TwaDO%MSkw0GCvf3moJP2svibbQwJ{S6ByZ0MNLGG$B8nmLOmZR%Pz zt8Lk|ZGG$JH7=aEaplaNtxKo&&Ye7acl+|`+xyqYFD+#fx><RE@elK?OI~ZN+h(Up zhfNI2(g@ph^Q{=O=vHse-S6I{`KqOQbGIytaM>+*Jjlkam5FhWLHmQ+wg**))dANQ z_E??cQEmzH;#@jK`c&=rug90=I5o5?*#Eo#`(F9k-}kEbC26!8ul`pyg-7(FzEh9a zQ}M+<7Ux?F>_V*9b?dc1(~ynIcsi^2dS>uG#mAqP?KvCE_Ej=zhG0;S(Xm<EUgvB$ zDR%qL<$U*dn~vMDFWLG24zt{+j-Fj}wu)tNo&Wn?eqm3M)*<bObFLnH(yAf9YLUMa z@761q4;<By({l|!{Pe}#0@YuM86kOn|Ky@FmiAh_z35UA{JZ6iLD}-c-7mu4b;fb- z{bKoUx@Rf#smHhc4Ni(&K4Wlt&dzH+yU(%B&e?dDZ}*#x=NU8kzMej@W@EpF*?CUO z{qIAbX=;ei)h;~7|K|Dox6ikOJeKw9e^@2CGW-g3c9x{z(IJk%R|?6`F{9Wl~g zvuW9TE9sm~N6oC?fPHgv!sca6`Zs0=tynm%N@|D1(?hE-E>Dc9Z1P+-Bdsm+Uim*} zyC=$gSC7uWclh~_M|S&!&jpLtimZ~oej@7CqHyQEISVv@EzAB?l(BH?>cHR~%g&eH zlDR*jurl~wSDj?u>9WWBCBL?8Uv$5<<<sqp5ih31-YMPZ&-Y=?^MpHhgnf*Tni!uk z%J+r2E=?C2+{W+w?xgnD@_7{B-^p(CM6mtYi52Ig9t&D>iD(ucTfg^p-IG=Oq)(+- zieAiK_pVw|P5bJ8iM4H?V+z)t+84Td^~9}LD!08#W%j)|-^o~a%ctV<gQ2<WUslgG zD>YAvxi@9|qLm-oYWJ?aBPSaBG{)n^_Qn0q?K`d3F6_>GD);_I+`FvX%h$zQuB>yY zyddMuW9-xwcud0l8OvlJ!?Qe_&qN<*>ektKhHLejjgTm^{rvt!?6(`w8%(~>tv=u^ zSD4Y)^J|T%Tw%^$^(QgcgJ&*Z5L2@5h@}1R|9NNXGtGm_9&Z+yJoT0SkJZykFXm(z zd2cS(-BNV5Zu?1#?Taow4-eegtsXux?w8E>(<;oTnvY4tV$xD9BBe7mEF#tSeyQ1w zl<wTJ8{mMNtaw^5KBo7Ic)0V<7crg2ao<kMw`+Y51gFL6_a497eCKlcPw97azrC2a z+)P?%Q}BPiUlIv1_c_&OCe4ye`}ymAPQKMl(|3ZdUULG|pT103zviF*!nUYanF}m` zMQ1FWYWC<(*T2-K^}L)`U*9h3XFT?GX2K-RWiu0}nbuyvnJ`&*+s(x3w%;rj&Mvf* zgc>AR{N1$G=+w%Ne*#G*8s(RT>x8Rbs=xd(L%8VHoKrTePoIDFd9|{)@>yiUMAd1L ziBsp8vF=-V{IP(td!yHN4PkJG$zHShpjvd!=EHi`Z;Bme@#t61va8#*$m?m=ti|>1 zVo{mO=N9}@Y?D+xs*$|Laz7+Spqa+V=4WY*e#p(sQdujPH|pDO&pjd;ZhufE*vI&= zPW<;s>nrNY+JF9C3-C1E7drn>Ea!dx-lBlJ0*8AhBuz1Vw&iDG#kub;l27M+UK0Bu zCA2d8dVqKA3jVK~8oTS>u09w4>DgYH++VM}THKzNg&R1w8(Q^VblqOmA6fOX_>TVM zS8P*`_nJbjdoTSTl$)NNDO_Q<`o7@tV-^pm*tnLep40fuT_<hza#26yCYit`M<nkT zUfuL=R!v9X%9yhX(A0M#GyFmLyoH~u)SYJPELW;JzNg&DCu~*y#pri7^Y_cz85TWy zx&8HIeX*Mk!C#JFSQF2^Hs-5sweSAdTPz{v+S0eVH|1&{+}$p5Z<8?RQ@+pTS(iah zz5D9vx^1<ueodYHihT<E+T@NzHxI*866b&2n`{0Zmd+*1=kl7}OqyN&?EXYZarDmm zdqca;qm$b={?c)miGI&%dHplIP-6X_ds44P>eQ3Z^ID4{6Xz#|tXltU(f{ew?}*!f zRNJ34ZGIKInN^+dZ|jm5mwhKWzHOICYY|!8W4QeNMl<y%J@s0jMW&pOJn5*hc+R&M z#=k$$pD;nTZ}-cE)9WV56d%&8j59ctr17|N@2RJ%@uJhGN<Yosbocf%-S@j%_B`I< zJUb7fYF5nD)$5Lw-<`a^ea4!SnAELTKdrw1a{8a}eP=|k?+7$=t>-)ImI+GQB~q)q z?bswtu0dmd-q*AC>GLX!bvB$3I=yDYsaZR(p(b%dW20<e_a}euF}Xe!O25+f{K}NT zbC&N{YyA#$*8OTyaB=%5RlVKH>v!CintFTjlDjoBLYtiL`_+O<{K=k{3dg_Bhvpv3 zWZ6?|%G_mMgZ$O?%4^QYNmKXb{oivk#ZojYIb_x9XP-m&iQkcZ_v(09)$!O^uHIKx zTi&akT(AwAG-h0W9x+?E=$PHqO0J8$|1%qW>vUmYsJNA!kdlz_Aju%1ATgn+@H1!P zgVUZmCzB^6B_uL5{r~UyiNj35=xNF_b)yfC9t#YVB{nlCc06N9(lj`G<iN25hYMsq zejD6jnDj5<?mUJJ72abE6}NPcHnO%d2sAwW|8CcJCM89OrfVJzEp42l87&-JoS0jl zX*IO`dT_WYB28w`cNUe}?aguLE|?UB^7`a9F|GJf;(R;6N$&rv>M1|>pPqJ*DPj70 zhc-Ly)x0TS_a-DTC8a5dF;419nAMudIw34MfML^(2A#MztG53=Z9NBlj?c(5yc;MG zI3Z|4a6nK%@P?p*z=*{vU;f)Ob9`Vb)nQo0-Ly(LY(l^U#?_l!Rww`Y|Mmal|Ly<$ z|HS|J{{OyteZ2jRUkR6+d-<=~M*K~9*38Lo@?Ms4SCa+<gWP9*OK`Y3G|2MYb>LB| zjz~8$*zhjt->Cy1@>O4-`Y#_*zH!69|6lUC819|qThCB&YidaNv?Xo=EoG1Q%Ddfi zS-CNG!@cTTd#_&q#rkr&L^)q=@vIa#_6rw9EHC)IzU`0_7P4d|OCir(uA9YT&r55j zu{=8D`00K2%C~<tx1NjNcz)mXqblp;kMG;#VKa%nOHPIJWQe-$ROT5DD&``07?^J- zTP@e|_FNW!TukoL)=by$B6l~>^PO}=c==3~)Et)e5;GroNd2(>mG8OutJ<{Au+N7I z=T7TQ`J2POXVpO|#gwJRN_o$3Y-3(I)y|{n-io(TuTth*%9OnQ(ZpWSKaNMqt91Hh zvv1dWcT5ZLf6X7u_&{S~=GJ?MX0?fWI5T^snrjuGT;A|GGv-KadXvPLgDc*;MY?t9 zepQ}-)c5xd?eCB4Q`I}JJ5QN6b$@^R)+c?N59Jm{KeE|f5G%frX{G^RWJc_{GdujI z+}snG$!3)KV9v%XP7<qc{n*<%bKZfJNW0q~YvgD8OU}$W^(0W?a@z{`!x3}r5>DA2 z`CneOEBBbi3ZG^DM`Cvio>;SR_XXY*zJFN<T{mycWtjW5UgqzX`5&8WuEjbZsdV;V zU?<?te~G<OXGXzePlfLI2-m0T2|6?PJX-TU$ir5aE##Zl>8d-AzeL3J>50Xb=ZZ2; z<2|(K<&T$I+sw~zn$S5fB_L9Ajnm7lxl7IodZ>i?|2jJR$0je+PkO7Xx4u>Bnvt@p zRe85k@pZ4gt&cZag&%bZkN!4g=`}C^_~Qma{5M|dG)_~}n<w*bQ7g|}y-Ua5dYul^ zJi6+rfzG?2>xmck-k2`*Ddelxx*1XZe^XX;C`Vo`>R7NPeA;3EfTy!|O*D6pcwOZG zb?fQZudbibO3k11z0kv7VsV6|g`!zbT(bBz?YRMSj&sUS7Y_V3xm3sORKnUi=e7GB z0vz@~pWUW;<<#zqig_xDc2CzOMQmq#cYX1@w}s!tE#;ini53?;e6e$Vi&6jP`#-z> zi%+(G_o_>W;eS>Nr@7gtJAabms@=k7Nfuo{ctWnGv3W69D%X|!?3b_P@82O%>!fI} zakXZ$|0CWHpM$Ue-syN*<m<!QLlqJ3e0Tj%xtw@mbHen%{Vm_>cJ7W^cRqFRpM*V! zvZE5&Z5QORZ7n*#Z{pR)1C3IfI7?P|Y-{K%nJsnOV3yl8wjAcg3G5E6)+|~puJ^9t zUtyjga%Hvy>sPS=$Gy2J$qsv&7caQSs<q;K1J|qC=F`l|-@DDP*GsoNlVf0HU}6wr zVE_N0fxqI`l5o}vC10VnSu+{g*xFeRJ(!Rq=G5eVLLmBk&b7DOOMmL^e*O*)^xl** zD`qdRU$gd=>DOONcS%V+VR&)>N3a5K>Waf6Th1J~^5jUx-mtY(j$PwfCUmjm`eK9G zR(oDvb>H^hG@X%i0pk>fg%)flI5sgc2yn3|GV|<cExfroO0+>lqf3mn+jVMf=+@PS z*%g;ppXXTdtaqWvhNdLe6L$o*us;b&p0;7<$Nz@{Lb)9_wX4qFQF?!0`1Q8Eyiqd7 z9ru?TtE+9E$)-Ks=wyrEq|P<lLtd^q8OZkdT}9}he_Qw4ZP&H0tvNhl;%bpeXKKT( zxu33Tz0vk7Ah*Eb_4fOBG+5Y_^U6~U0y4JDbGHBaeWPL3=lk;y>aiLLmS3_7Se-Xt z;@pyEb$!*Y!_7ZG9(=S!{Q>8nzxm3`ZcA{nvGaa6ss0z=AO0gs;ltI_`wyP)6fSm< zd6)T$;aJ0YWjDUihuX_U*cezm3|&Ot2s;S|Ijn3y7BT&A-A1-ak|%@CAMj5$$x&Mu zwK(L<BwOj5M<QqImT>D`Ntrk8?v^VGtKU4~+&4)p?5W<P>3r3Xe+FpG|4=2oski!P z@V}4$?p&J3y!29|t+%Do&+Gafq4WLMv=v{{VrQA>GvDc*?xHP?^7GQFOn4nWXV+~H zK6684*%zg+H`ktExTtWWb7rucplL^-;LHA2&u!^S)2+fz_K7Mi>G-4V%r$%GHr0h0 z1}?WY3hOdzi5}VTm_fGs-cpCFfhQ7K?tZ+gCl*_xAl0Rz_vfQmzx~p$XO|zHbn?b! zgN?VC6#^I&tX<ad&N#ShCgY6ES(lTptOz}I^w5b}2XoZs_DDVqsGooQX~~T>B6qbv zJ}<FO)MH=D!NX`2780ua>I%p6Py5fODRwP#S2Zu|`1tN7Kex84?n&3olOlX7#k><r z3Y~gNJ=c`R+)A0czT72+!Sm+ib1TjI-W;8}p;9DdbrjR;B;Cd@>IQ5Kt{WPHH(RhW z+;Dho#_Q_O$f=dO+&VyfLPGh%=3BBwU5yM&HN;DIaxchv^sRR8<ONN?i&h4GRa@D2 z_{CeE<r`o4hDlpA#{Fy;+k13l!~Nx#7HO@`s9s`WsCy<Oq0rr+ZO1$R*E%~-Y`fdf z+a{nZpa1X1!Qh+e>%!Z5-*(qW-MJ=zoOgcthmvOt&tCt^$^F`X-oNF~=6`TqTia<6 zZ22zVR$2C_I4k>(*p-hD9Xr8rFHI%t_s3SZ`K{aMZPXOX+iNQEa8}TMc|C>zi_)8i zgH9E$IH;3z^zO1a;gxDrB{*1_#g?uuU2W0)wYDSWae~evjl2vVONk4a)=PO<*3T(q z)?f-cspp))s?g%O)96_z({qjA9eHxWhl8xQGoP=woj2v`pZ^{02PP^nS<jGWs5m3{ zzc$~~8|MNJ>~J~7;B_a2-ELjkmxG^Yh+0kX)OsPR>8-LRbIPOuUY=E5K2ecb0W%NG z(OBB3YI%9Vi^+0UNAKTz>iqh2mdAH7vA(lQ#pOzNMcV&<BWxzbb>@Zod>hL*+%KIU z&vt+OTD~r^uIQ)h^0z8)<{wmFes<|am(SOJKC$07Va6T1B$Iw0$+EXMmd%|tPx5SZ z$MyJoweypu<;v3L+1|>oxv;Ug=Gcq=<5#aaEZFinkNvy-=X0~CgjXv+VN8DQEg9|| zUc}vgVmbF~t!BNN`9>ByIU5fDI=%l<{9}D4{ggzJXNmH1Cd}U5v(3-V*m~;4iCd;C zT~?f5TAu%ju`r5t0fQFn$?05TtFtEBItf^RZ?onKTfHRE@!VURWgWqO9Q!ZTe-r+8 zDe~aAukQn^x>n!4Fx^o17Hfm_@914ztC^jfdU_a{7B1qRDXmoWZV%G|eftK>`=6gG zi55P!Pvj8RcG|?QJbOoKy!p+N^*f8c-wJLGOPTrLyJXTGh1|r_kT3E&Cx3fY>-nA6 z)feURK3%+ddRf0+PQsR%e&*Z{3mmsCW`1(7&*T2y|KEgfH+=uMC+61XJy~^yD?ePw zK6~@aBW6AkE_S&MJH8xfZas96UGJ>S9FA5szH=u0SAPF~&HlkJ?|uCH_XQ8n9sTWn z__w~Et%sCdM&`2%6&faW8OwIYOTA-fvey4U>;BCBEXxvwXD#DTOw3a;pW%7rj^Apo z%NDs4tBg`oPY8PQYUV}Q&*3X>4Pu@U&{V?1F;RSZ7sI0x&D5j=6AcSbbzPF)Fk`9q z<Y)icSYuW6e$T0{@YlcmU&=#CV2b_BP>JWF26cL&n?KxF)acO;(h-rEo-k9cugI0l zX=ZiGs*@?7OI%qTa}s{nT?%|Vh2Lr&$Ff=OHxr_@r>=@n?7v@jUpcgFp0WFc)fX@R z{Bz@5)s?v`Qa-$#Ij8l9&GQq7c-GZcs)$y6d1!Bw_dEFgroxo#vd@+sJH6dz)z^=Q zu0?x^$5~&$RsPDP-m~!IpA?2t{}t2BlfLTiTyk^H`7L~B?eZ3zZ}@XId9DBJ#^W*; zp_c!CICFou*qV`3`Q{q$ce|j!x4+LaKd)MT?&0ZI=b0QLS0-+ZUHR4^mtpR&o^@B$ z8MTEROm?wq9?Z*3JGP6PWw)s0&Ru(wu7`w2><Lt4<>1e$?OpJwg)zYQ+2)02J$C*V z4t!gC@8svJ-vUB8j-8(yFk@Zr?UGHI5@#pR-j=!R?UTr&m)&bO=U<8uySrFy+Wy~v z<?nvgv;TLmT)%IX+})S8$**}7a&l^U?Kf=dzN%fWIWcGXffq4SS!Vs4{;a4dID2#Q zw$<i|r#g$1#B2UY@Acbw^2O2jb?=Xt?|j|A@BiJWO*f7_x^%ClpSfl67miBn|9T=1 zTAl=LY2BAF`*hvGR3&qh8C|Z2<ECrO30bDR<)t&jNhynfPgXU|$_?8Bi@pk^++G?{ zmv%5@Ta;jy^ua6xfisUq#8?w~=NwzG>a?^`m(ji)g{BJO@Y;{l*Vk5Rz4fk&+xSiN zWJBoP)!B*LXA~c|owp;}r;5!)-1ZY={-LYycUNB4t@ASZy>@oq$D=lx_u1_YL`vCA z4A<V|yl7&$o;Bv<8+OKrVil_18{C`Nru*<WWSGvrx=3qIVAHA;r?ADDtd;_N6H+)t zmQG?YN}42ml69i*?gj5}mUbUMH~GV{Z+jN+*kS+9Z`b3eJ7(MP%)esw^TxHz8m8)$ z?C;G{)*q+xADYg2|M1%T56XU6N)#LkIrNuHS-a}Hh2YcItE*?;-1wKrviNV;=Gv%- z=Td$<Oxo*u;GXd6s-wHTrdF*s%LrZ8e)!#sTN4b0N?W;J7qvNWcTG+_Di|ePdSBN< z<SWZ2SLO0IC;s^yc~|rzYL|iB_ans$l4@m}Cfo39Eb(=<%Q=#6-hADI#fod;Cr=%Y zovigYKTUXU>#6rn^!<5Rr850DMxxW}T;Kf4Dv|3El>B~mbDd>2TfU6`UcETUXZNz@ z`=76?)qeE!etezcPvv_42*q@*u&EQjsaLh!xXt%zCsRk?w{vDYLpd4RI`6!mf25YZ zM%ew^|0%Jew_lx|lzMWL&UdZvO}e*lNZd~FG2QFT!0h)xh{?0zLUxs6xuVJv_3H|I zoo{`;7rpL#-Tdr>LjNmwua5LQA@J{Q`SgmVbHBS;=RZ4J{r>m8oS209s@{J$Y&0xy zIAb2d&L(rP=Hu_mpqOiAAM+0__%Y>4@LhqE9n}F&HGAf%EN=~}{O%NXr1?$G)#CxT zWiR~`_b9X1aC7u?nkbcYyEBE)U^9Ez$?BumLd2aUWbA%DJLbDyey-u;7q4fV?W-*L z@$}fy+q-vd)6dF@VYz;^_lfuVqyN6#IdP)+#<yG9H~#%OapLnv`+eVDu(L-;^VfZN zvDm)xwZB6|yo1Mw?)eGLKax~VXBp1!VcRRF+^55`nWc%7O_^gF|C_>P^OKIXcBFK$ z%B4O#>Us0NmFFkU61ACpQ+Arj&Q&;&6(ae7>0ruL)gv8EdDE3#RK=zoStxNY;6TuI z4XtG?*PHs1?SIeh);PS*JT-gPhs)&&{vxtV;`P`6Sm>91!T<2}x*x}z6((j!w6o@j zURflrl-6@#^0wP&*h0FqO>MZ=A7VOtghg0hNMjijpY~+UQ+-^=KDiw((f1ebKct^3 zw9$m8qcBuVIB`+emDt9ps4mBItkWCXs%0;IYrOf3v*o6kT!XFr_RCeNikfcI?kav3 zpLgd{sBN)x^c#ndXPo|AzVqkfNj2_&NuMXZYkG7~m6x}B*_D(RpKOwrm2E6M*4KVy z`HcDvDaQ`Zwl~u(^IoQCpI$O)?Tx1WcQz%K6uTe4R8}AN`OB&p{$tNXzpq`%UfCD- zXLZ2ICpY;cl-=&mU3=E_YUtEfR?EEEtY073*&dgw-WWEyk@aG$=0dw8YZ~0NH>s)z zG+r<en&{wi^d?i}>qmZy_gi)-->Ce#t|54_tJI6`@2kq59C*O>?SKB)M#B|m@459D zV?XWrQBboz*=@!{K|T4hpBfq4OdP*vs%MovWS)Jyd2aWtemUtKUngs?uTEKW-u6Yo zjlWh(0?8krTxDjy&L{G#JzqL{M|IkYj8923cD>SxofkcK*RE9dbl-ahcTzZOv(BHI zJUe=Jxa_Qn%eP0c?ww(68Wd+-8yR|f)4fM`cWlTG4F0{y?3pTG=&H${0ftV8jGq0T zIZ?RQE~&(7#%0E9Q3pC+x1HzfnB?Ch{i>xyzx%^nBYj=b>kNI)Q%{sExYJ;<bZNVH z!%81tBV8fMwH}Q^3%A%vRc>dv?;hNCkbA;+Zmw5C3)uJNZ=PRiu<QIh?TC}7`|UlB zzSz&H_DE{8aT&w?7n^jJ@E?o&y5*^B^wUzmWFNDsFO~}yr@DGhw7-7%gSD*0nH@P% zj0-jNl|xFBls1^Ux^~Dbn22hMJ#~MoVP&H;jU`D{rX+LT0`7#v3&dKp(zDq4m>j&X zhko?Z@SX6Tae70TVbH9EhN6-=>r-ypoa_!?th<Tzh54%4$!}wmy^qhkdCThYiEL@> zZR=)9XKeVDv$gj0ZuV_wSL#UZs#}qm{rg7PrxuHV2x03Pcej3h^5}Kkt;?_VazF4^ z{b~L8>ZiHyYdKk+t+$w;s5rUY3OwX<Nlxu`dqTgSHs_DnJ|Ejp3H<TxtT!VZE^#?C zxi;}crKshLZ+iJ?ONT<xamk=EmWey3zdU(K+xC9X&YmZ+65E^jo8ERFG7>7Pc*h*A z{{3xT;^P;~^Iy%|E0Ax{_Sx^4&sOuJ`+{w9KTp}WcC*&0^q$i1xw3kTXU{UX+miP5 z^7b!Z-_+FQoZP<c@%_lV7nKh#x{+|c)L2_9y|46ZoOMW)aklcC{j&vxZq3-TCd2lJ z`<pCNL+SH-rFJJdYfL-Bd|>0N!$BuY)gt0%s<FgP^)K2K(S1am#jf&(OKGyA)a9h3 z_p?&6OgXu8)ndO~ZacMTZc3x#aiOizMMs2<Kb`g!Ppfn~<XOH__jr-=q72E|cb(Go z|Ev)6b$st)^R=|mrpHR{s_)AAL1B}^rB`g7F+u+K&buqlTocqvn9s-EAtY%S_R(v< z*lCF-w!;s0TIz-GNI1!TX_Ef~|0jhCGk&*ePw>9sV9?mFu#dx~@{{7O=kM0J88W+m zQhLBQPxq<y%X<ZB;qTx6UVrye-CbwxY4dljt9^ar-|gjZY&I_}|GZ_=tjNV{*Dl_i z<+UvL-P_ok?|4h}`dy}#v+a4SefI3@_t8Z^auQz(N4N8>&&-T0uE=;TytFl)Uw>zQ z?dOk+G_`O4x?cM8Z|v(|x5NB2mF8>C-n2r^=2B?H4O5|YktR!dOy1_Tw6LY>$+7sZ zPAj|p_SBW;KcT*-L%+oe9zHjFiG^6@{pYi$t-p}u!pgJA`$<sQKaW-?7lD=jOI$o| zP89L?4sh*pJkq&QIKIZMCs=XEg%7f`t*Y~XUNOy{f9Kjut@$A$*10Rb*>}w^7K@VN zPcFatU{~L|-pgLy%htVqbK54uhfVt0+=!h|F23D6dAt4IZMC0&mHHaaJ?k1({MzQ} z)E_0^j~$$}>EKt{$>v|g!+hhJC*3}}Xqog&{v{EQ{!NKk<j|$65&A7{vAt%WkVt@z z^rwK111E%U@6214Tb^Kf?SZfG(OH?z`rfC$$F4khV``J}D*v5~lR8dL`moTT%4kwx zs36y~9l|OPn<rR=O4-a36V0q>)XF?Er|q|h@TD0IYC=uVv_0MC)c-WFF@3+Y{?LY) z$y#epA9<PbXOhPBm-pVkWIkk*!pg!GEy0vJxhypBP<qTF{^v<G=2vID-N;wndOJ4p z`13c<KJuTe{?RvAto)e1v9Hv{IUl=u`YXlv3154%_lN5*^D3UeC9Yd1^01~j=BWMX za$I3xYowrYf}tho`=!>${Rj6i`OEO{9OJcr>EG7;@33DjxiLMr<*(k6EB%cgiU+1| zcziys;*iny-8}Pd_#D0e-l+P<=b5h4V*aGI-QHxpZKwP^<NuHDpQyjFF1Y8`g=)d% z;^a`*Kn?rzX}%|zuQ$1;AD=PlN)iLBp<7Un0*8#sGr17n^t~~W%?A<_Kg@};6YAGE z^zTUJL!k@HRvRZ+Pi2^Gv);rbF68IG#-G`@+xXUGta>ria%tFxyz{GGNWNNmB42XV z43qV`Q=XdLEB2gpcW!j}n^kl6<|Y(eUgOEj+bG<hKCR5m{Cm%qsb8;E)UeuJZw;QF z_54n^SZ}zD)8P!uyuK%AXLVjY^wW6y`6#=k%eU^_C-$v+{qdwrT^SPm(hk4w`m`A5 zSTy$pI2lWAT(QvZ#ABsi2{$Vhfw^z87xH)+-B_+;yU=2?Lhqac!L3n(Eh=460+Yn_ z7&&g=OaFT=_}A6=ZSi~W=wAy~NzGYzW3QdMQBkR_@|j)Jcp}yI)m%5acR-*{^l1Bp zzN|;r+)ST^$+Lybw>(w1MzHB8Ypa9ahMD3C&zyU9Tny8=Y}I;u*?NO5GIQ>kU7B@! z=j*excZY}TY>WJ>x^C9QcZ+_?|9|%F@BaT+R`Kfp|Gw>az4`GePfrx&cl>=id45mr z^LBIdbymOLR4?wf`~2YE(yx=v?g%RX>$@g(IwA1sfwSCMyW=Eh*xiXXDh)k7$6rpe z@J8}?-YX&@Pg6|R?##XSaJ}ry$j^87zpJn+`xvYoyY<(bb^B({-q&^d{*524{`WU- z+gb5zT6NqVgI|yS9nAgq;f`O-lJ^mtw=d6^<KR;)TotnTkwd=QCW($IQ&(Q^a5*}$ zXl=xTpd7WU(gFp7Mo+C+`Q{fYX<e}Ln;A7TV2zOGQ%(;}cL$~m;tD+C4E<W6mzD&z zl<>${b2;p4V_WgW{>meU)|FSgg5Q6A^75N|{=M~Q%kO)u?Umj=c}Lx|k`yZ=o3cF$ z-*#Npo+okNahVCv^e+_*mwt;l9nw@2w*Is@=u6R&mjwzTQFG=$|6pBn)35Bl+eSUT z`JZ%rROP+8L&d{x9<SII(tPk<A^*0JhmIa16F79V%9+|dyJEBSy?YER)@um|DoeD@ zTgo6l<8EVVtOU<ddltJ^`~Ba~%C2pEcXhJ7&DFiXujX$2UU+zo^fFPK9|3Q=CC=V4 z3#s_O@<#np;nLH6XI94>?mqpnzU2R*YWL`q8)q!ske>PZ+1++-C2r}wopNW+F->c| zV$2=Txld+e63fPtZNax%wF*`Hm%YxJ!6hP4KO?E**dZw$-t|$vHA}x3eV?~OkR!fr z`iAu`9$iW@5^Ee@yC)x#_{MooC&{!RZPxljyLnkR1ZOOjc3RYY>xRM{BWX5wy_#>2 zAE$?{sb+rdKljhoKYVMiCY{jDdl4^q^V743nU}xq-JrKA?ViIzR&nXHwL9|;Tjta} z5zMjKpLq1mPf51f=I67ecYT)L{_g)V+2&<?X4MEQZBRSfoa39OXs&R_Z}-_n*O$5P zFcubgw?<px&a%}mJ8nJ`PssKtICY`SA<F;JLDTg|j`_RQdvEejdN|ee{r9)Ex!!Li zZPHI?SN}dSU9Ru%^Q~JSb#7gM{NLvf_txLFt9bbE;O%{F0@J?e95^<m)6aK;W7EXe zDeaSmos=e>mRaJ+#j#Z3rsK6Xf#2dQEa!V=2_2ZPGrz&BVN39Y;LY6na?Jlde};3o zq)+I2VSY0GSbwLw-!ZOt{X6}-)P>xaxVtN*wcME6@9uhY(lKQP%jJ5#>fNj4Hgf!y z5z~|G@Lcs<yXn=wUn){g241aGC(GMu*#AoSvUB^xoyPud@5TK!tfUjIqTB04H!|nR zaXou>Lnm^k74NFUr8Ud5c@s1}`*>$4y>edEmf<4grgdS#MU9u09d`vEZSB!wGjkMY zN(y3f2z#rukeQuT_T4_MxXiz7SN#N@?$Vi=?JcmNx8bOVYVkUat&_g`d;GfGHSK9J zJ9m9j@x$p-cH7?n`#Ha(cmKA$x3h12c{0a3KQ8`e#!185ciw)^pHuqSy8ZWZ+1l3; z%P!|1&-MGf>;->UR9DWVGW)LH=ht?x-sSCHWYl|9@%)9@`2AlWY=1vbZ@uJ3&r40l zf{Rx%aPG({yR&GKli=!O(~nP{_oU3j==8Md;$fRq-B?+=t#vhH6gi{S-ga<_PFyh| zCZscEHJdg!N5vLVj;Ms#tDE+3O}c#h@#FmczOCC&UVIT2X0>bf8yAUMX2lwFX?fSf zHr<;2+T4jU$JbtwSu#iU`HZRDnxX7_81)#X{{M`s`Tq0srf*wg|7icT`TfFaeoxAd z8CMO{G_9AgFIjRlsIpndp=WY&!v`BiH$~Mm8Y`kB7P+((IPE+AL&T{oQ??<!bdFc! zoGv}p34zNj6pzoiBD7R#<&Kt{q9?y@xiabe_PolX$Dc}DIL|)Zu&s6XH9yX|hwE>C zJ2o#aKX3E(M;pY>-`m_!$CX$jr?|1T*G9fpG3gBdlb^M58~9ZiZ?i9DRA*9~oHV0O zU|+-Jjeko*T(rc!4}J4awGur2OU&mqLpbN1aI2$A%S8o3C+lz6mZGNexuC#a#y$4w z0ZH!y({)RP9>sTm_t9Om;jz56_b0jTe@6t1D!gNtJoTJ?Jnh<1otb9k3e`{USV|=o z^k0A5aqs!5(-*{_xIE7`I#YFNPU=aa#mSF2Bbi_J-%uA^+w~&l?&DBHvn2_uB_>WW zVw~Ng_E~MI-vRCWPZ#dE#3g>Tr!7H$-O=Qfl0VYV{pUZM=Dc}!EzhFuN|BZOV%A?~ zn`iO;(B2TO`Qh7k7`#wRDV`i4a(}5wL{t02n<4stgtU|vYH~g)Xq~xw(FX1x6;k|W zd~>1}9-PQ#m!Grh)<%<J1Am6O*%GJ1kG!+KQT}0$!QGsaloX5JAK`54pKn{v*=c!4 z^d48aPV=SZ`JXFzf9ShRj-CH_?bi$0v6AxZM2lpvzWW#}Dq7O7{$|Gq?vE~Q*?%m& zZrIO#an57f<;3l&8*NP#`SXMfCbqJA&&@Xxdug`DN^5=Yq7%otd0P&=QJr+alwn%< zrX5G$^ylQKsz^>;VtVGgJ5Tf4BR+Ev-YXDHSbaQq+1n{Ldk;M|@7?cv;_~Vv!fd}= z&sS!}e7P^b&h&PRTlv=7Ifq^;WL!MDztjHr{Pp=4I6Ail6~qPISNPXc*(B~aucvqa zrnIx#5f+s~-J9%pl%CH1{p;7az1vb(KN9};_)i<}^NkN5wr_m+_Tt6u$^R=V-kZOh zVfVK$|IOXsU#*w<?tc85L-5A+mbpGGZcnD!YHdHAlysjbX_LFFtB*kF1NnDs>c98L z8PteyJ1n_WEWd-JZoS>aHUU==gS9=3>;C9CPB<QRa8Z=y6s^M*=PmynIe7m4+c&l! zcHZ6p@9Ubm#i!?2J-e=A{VV#T%kgXO;p?kvqQ5ggb)LP8&sO1V)|+an72zKiWtAvS z3JnzF*%7PO?5BL}^$Bic*Ctk-=eZ$ELYt0XIl-~W%U!9o%4tHoq~{gy^{etV&rfsl z70BUOwd1PIZI={@7O5vsnuL}X{a$`F=tJI-@R&r^%g3ue-dNeBcD>?r-#cyntylGJ zcC6OgcJ$q`u+@J*dHY2c?oZOzlV-ihE-;ma*)s3j+0@M)o`Ic#oxe<HEShpewERLv z$RdHyEK^QR>zWvHYRRPFu=yt(Rw_AY9sMb?u3^d*&068E6-<+2Ci+_cpA@>Lw5c-n znCS8~&#U?NZQs4O>fyXd*0?_>Hh+D0Bhb6>V{H7K&DC)~gZ*o|&-B{q&db>IY+Y+} z_4Ti}WsY9_?#*$kOh#LB$Fk(>Yd7#jMU?$*&zb7a<g#Py&KZt;ar{^6R(5<)>2Z}8 z(`YPpHe8gbS*#NB`7z@n)mp#3X-Q#%M;b0axfH$bOz}?{E1jTyTBfHojej0tV&^lJ z@v~XAeYUQs?dfW(@Y3ho_3m%1KK|+2iN4L(Wq)2Rn0zj^#Dph*=2c^>fIGW-Om(w! zWvg=zZ~OkMb-M4eKa>6>+%5>c9T4nU`S{J-&(m$T?JfU$$93kiuC}g2^HSH&-8`$y zOX}IXIID`9;OhzUhfZ`)DBzCX8}&sY<nzYWLOuoAFB-nOJv=Z|#B)i7$s4uo;~{+- z8##+tpU@Com;P3$JYa`O1&606N05~IluJT$0+NEyE(+SIc}HlqrTl6k|2g|6DE@hE zl5Bg_&)wn%#~lNvo;PO6)54nX+*sS!pfyk1smAt$po8+EQqEq!jF4joq*e88Rs<bn zJtp8X!>cr_gh|V;X~LT&(IR)^f4xwC(*C($Z@sN~$@T5u-u=CH*ZFzYbouH9xnJ(F zmc0GZIeXvNj}s^M*S}W#_Op@od!HZE-3y8;eiM!Un7fp$X*=&BWaB^g$@0WC2Rb5O zX<SunQut=DGI`4qlc|Q6pCx?Xut)uK!BQVjm0JnDYE?NFD}GxX_Wo$=Hch8Y;$tk2 z>0g&GOZ~gmZN3#c|LfVbN%H3x`Du6fuPgQRaAejWp0{7(uINcEy`~+9lY9>J#X2tU zYx?Y;BJXp6q3A=QifM2N`&qS9#~*oI=6TF{ptez^-$S9a;l>q3x5YCPaz0$0X%M8t zc*G*3=Emgp2aj4cyJcT_oalSu?i0?8j}xx0IC<frkl~Ac`qzH0ykUDr{(ASu-FB)c z56!+jZ~py<3-@n%a%lUmcMj6OlfD|oZ#^EL#6MGSkCwN`5An$Xd{ruwCO+OagJ-4) zca5&JVBoUu>J_U>Dv}q?Kebf&!P!$zp)Y?_>7|*7cq`@>&6##D`f9RglGl}`(|wHk z!kc3{MCTUYDE(2jR#5-CwU7RBryu<JmUnmE(92DGv*F&;ioQQn_T-qjoVUKCayp~) zT*s~$H>3NB-E~)AObXxq!J9MuzU0N5Yo!_!PN`0G6%6&0d~hXc+7Y)E%D#upa;BRc zk}C8pPwdrrXg)ED<?5%0SFSqlE7tqfzub18&z<!1C(nGj@lj;&{l7Yyo%`IQUIg{) z{yLkm`~3ZR{#s{tZus&{Nrhok;spQio3y!aCLH#)xa;k4D2>}^<;wi+4VFh134Dk! zn!nw5%AKdC)=_5`c};tBEGKyHZNq=LQu{ubO6$s}-?};d{qBM{PZsFA@A#*ZY$f-b z;rI~^qXo-;1gF2)vM5Da>F1sYee&vI?F`|Tb8|RPl$LJ|SX?F3|I}iB)v1}nza3t` zF5UJ2ck1@l;)*pN``6t1t^YAA{~Nc*+Hcc(TE09E+PZzwiHLRImKhjddbF9JZ540I zKZm@@jyBWhe@_go6Zqg|)}()cLqYtU%e;l6tc#u;vJ_d#(BQGH?2Z4Qhq0x+Yc2Zz zv5KAO?>nBf{r`n`SFb#~QT_jO@v(|Yw_fz$yc2cdP{+9>p-BdfJUa3(0@x-NG~HA( z_k7$qRVR19WYGcL_a|m}Fo&wG3oEPEE}ZD5{d8@|2G;dAb5CB|o4g^jcim^J>2nYM zyZE+DIqsQO@s#OD{UqNSeBBry@cU?M$08#E`5ymE%Q~!o&hTSOVteY7(;M*nhrIml zZM;H@4++*xt4yn!pzJ2a_;TaC{!0pH1rzs(Wll2U^qZv`edFcy`Ttfo+B~++mXUk> z{^)X<b^E1#W_TFBj(=I^*zFZy%;!EQb*bsqrDs#mE?jW_b=l3_nN>|otPJM!aC2|* zQjuC4V(hy@;ozw#-ixOgS3hFiG%HdkTp;=6;b+H1)oY*dC8STR+PUS+4%1k5rA19K zU8i<8|7|Zn*xmR0QlT<$Ncyt3uI#!6BED}q^%&>QI<w}G+bRATAFoSqxV>rh%L9o! z@|Ott?X5jAS^ZWwd%AvZW~_C@#nS8Z=g02dR9u%n_4KU8r)Ssx`(;@z9&+qZ=;dmw z8tZwoYd=oeKGW8lYgYH0#&uV1b7wy{<8r=!UqXZPM$g&9#<Rj?=L`Rxsw;8s_p%54 z{(8E3oRJBv_v=5tZ<_pai*W0QOC83yTPN6G7d^$d(VFkXXTdey?(0)6<)o#z&Dm}8 zW9tm|?lO<Jf*l?jjQ@S@HdTr0@Ev-5W7X*m83*n&xV_`~bYh`X>fs*uDa~~+7-#s* z>HIJDCi-dSOc$31GnSr@!f_$1Y<&FvIyK(<NUf1b{uiAj^DpGn_N`*6%ce>+KUFhW zeSWLQQ;vXn1^;jMU4HG>w>~Rl_qQ*zMBB5z?sBrR$@fqGdi+=4efc+0YV*47j7saS z&nw>EGuPV8#-cJ%ac|<WD>F8)e#pIkoy|UhIbZ*E*Dtqb6`YzRWGE*7YtK!;*{#RZ z4f?k(mFSl9nZ%^lq#5|wI6&vC!0rQIKV?PAHc8#`J#*^tmMfoM=*oAxcfD+yBd%wo z@@%85yUc=T%1fP=xTl-VY~sB<>(~0(VIfvK3X7h<S;n+l+xX7cd&`zC-~P4aPDR@3 zU+&f!>1oq$KVKJLT9a}y_jGoo<Cdkx6${Gm|D3(9_BBVa^M&PhYJbZ`YqMBu4|UmD zz6rW^r*rNkyT+oBTN^xzY^HE2F-|x>VS~rpONXYHx$!oxo%q`Q$EFymGabTHk1b5Q zwR*Azrx2^ssog6b9~rBDF4SG=b^drq>I3y9fk^=`JWoyE5;Kk2!J=1Wn%DPBNgj=V zR!xaaIwhRM$>OcLd-cS!6RoY1&(5j4J@<U|>BOz0)vwPTQ8P?k`SZq<$KRiq>vF%9 zh`Lq#x^kJ;@pkK3o<9!vMW_25-T!`7`mUm$ms}G|p4K|1?2l_aJMqmS0XNm73%d?+ ztv{}JHO=^=_>0F?!OxGlXeAxoz_Kt*esiHm^nQ(F=iQcv&sqJ<_sX0CBmKoGK2j_~ zp}##t=U&q`j`b{m^6Evx0r#Ev3O+wgx*gV#8=<n}L*quiurK>sLeAg2b$#!i?WZ1{ z;bIqCE1ezl{pQ`;xwq|$@4nl;c8SLR?3D_Bw?19TSXqAa<I>Q7Ov`MJu5I0>*}iW> z`d@GMWp8(;=l{OdC{&PV!!dF9QGZ2wKF?AXyCa{}E#=xpH!yQ<<ZjM1bQI}vZTvCm zLXo3G&^2bo#q;+&IL>Pkn#y{MRnYUK*3{_42M+NQPc<koO?UXp5~g6@Df4D^=*h@# zg=?#QG}&J}Tq|HavOe;uQ%3_Q=klWE`l%O+b_c1<m7ZpLbSdlp$+;CfC4_?4_gEJB zUDdf`nq1V~@VV&kRA%0t79zhTV{-&#zpd1MCL0lxJ3s$=s(DX|-aXqou0WB#yRP+0 z83D$#+Qc5G3ckNk((~->%zYIZh4t}EWCL<H2QFA4X`X(}-{bp>4PoNj1Qxy3QB_*H zk7tc*OV(7KmK6&Z$t;z6I5lL$gRh^Oo~yF?ITqbnw&~ojAnVCZzH7LbzyGr7@4-Fa zV`A&2R2<Y79_cH6AjIc;g@3|}Egr{rsJ{pjIAHq3pM83R-&xI#KTfyI-4q>=azv)x z$7<bzn1a})3?^N+hmCfv&guGL9vi)C!`c$>y3enaH!mxB!WuO#_x{e>NbcYxXJ^m9 zab-tISmavEfVt)d%PY?s)>LQSpOo#EJ!?73Z?kXTW6eXPs+T8*P5yrCBJcHQW@klA zcHUdpaPN-g;{V5XP8F+Ox>+Gb?8~_i9{JC|Ej|16@9Ed<*M7de>w9k1si5MQ6OaBi zKeuwvhqA<_*4|fM6fHA<vVGc4+pDoDyVfTzNcy<Uw_BS{$|0j%>Bypu#U~p7aYibX zl}4Ox(^)%X0xQ=I#pJ~mjU7SnSh|=ztxi1X7fw299&pD_;j!hr<j%i^Mx9FU9;m7u z{>h)5!K?PwTf*{#;?pJ3s>)&wGdPyaE95BTW3;%^rQmv7|0|2)Vy?-{x8A8-_W8-J zNbBcQ=0}=am8@vpS#|!~nXH)Fn!FPBo7+>LC!}BD+|RG@NAIJ~q7$v>oR8#hPkZsC zV&e0ecV1N=)v04_zP9q@=A|cD+Ppa&R#>iN+0)U?Z4w=z6fEMUpvF+bqPoV->%`H{ zCa0nwT4%x+N=KiX9rfYFOnHl@HM4dpWo&2HjtDKi;P~Hip6CD0+dube?FdtJ(W}!d z+pZ#Lt#wS|n(&<Lg!^yAwk_ImJ$gm|_3jU|)C9ek%T&DW2>!Nd*|NoEOO}V7OVhq` zDIoE=N9yG8*M?h@PuMp#9C}%?Bw?}D)BxKcR-FyARrYRkKFm66#(@H-go$nkDs83N znRzX(l1yH%X?q!)`K0#YTF=+Z!c$Uoj59u7NmQKV_D$k$zrbJq%PI2PRBmLMukl=! zcD?D?6e|&fIc)n=_*mvFIlGY4^Xe{9y)#LzKPGJwUC=#UOv<J1!Ctpz>E(td?E0x8 z_3Y~s<{h5?uWf<N4VSNaHENea_J}z7`LXG+YrT~-?we8b&OOP!$5JeN9izPM(=XD= z>ArUHKh{*_{W<8vJuiLR@of<{l96@QI;WG4@>}v4uCL%*^54R#qNR38#^x@ml^y&$ zMBAn`>3mhYlB`)|qN%au3fp#lX6uz%r>}(Ad{1nz@Vj^M=zfpDbiX4fUw8#-T5BFM zY7W@G^m@>ljSC$-mvMOCT^`6J)5Q5Bi)ZtaJt;w6Cy#LonlPJATVc4ZIB8#wg<<yf z1l8y38(NJvcCPY_+?wv_B&wd|6msvW@__}cY=Pd9%vUB|J+!bm!Hr>S;?jh`3qnhU zbxWGI7Vy6Kv5!A)%aZBq%4Y5OFFjf3;IBhG&X*^;W|vpV+7?f5PrqZc{?i##?bGeI zn$p8`Uhdk~_0PD(_xy>{)6(fzQm?Pukt-O{Azk+4w%E3V%irE!=F=q=aOWJCQqjWt z1N%;_(-XTck!WY~@#l%su1z}^oA4cx+}ZndX-T0{#-1L-XDu&#(uz(>zMm-6vOXnM za${DiBjZQ+jhkX_?F)G+wc9U0z*krJ`LdPVK|X6mPIJ3Cx|n^7KJT)I&)O{~o!hEu zW>v~zv%JVXT`yP)M3(-wd&=|hX@*SJs{_gj`}kD@8$5MA@}~52cC1vdyF8~PPu=Xm zG_Ie9*FK$1&wuxB-_JMC&Zg(jwD~!0^X&I~f4{3{KhEZ~u+h-*jeW(tw%mN#IaXgE zMBmP{DBn<DbNl)H>VMp&+Ii6%9{ecs*mFm++RK15iu2)*2(BazMc)ut9_?ZqB~ey( zHKmV^OV+P55Z$uDi(~FguWMI62kw;N65oE~p^F~Nk(C+ATI&60g)Q|@FNvsI9K{ej z^~CHJo2(66YFmUF-8Z~G%PCXC?A0j0(&NfpkCz@t1#@d+S9>f{m2_`0&3jdow78|W zXKvi)lz8239v3|B8y*l$_U~+7nCpCc*Y`QoCmi|qEW+i7m(nDT*Y@8;YQtx~e68Qj zyF4jU=H}GKHCs%*=k=d?8nEMskFlQA^QO$e>#kLPrOSjZ^^Y^(TXV%YD1FvvwZtf? zr7cXYlMWg?91NNl+>+O<Ss)=~@MTg=7-R6+vpGgQ$N1JhOp$nV*LdRP*N+r#bUE`z zO15rPXE5Fu*LLuQy?qVe|CRNQGF-2Fd0LP4GYW}ihD<Ox88m@^>CS(W5nPee&$@kh zWb(1=wS$=2iG_+xjDK1LLUpHZ=ist@CAi8$QAwolaZ9jn`=P_!mo9xOn0=tU;^|-4 ziN8voaJDrrG3U9XXCZ2=Bg-G~=FqH|<r6~7lX71^iJ5M8`$o|AAJ2k*`KO;d`PKY} z&hI<hE^fUjn-F?CQtgaN$ETS$rm0y7&VGC?QzJ}!!7T%^Vnac_V8+z>3z)h#$GNhG z7@fZw?vS}|5kpsv`srM)9GNw%U#vd6@<sfnBF}o)ilZNozMh|}ebsX9wMWk$dX`>( zeMj`Kcevi$^$pt-)>W-&^((h3j#>Apsy;9_F>k?++<QyzNk40?mQs`MxmT0#ml|D{ znH-oG=lT24xoNzIugdl+yeukXdLX({mi2?coRpJi6L<1dm`-z4-cZdLE5p6!YO>E? ziOx$&3Z)!2HN6sTGlJQcJ+);@RAO8kR_eWCW`ih8hpC9m^fMazH&&;XFf4xab4!hg zTg#F;Lhl*YG3tJH`5NJobd1Sje&WkBJskPdS}Wa!OAp!xPTadF(r=deq1!W`PJe%M zZdMb^{@uCX%Qk=hDD-HHPQ&-K?bj>3Yxw3GZeI5(?DN0q6D!Wz9c`TLyY0ryyBC8e zmQB8zGBY@=+2Gc>FOOq?`9E>`-&s93%s8a5JLIcXZ<JE&cBY93+2e#JzbIWMu+h!l z%ckuJw~;WX=v4#N305n9oilC~QB;bIOL+M7l4pDOgB8pI-umuB1_{;=EyTUfPnKwL zesWjiYUgL4HrDCu?;HFrI4yaSxwgbsTSIU0|DL@2Mc(z|KMz-=Wjwy}vv+mhdDY*7 zZeiCx&VOOQKEyli&;Kazw7r)fUQNg`$u1337BjK@ro3?~|7PDyB6p)Mi1U0Cov=9V zc*U|9P1S#`|D;0~Jy(vL`t)bzzdKK&HYtTW>CQCVI*n(R`U=(N&oBE-&Zd5B-1WHk zQuZs3XDm$GX3b`e8o2^lOEk?MFW!Feg{Y|KPp+q1170K^iCU$3_}F5<^%ZgHBBkHX zyzj`nZL-__Pt`J4^;uuvcgEfRcf<R*jimk6)Q+UKcj7bm{p~ul>+|Q`+$Y|u?Y=7_ z%k9U<^6Sqgmd_`b$A;>KZ`HOkj$g;J^nSu>MRwb{%if(yHP4!GV7g1z)0xqE^LBjG zIl-wHUK^$r7+d;xX765!xV83+y%KruNmf6TTps&fe(zIu$+F9fH>Q6$l^*f+rt8{| z{gQgWon5;B=a?M%R<+k6Y0Hs47x(Yz`4O>B`&&y$by~{4m+>=g=480L9N%@3!{rj+ z2mZHihX0zPb3$w!xT3l8gSl?0GM!nP(7Nr)A!GGBb2hy5sk&>s&&5CP>hp^4`(&a& zHJo02P;IYci<Qe-wd+MT-*T(X_D$aR$jB|fblUEir+P)3Z!-EbuU|a>uv<!#uwp@8 z{A$6;{w)RG)3$xkk@k%!7juwb`{vBLz*Fa>7QEAOcrIbQ*j`CB@Ztp5u0`?__Fr0_ z<CwhVgU5cw3^jQfR&Lqf4GM-`N*P^W-u-!%)AppBsWq$F!?%&A`;d@g;gg^P8Hd74 zG92DVtccQgH+)dEC26IJ@3+#Yo8OD*@$D84F`90D{QUQ_^6#^9pG}IiPP<mU`OW2L z(rew)J(CqKL_A87f9}6Iz4CSXyBVQ}Iz9x;9+pmXIiwx7X4UJPR=bN%<i*-<e>rQa z_Mz6fTNU@(g@|*uhOgPPEc2e$>RPv}Z`RtDYKO0Lkoar$O+-$3amOOFx?h2DrEa0G z(pGGq_u=fiPtTG#|NZ)qX!vV>`cm`VMw-)(FTHWmZe{OY^L<B>&Rp>hI&(DWBxh@J z^vC}TxWtZhPE45L?HY9{x-EL6)_J!I!_7r<3}@_jRCL*7%l?$AndP~><k?eKi_KF6 z8+UvR_VLhmYzj4gb1n3<3TK;Aslvfq=bSv7io>JdI!{$N82W$p&h-pAk5AmIH}+QQ z)`?6D(rMO-7K=zb-KSZ2VuFR|Wwuudnsc^jnqPmi!1v>QZ%fU~hQ`Lv>jEs&_I=wB z9{unA?Z~y^o1WY_+yCy?x^J1!q_fM*%ATFAzP~*;T4Ud{O@e=n`6snks<Q{MG@sC( zF#q|Kc9Y4C&lhxvcz#;X#~|pzQodxdlIO1umY>ZBYSwEy-ucm87%CFBSI%Po_f`Ay ze)Z2webTq;pX|pIeP>@!+c5d9ZH3eJ^|BGW%6)4qOOO6o@JBHqFt*W3|C8-MJADl+ z{p+b0Di7{n``EK+>PwUOH&wSZCg}d~ox09oa^=Mn{g>oDwzjE#ZYtN&`Tk?4k^UmJ z&!6&lPA&Qp%)pt-#I?}pkz|xmg!M*Ep_?Wf%?$n;|8vhc8l?4>hoN=**&UUq{tHPd zxcQy7>HNCY^i$aWGw&L#)Pss*@8_#I96GwQY@X8zcguZzD>Od5x;<Hor(dzPuAf^g zu`*=gtAn)>S0f)KDKR}W=Y2MZd)v?bpY#tO++1#$z@XH@vdSc6>c+aGiyT&T#9Hli z_bI%PR>WR0WA5_v7f&Y?drmnX5VkSwkcqKHxSUt&g=N0{Z(hIK`%l?jgqP>{i;vIP zn$Kpf<S4oBy6gJ3-*3{E3%vQEG5N%gtDUQrwfWYpGMgPH|Np|j)pq~33BLQgZqogT zU9)$cNpr8;XQKNlb?Q;KmDL$Lzr4F6^s9UG{ErvvyVg#h7oyg4AYR3WH+G5PyH~Gr z`ZV4Ih)P%|n|%sNW<A2duJ>&DB#*6o)Z)6`Js+<ScI{kI6?$uy#Wco*vWfGVmWFQk zyw#+=ujAFEmfv5bc;pIhnwsddFi5Z%Fi6jGwX>e$I5+yyj5$v#lDCHls|6%&-1|eG zGqQQ=^Sw+nGi2|1RGwUWHr%f6>8jQGFVD*x?ex9Iw0X7J9qAYMg!I+rb<cXsWb;nl zGVRjwv+I(chq$MPZ+P<g%f&;gE<2udCO2QLm3~^fCt;hIN!8ub)$87P@3DKL^Y`=z zQAWq8MBPsj{4=x;|K(*le&F$k<ay7xH;5+{<lkxON;%r9)@&x98Cdkuaz(*y1NDGq z7aw-ioDkFynLfSJBIrm`_>JUAH4iLWe}DNCdUK1>Tz>b@#on(>dqR)foaq++tTDx6 z(UXa*ZeE?TDw$Vh+Oa=h7I#g$EPgk9|LV1+?f<{D{XV6o?RBs6qV?<Yt=sa}Pe1Ri z-Cw=iO-lN>)pVVgGTAvYFF##!|G2vM{gm!$^QLvbJF@C~ZdLlqn5lEkdlFtwY&&*Q zZ~gXck>@T9e<af03#Z$}pS-89>BgzapJr_RcE<w~_4SMIFY%dZ!KAi`MKo~!L=HA3 zuN6v9gEy;x$h;u7JHmDL(zMkpY#47XGGDg7Db{dSn7>G&^%aj9Wm9{a+~52-?W>ae zt$Qoufg{bH>sG(Yd!T-BMMHxi&!WIe2?;3!gUPqPYCrRtUU=^1wG;mri<VCeNV>ka zHC64=rd=v0GgWuCbv6B;e#7%n=kt>lkA8mN%zXFwRgoRb_CL$j*34YL<ZR!gmH+Q- ztWJN_y`=Ve-hy2_XR$oi)Tw?WJI`L*!@zp?w&I*)Ykqefp1C<(`_Lt|Nt-R$XP<l+ zllN+R=X{^PObv(r&5Yh5d??s}Mf%~s9i<EOAFTfvI%Vk(j%4Q+j@RL(&pyjkNOBg; zdf%7sw@WMb+7hR_E0g1jFUIZn@v~pzc=pw=%M!sKuPrR`67Sq(pt8=hl$|Zyt$TLZ z`x|?cOE0hbxNO~pYr(TqrYEdD__*NOmbGvB*5>K)^}gFxbUh>E?$L#LS9LA^K5W$P zD>r5LR%Tv(JiBW4&+v=B5dqhOo@M8LF+aj)UfOr(!RlMr>|g7A5ff+GRnvH{Ahx#R z!J{9mUhmm=_i*m?wVY)~m&x<<Z*x1%)h<1Ki^-2Yap5nG_DF~QtJ~#gu`K0w{M2c= z@p|_;tyOy}gHF1z=(=y3p#193EajD5r8=kQ1@B|_U8Ywmb}p^p<wS9(TR-Q|dAfDp zo>@lAH?L7U9~}Ko#d>1Yx!~wmCDR^lJfkULYg!TZ_~VttomT$0EE(8qjn{Lo=k|AI z)@jd8Nw&%Vd^B14uXl1_N{XId$@53jH>b?G%3H<Zzf|Ox&WzLZSdSdAnz-!wlLHTI zzp}ngshTwVfA@nsn?FtYd-I`-LD0nxg_#Yd2^xVrU->@v&~SNr^}~mN#FWS4x6M5G z6{kNtHgj^MlcQYb(I@4C>+J$;rag9Tjx>=za_*Wsf1Y*mg5CdTE#DUV@7y)EgLxaL zwT5juedJ=@%v0O6Z%D71CDog=@9Uf6+w%YaxEXEk{^9fS?fL&D>gKJHzx%&$^XvZq zuXcJfFJ`~@@y4&m`)kUie{NIgOxjZ(dEeCEKd=7TtEIIY<t{4OM=;KuQuOrd;<|3_ z`4Zdf|7_fz)9JEp&(n?l`Sbt$i2N3rmvZ>=@|3REC&lZEU$?3I%gSZn`1kI<<{P#c zb6N3utx+9^;tl`1_x<etwYd0G{jA7;{OT+pVq50)N*Y!0c;)r1IA*KrZtlmuqAQ*4 z!l(R8oAWLe6qwn~;VX31w5fP5KIP!$GeQ-gUR1PS&fM_Cyd_Gjc9YwT{J(RjZ{7OK zDO9y=wfBkcn7wN%_pCMfIQhdTRpw}GF6BEDZr*&N_x*(MuD)BRmImstef6&3)a^CP zb}ef=)%UUB(f!(ccdrDh_}|r<`c?YQ5y5vlI~Iub6kZJWjtxm$*89xpq^jQ(k$P3O zX=x_=-hMS(ef3Mn)vVK%E&^S*r7oXa`9d*YbK0jOnQup=e_CGKb3~eL`9h23Gxn&S z7H^2zxn%Bd<G+#|2D1+BZC>ox>68`5ttH(p^_)>8<m`f^IjJt2YtBC7vvA$fe!Jv; ziS^&eB-@oc5<+F;q%FG@cuptC?zD}Q=v+`2pAwm}V*Pg0YcjlMIxAh|7hC@H-nFYE zB|vG0iq^(8MvEN-jtMt+dhBuZdYBrxVterC%VzNsUDal%*DA}hHoP_YZS?r1p~_d^ z>-#Nw&TKAf{&U7qMnE-AsBmp}#=$iik8CoIEs_3bb9lvq32{$0{_Iu{FFch!N1XSH z|1UA~l7F8ZgZ}-QJpFB<$c*|r|JJ{&k2M`KWlz?YzPs~Z<B7swy{<j8?EP%c#8%%m zoi3wqFm;#Edu;_?%WHq%%`%IhIwzKeCFalemn$x8Uor3XqS(vxg@5rqJZyL%^zwVo z^ID&0ZRF8$n#bd9&~CPOZh7lbx$P#?gMVdZDQx-YQU8D9*M(0$wq1IbT3YIScIncy z&ZVWNXL<jO_2&0)aT0Ey{!MNcN4q-T*Av$^8R=}b2@4a8<`Yp%<c&YMpZ`ok^Y-L7 z3cvOR?^N#f{JN2C?~56Y91T5<3-q=%>Vi<@gNS0S?N^z!yIxkM>~Ol6IZ0LTv7PDF z)BE)cKczh4+w@(<(kir+DgU}g`JBFv+lmb4O78ZnOtrdP8HEJ4iq5Z(vs8Thaf{JM z#s9Mf<u9=|-khQu7F4}rc1fX5q=WDl#gycfTzA&4nmJ!hcG<uGrqXfpQ*+a=Pj~tp zoL66+@cKdTp#^VqAAP&@Y-+FR-`L!x{vB;L*S+ffHa>mY<Y5ve(#QIGO_`HU^*4sb z!go<tTJf!Z74olmOx>G!OShVG6|cF=HFrty&iRKm_^x<e&_Bez&Xwc0z@m4rqGno5 zXe)g9$l+G0cQ~6@D63ND^a;wh4=sNC)V=EW>tFg@+xp6X<r>c3dPK*{`|s1#-lt{m zrT&kPBt#qB$U69>c=3jiRqrLmw@+{Rot4M)WZkQ--tE&wOn6P7b<dNTse3In?6KZg z@o04qcNLM$H5+epY;*~C?`}9C{$u;)ttWNz8DjFd@8q0Wzis1Fg{*1iU7w@^&F&Y} zx?lfyEb7U<;{L6RVk*=oZg{%qSz+?iY@zcSyT7ZZGz(pfWm++B@w@xpC%10)zv)&k z$h9Rz<k_2t$vNF~FYZ`(#3DEL<(DLj6NW50>)8ZWKD7*s+k3z0<Id786+LXVvhz-) zYNfBfb!x*7F)0Vr`w1TL-qIo)4Fcu`d^KLAo4CMY)s_U+#?YWe2UtDsa$kKV<d_ip zl-X{^&BM$!S5K&_%Nu$<_;mNAM&r`$zB>1#bvvbNwiYcpFg0Ap*NuzkYpe7twVKZR zS9AoYguCAL@i`&v=d3s9=x;Uoxso@^y+aON5YWp!yyJr}7w_zJ&(*7fxjJ2VRSyPf zc?!sV^lEVsELasR78o<(?Pu1;tBXXkmuA{}X&r98esG1`<Zcbc_fOk}_E%?IpWMD{ zH^1#pZ;RSr@rDmq|L-akFMqeMHsx;a?dXOBTc1pRqWLTPM5~rBqv7gRiEZXLw$8it z?9!%bv;O><HEq+SXHopJ7w4b6|7fbsJPYT4ENU8!=athZo_VOYokewt%jxwiH#`qm z;JW5|07H~>1=o+)q2fa8gLX_^F1BPM&(jZuQImAsuN2ixn&YUw`ohLk?PWd5aceE) zCSA;v`}uu!;KHjLH{3gU`Olt=^V;Rte}2xguKBn3r?mU2=<6)m)2CY=srPVOxyWGq zg-pS`=*O?}_4xHqmPRC7WLut+U0(YrZNtIHsqyz(ymb^;Dg`Az(U`6#vf}i~Iid1j zk`J<;=4adZ<<S@RneBRNoj-bxK3QR3ew4fX^Yq9ya>-x$_4iwDoLxP!=E(kg8M!~N zd@;?Q`X+Z>^S<zhsXEQNQAf&W?)K^a5q5bw%Ota{k2ue*6_Z<G>px?g#(KGvUzvYj zyK;T~O>=`C^-K2ErTl;J<Hn62M{dmc&{Kc5(ecW>PyI47QZ^fo+1u`|E-Jdd?&Qh4 zU7I-W9C=!hevmVCht`^++l{mS8oFAz@*VjQTKs8w-X!hItBYP9yT4s@w%o~$Q91oH zx^Bm*o34L-K7W#U1s}WMC*5aGN)|GoG9S%2{@7Dy_HWh?8<-t-gzb=b)<3}R_~~Fp z;f90puU9ACE4cH1Zs?lRo^jVJ7HUryUaHE{BN8syq{5{*XPu;@0n^u+C5wv1lssn{ z&6d2cw%KvY+$tIFbNos#TKiHOFE!qfIH4t(qQK4Z!OivRyFCdNE{?tZ1vl<a*Dera zW1r;ewz*>(tHUJ|50Q&EY~*J+K45j0eX!=DkF#gag#`yyoM&5Xd+u>Y^2rY!>7Xw^ z?%Z3k>Uj9nZ#JBp(r0jU&ziG3VfUd~##OG5qW#QGCzf8{_pxsZd*5`8@2}o3f4lRm zs`icvn-|uo8h)RV`@XXO?j^5%_t#ZR+0T!D?^*NZ{^?IGzN$|TX#Rbfw9}q-fiT-i z{l`mL*nU?(o-6w{cdhQlg5>79b!)#ae;5%wL3rsCnOT>AO|hDuR@)uCt}C@HDmN{* z`>eQ+*Y!)&Zacr6kQRG>hInXuy0gu-({A@n=9&GxUcmWUHmf~)wZiOD`#7W12huLq z)RoNQy&EEOf7x-<)B~m*4S)Sx+1F$(Qao{|OY<-j+wS|1t{J@iYZU%^;-9|OwRy|r zd|s~HKl|;XFY4Tp-&;!OmjBvtE5Gsuv%orGIacd6O@%vJ@_K*I>GL+rogpDF{pS74 zWA~(UR;*p8QM{&T-7Hx_S<l+}XHRz?o#WrLOe<-Eo8jd(B06c4ZzgdyrkF^mHrF&B z)R;LVnAOGLL6Ew;ebtP!7RNW=T&a-3ylm>%tjSgn_kG#FKytRPTYB!zlXsS#dBtop zbD`$!1hJfF0acTYcxLD}N#3^cf4ys4&YSdy$^}PTw;k=yd1K>0yJC%j_|f&T71avi z%3-@Mlqs@XOqwjSd9CEJH*<25zs-)mxQzeJw=0dctG=IIQ?YaQ)Q|V4R{mKYANQrI zA}jb*b(2%=Bgei^Ojn%LYXmnNH}PtArCQwN+s(0DU`MdZ#^)@HHgIvC(3%pn?xWr^ z5r@b{+o$OYoQa#tl+dar%O<RII?lEGvG+vb17;r=_$9LD9&dcFmdO9h(Ns(MPOoqD zqk=6t6@S;gnUreReN|EQ+tTi7weqH?U%B)1ufMT*k>!n$!vDe3eys~gK0fQ)uR7Pw zXOf=1eYq&r*w`oE>yPzU#yCyRIF;#+YQIlwehWAGt^H)fxqr52{~Z?jojvQX>fTtc zjBoN2m>Xgk_Ol7Z2T4SH_RPHV^ptBH=U%RMv8f`Z!c00AR~;-9n7Q)>K2CNkPw?Zq z?#X4?V|I{d^;FaONoo?Yp|clM%2<hog$hhzbZ9=H%$A}Py4ris0dJ=lH3pN|m~~Ex zZBbAu$$GZxzgp<F4+<CM3oc*YDZ8cc*Ymk0udjq${__2P{yMEa?R#AIRd+5uva0jR zgvp)iF|1cy<JZ2`XKp?Dv1gH(sn7b}=Z|^TZeF}}=gN({R_)nxK>3q_Puo`87Y~&V zeav+c*uBP5#^ZDcW5u22qLqH$>7K&<Uhm#b6I5n3crf!|g1LtDaZ7QQmuFaB{#*ZF zyYPqPTxQQ_4pN*RE=)qF+IU+sPTY#F`MPKVr-Z;_tzzL_e}7EvZn|oAWh<LN$bNnM zk7^%TCOAwGNoi(G@4e-EfcdszlJl~APcJ&0U;N?dr@1*U?<E&*e-WeXeed68_iJ;* zf1lQS-O~H_*&5r;;!ig`y|A2f&OCono%Ed7afZvvx1Bj-z^auhvo`Sfb%~Ts`{yLp zyKdg^R8{)zy2)?vGj^YO89s5cE|2TXuKhSaLh32cy=9yC^<0~2fAU&#$J(g)hZla$ z%CYS_vFfX;mh=>l?)O^1zK9mNt=v3?^}x=zW~*7NXD@SnD$J4Q<$b_$){SPB#PvQ` zb*AlSa#Nfh#eaI)>#OXWrY_xa{r%Nf9;?>)mTa@(3M$g+;Y+wzC1q?X99X=%E9;(C zsPEacB3GT_m&m!EcbIs2#=o=TKmWTVHXkW|vg?R$iY}LQfmqdSrIpK0tiJf$S!3<A z4e$L6R;)YrZo!2`dlYA1F@NISQ0D6GvNK$9Yx^Ia#ebiQ{MMfJSM-znT-lG#JoZQb z%}T51eCzO_s$%BzA72yPmPRgiI8&!QwP(pKk0iH4d*@Zm%DFwK!s6qBG^3dnS;jXV zt`*n&+sv&<;l4SoqJVp*iu5*>c`C0PCr9sy4s)*L)KXy3JtDc??Qp5-(xY<aeUVSL z_8g30e3-f7WO%3=$DXSWjt{&f-x>soEagpVnE$nM-ptj-Q@Ra$ey&PB-+eBAE|dNS zN3YduQfiLv7K`csd1LFZiVHJ-Ov!6?T<f)OVt$!Jw`StQO230kUtH3Vek2%gdh+th z?J@s4uTHMaRb6ej_xlW`HH8nflNYc1^y%dD@^`h1(|63jcen67i&sv;|2HpZ+$;a! zICqhE@P`yn#fRG`e)_JhB%OOUT(q)oyY9!^&;8~^AG;g$+<jy3>-|<Hr8Nvs&C=2& z?(e_!I_k>39|DM^GVj{W_{(qf4Xq!`gcn$a%n7@oefq{i4!I+L`xiCEwAZ9f4*G7d zwdG6o-VArPO3D0fO;t*3D)w6aw(`%kd9ftTfYEmHjWg->mhL%iH-ziO{M7%=)H8Q} zki5z2f8tElLXNnjJhPVWo%=R4Zch3^0m(^|{C#5u73cVU>U=&q=~rKh***u8R0-F^ z`#U%yvpJetL%qX4uJK>ndbE=3m2l!=E6rbXbu)WUsLyc-T2<)uODtE)R5hC8<ILo+ zO5Yono@+0=zMMJtdv5lZ<L!_Cy_nT~-6dMxf6cvL8>ALVN%vhl{pyVF<J-rgnY_bw zc}2G5ym+@eYwQ2Nf6eP0D#DK+4R5o)_O-3);ip#~3knmCuL`}shWE^}^~YC7u^lSS zP2PL)>fN)l%?Cft^KiZU<Ll#KyLZQT&)d69{P(&;3v_LqYcDMSa(C$m<`XIQbCT@W z-rPSUseX0Z|DvwHGt%x)PWrzv?Z42U0K1|L@m!mKvdSAet);&&eD>v6=SAP-*<pO& zzn7Mu`dQ=4%NWuhax-B1q@~}U{Ha~Be6G|IBmU4EmAsQQB##74$}?&2P+K&q=ax>$ zBoQT7ZP!oIC7VCDOtiF+^i2L(FFH^9<BfTrf6PA0EGuKRC2ix@&TrRPlZCro`S)IE z(|MJ=HE~~CN&%-1%O0+IDGYO@Uq{=Y*SAzPFZjr6mXP*mW7<6P$AQ_*{i157k<~`U z%4%7su5kzmn?0B+%`B`w>)icmkzbd;pRrz7oAH{=Bgv=7dTvS?e`YdUw0FVexDbI4 z4g2=Z&E$RR(KhF^=@jN8Q=C6sJ`|m_de)hqJ(WF^u7}(DhiGvv5#aGmb(-Sx&oA-a z(|q~8x#_$2Cj2tnm-zJEynnuP?RH<)Gz;^I*`;3e;_}8town|`et+*jn>g?PgMEw3 zLrwNi_#v?AoPXbzxLs@dY8DlH*ROS1cFIbvLb2}b?(f&8#grr^*Pfgtw0NO#X5Dw$ zH&OS49?0G>zEW5aY%On9vv>Qs)drW}@5uOj?&W-rd(#%~>NNG97Qb`OA5PVSRqLXf zjek7tH0GN9Q{ZOjMwi;X69bYP_C9x!yI;~}&mR7Y*|lZ4hUWdg)n17W9+F|Nyw<ek z9ek&y%=V_V<<X}rdYK|yw4W`Qn3KWQD%G@P0h6NCGBLINZ+sJkCJ3zbVn6p-sw(GW z#ILD4RTA5;|MRj5kucVsxATqQyNr3ei`2?rgq;gb-u^Zt_~vD4<4J2~%O<V(dvk5y znbpr$pDl`eWOr<xoWFX!xl~o#H0fI<+|Oo89(%ENk9Yf(@;C8}MmPAE-|)}=roZe) zf95yaGj^Y?7<P&l798VL(y?t{`u?zhz}&9}QR(~Cqh||7Z>{@So$<FbXRT|>)m_i$ z<TQvH*FR)^7}jw>;F*lM`{US)3!g1)`=vUy=}CC#v)fHvsc|YX6JBrW6w<twZn(_z zn%gt|Gk?x*-uhty8=si~4_k)9)i=(XACDLmpLL6PwAb#fqn^ssMZ&3J@iXT<WIccG zz_+mU#s{}PHBMZ<r)|$#+39>C<+8s&T+BTmXMUyhLuc&d_iqbjcRf*_&CcIrQt<7> zho!NHe}{#bueo{lx|e0ryxbF^nW9}bX4%vCDp&Ys_ub5mzg6;<S$^-QE9d27uUEgR zt=6x6_3P<tHNQHox};aKXTxm@r`9jotz}ZUB>zcUY2!@Esqqr&9?CgwE*kf2kG)ua zXL;b(@;wtT)>#~_i`raoc(m`wVjlaWbv~QlJ6abQaL#|C{Zm@>T%7$EgIKmF9-*ZR zAJ^47?fA6A$*hmXv6scK{EzjebLxg}Q*%zQz3{v({K>NUm*>w_@Ly*6ZTay_JU^r# z8OuLl{$sR)!QQ3Q{vFSi>+Z~fXR;6PNM5|;X-~O`@I1Fep)<M<&gRsslyy;1ojL9H ziiic#`I~S2;67N;ye;&8Ssl;YrL&tF5|`F?uUgYT;qtWivlh?o?Z3qocY0~?_4h}f zD*iYZwsOyEPq}jUbbTX}bvu??PP-nr>C3r$j4D|%wTqVe$L-9Y^DE2x=YdVD!(?J& zos$l;hh29OTFt(rxpux?&zat-PfvSu?Yr=QXZFN>7oYE4al-iTJ@@UGLUqqhE?1l8 zqVAu&C;#-9_U0S$qRZ5l-d`U+zkR=>+V6)VzcbJNJ1X+q@a(?!o08t$O`AQB{<L(M z<Q()vXU75Wb^Xtu?qu}pN~yG1pHsNhkh_iRlcnE-;)(i(ySsYkxd$C8uUIyVJH6$~ z^*P%oE#qIjwq<MBWeLU2*RGV9HQmh0>X}e1k{K1e%3$en*U6q%&z4WBiU0n1_hg$% zv-_8S_VD_7Iel7om&xC|odsI`ORjPEW$sPj|JI~Zy-_Cf%)z#tH%G6Td`ptP8a(m5 zf5@8WnrUwyc6D9NZp&JB(cAf>6GO<$spq=_SC*H}zA)#@{FZJGM&pMXx}lc)r?4;` zyP7abL(Nc%bMd=t61+3_cz>@6Wz>Gu$zsHJOnT|z2O44L%NB_7H&1tTJ2*KdY)*85 zSgqrnj<hD0W=H3iR_0AC7hD;e`1pH{g;?x4?Oj@+^e#=URg~dB>-=@Ryr-rywFW<V z%I$9WX~vW%zG|DB{SO~vR(^b;#ZUXj{=ifxp9|urr$q+p`QE+}V7s@CZQ1#Y-+gTD z=2;Z~&Yyp8%gdMd@71XpzU!2)OuN1=z3<x>nWK*<?%1~U*Ym~O<G$Tovg~8$>i;&f zc6IOOU0wa&vi#4Fox8)=?)oiLU~yW2U-R`03!cj-e@wbJFF?5QVB@@p_a1JWwyZ)U zE<QYRUFHoI&kL(k*O>}TUTESUxg{&fL``8%=Yw1KJZ3&?%?Ps*5$jy`Bdee%d!K>G z_cdzgKK#)*l5wT+z@7=mQqG@b&}5cm$X_3HJ%Q;B`{uVx95WxzUwuzTMEJ_Z=0}BQ zJS8<BH#n%Wd8y2seuLeU^|&%y%eu1+*=h$S2s%$GJi2B_!$Fn|FF_`wwC^?<eeF|B zUg$OX1m8-YVE#JiM|Ridg~iS`4@#mBb*+DRVtMk}`L(Z~JZ^vVQQ80Pj;FyFFC1S! zzv}nNQ~P57Ht+tWeg9PM^ts%n#dEo5wOnXau3qnXDKx(J>ATkxH&s1enJcKW_TPbU zOUGh$jW5aNmprfhaFO+}G&-^(Jye7vV5+<!*OXbMS32KliyiWpFrE{wkoob&rn&yh zxXgp3liRs3pHzN#M_TsV>?1a(H_F?dyq~%E-oqVn213m|+uQf|>{0w9pnT4-o&V~s zEi(Ha=~&iSicdK_UEJ^3%=eo8yjQR2Wozo^M}|cki)hc!$>TYa)OalIVDxoyEv-qV zOxeGiTwM?DyY*|@v|kR~&k`(tmS5yO6unU3+{1m%$KP&PeI~YbNkAmCi9&gkM??CR z#?W20Y{vgyp0ST!da;U=#a_^M{q(4<Oy$Yn9(`jHU`)TU@J&oYlx$^`h@V*a=6%7d zKT2vI*|2!|=8jC2q$g)4WffIACj0kp-)#9pa#8rnO=ed=J$F+K2*1B$*U~jhHl@@S zlxoXuoNwz<yu<%OaQCUVH50Vw|1z{K-5eGF<66>d?uGTW-fQn4`#Uv+d%d&WyK`L* zXa3z`YqF8k5sdlB{%BR)t@xlViN0qPX1rPY^IA-Q$dSLMJ4|<c?EL3-P$|lH-LGZO zE`73_e{)iPkW$SK`H74VpYw>jNKQ9+G&ifIUvuJ4uQ<kyQ$_M3*0PJ$$=wm!qro{x zI-v3F&$J`Hf9CNVSZ#G@RqLaDvqZ%-;@GVg#<Z)QY1hyeXiVEHHjg!K(Ruwpv#zG^ zoguPb)wbvQq`$eWsS20YTrWO2z1e2V2k!X!UoQpjQT}4EXV$mtp?|0UFI}(qub-Le zexrP`{Ih*bJ1;CuZQ|X3eDMc^EH2wHpT;w@<fmm;{PFw!q*GMeKQBN3eqGViF5$^< zOLQu?1=&sB&&qf0w4mVY2j9K!m0H`h?3vE{_2ipuUH20wgHK6`Saek{y``k~R{HxI zt$NP7!~bqfF>;W2*(2YuY5t++0~;>{_QnMN|7^l_E#emc8msd^e|<R+5a+So$A5YB zI-g>3(YeoMI5ve(5TCf0X@2=DrN|lD_qGKDaWORQ^nW5g<9EvpzpmuVcU06rbx+x% z7B$JTx%12y#)7zqCK3zH^%m7>`QD4{){AMl8@wgc>zNN@^yyupdCptKJl1yC_N@v$ z&7zea_w+lr|3QVFx^Y$(0@F1eqLUXc*e~|;OBdItJDaL+Hwl|$f69+YRGHE()Es!+ zJ$(w#q>BQFT2`qnoZH%wrX=aAIXCTmg2BV|$tN=}shvN)Ir&xX@yH9NF56e$S@7-o zMs8=5nG@RFosTh}E7gzX&^kAn<%H+cCz~50H_f=Hc4*Jq8IucRV`EM|Yu7L=v(qjV z7v}%nGjEb!oI6*@+upWX!J5ex+V9J9_q|n~e*f3XM!)8}uU5ReWmR)M{q)N18ukZ; zolfk0^ykRDN9XTcwYl|BUw?yyPTRxA_j9a%Y@96o?RLIu%bW*!JO9029_F*|z45aj zQZ?~Eea=KaT2*-4J6E@FQhx2}*tpPl)0Q;K{tmle`)-=-wxHdsueIvvln8Iypp*8k zVT)F9|LJ=Pp^cM&@A{CrLLvM}tb%wGmubGfxT%Hdy}u2I`-1%raaRfYxlSw#YY7%w z`Aj^JL!e};#UY8AK`9o+TeNiE<v$F~S#xUorBx5gndHyLOwQt5dC@X7h}DKwaptU3 zO)EbJG}h!~*#^!C{V?T;aa@2!z>f`aCxQh+6#d%{ZftnN<25g8|KXKYoCjkvL(gu0 z`+%FhX0^A=t5c@WZ{MqK^V_Fyxb9WuwwON$?&(c_`ADYoPOX2%s-D|MUo$@S?Nh5^ zICUb@b^dCe8-Gilo;tF-!hdRQh<w))t=Yeq8fb5yy0~DOmX(yxif%2Rw@DLkaYnF; zSs%}Gyyg|ONqB?TkEJIV+oG0TzrOvj$iC-c7pI23{5i$TEbL|B9plp%_@*5^6uRrz z=FF3VVa}@2-(D^DFy&cm$ksA<!E^!6)r|eIIw=#*Pk3cxBF@ed;3*ZX$;vCesie8= z(Fdcw;&Yc|r(AE|eaw&Fci;M-U9)E2cjW18xy3x|`1{+pXMbML>m)1se(%<wYES>2 zuZcIAsNYe~t~}+4?~EBNZb?s`1RnI6Jo8yymog(?@{SGbt5)o}Q~K`hyBhaLt{>l9 zu3PUtO}z1xpmXJy4L@cXeUF@e%<^;m!I|3`PVLE8Fr2qrO7p|2yB<^A=S@BN;ZsVG zO}n$&(bJPpempY!^Qw<)tn@vXOfTbOIoG9DHND35Ooc1Mlf|A7FM2FIb3k<A0SV(B z279$XPFj57@3vyKcH{Gk|Nh$RwymG7!^XG%{rl7{iyvtxw7=WIxOmRY_(n_qB|PVt zH5Y$&SS<NDwEIlG0Qcp^>N-b{&F0<Hxc_EEj^)SW-#gSi@0|IpbXUh~$4Q>{*8EA~ zOB)`!axAY96nmg%(Len`LFA6};dkYAb$h>s|NeC-ReDk!)5nO?{KYzKH>2NnKXx&A z_j&uaUo)pK2r#)&`sn)tj(d|E?@O;g<N9B{mf^EN=j7*mLQ|sSV#9mQ@5!B9zeZ`Y zTz&ag^Lt`~`773b;&=7tI?eUh`_E~mrJvY;{68Wt@5aCYzJ-TTYQe-_DUNu4o39L# z>=zgq7#L1Xjw=Au_w`!;yZoEXv6Eewfq{XOfe9oEK64y&R{Q;L`aO+}lV9#XU7y}J z*Fi9(fSaMCiOrs&;#Te?<_q^6BwEbdYvOZ3m(Xkjk=rG&NL<-wy&`w_%X?GpjXs~5 z7oQVim2&X?c}Y$Ve)ANkGvSXXPA@45Uc5)HYt}i<GvZ$jr-e*8d2vql+PPmdw8|pZ z>d*V}en;4hISOw*(<j~2QjGO1HoE<@<sqA~TJcHlnG%|J&#hR!T6s|)m%q7-b1nP4 zSsqr!`}#k-e{hM8neaC-;`$NBr4Lv)75Bf+OEd{{FF3G*uSnmNYt>PYHQN&{@AI(P zDXc0J47GI(xa$;q%`kM&hxvihM*o5&r61hRe9FG>vdHJBQ6WpFg?|hTT;t;Oli4z~ z_Ke5@kLR=NdtQIGjC);n#AluGytRH$WoL>Rr)G<Y`cIhonpspJxZXMBCs(KFitfN^ zg~pTFm)Jf2{_k>ipZcUbDJC1!pKyOk$qS5(&fF;3`!uLJZQIMevDw`>zN}gE(dCT0 zZ_w=QcAuPB0ZCUco@X^C*ME2GYn=aG{Iz7u)-x`d-|RNOtgc}3`qkefeysh!VD8`G znqtMTs+Et^T+Z3swX{|8nr7|!A^)1yHkf_G@h`SEcb<hjcKtJf<HvkfgZ`?<J7K1Q zKiBgwOpIS~^yC2nhQPJyA&*lFtRBlcR$gPhH1Wujokzb^82<m*v}B=UjI*QD*8k@$ z-!9zuqQ5gmuR3s6w&uy4tJ0iTvs9kkQvEL)_Urrwan5=ssil{$K$6T&1~6pwVLZfi znE45-0GlFPBHI+U<Ln;nv)O-gNOB}`^l@@<PUN!Wmgio}lf?6rH;_-9ZwtRA|5g5f z0@?yWf*eBHLR*D{h5w6Wid+)a6@4QXBQ{Z-PkgQTdx;*2f09*_FQqn1$H)lD6wADl z^^(0Umne5qUPV4qzEys^0=t5yLWH8Tl8(}A<wGjHs=jJ<>N4s}HIg-zG}E<=v@UAL z=mhGz>)zFyq@QabU{GKvZaCkF!zkJ4wlSM=z42M&vnCFviDv$0Z_PKFzq8nCscscv ztzeyPy~O&6Eu*cXZHw(IyIgx2dmjf4hglA19b+6%IcYoPJ5P7{?6%rH**)LA+P&R< zvip4Z)gD=1!d~)T+Fs^f&R%|AkzOfY`Q8%VD&7X(Hr^iIA>Ikztv=a4<vz_m{XVmO zmisFB{`6z@6ZDhy)ATd-a}1aqxH@oq;NigYfwu#n1%3?t6T}|0E@)TKv7k#q_kvyp zeG85VaSHJZi3&*zDGI3zEe&l9-5o9)u{)AEGA8nU6n~U{RCH8()aGc$=z!??(RX9& zV^+poh-Hh@i}Q;&PcTo2PB@V8JW(w%Kk*Z|oVd@>#K6vw#K6M9#Gu5W%V5dCz%YUB zDFYJ&W5un~39ex*jsh<B?{W&>A7?8M+i>`9YIpDN5`Mnp4?6BJb!u`+XRhq&=?YmO zWuH1_%5?`%<A@lit$Xxiyplb)A5E2+$`c*qSABBxQPDS3_e8{K{=53fEzSF0q>AGt z>q{*h0!I!fPVjC^Sw1tx>;DYb;758(N<Wmmo2YQgvT)szoQaA`=by&in{-aVZe7n_ zkz(c7f+AJ|?_c>B=Zf%YpAs>jT0TWl?b^cmx{3WQ7b?v5pL{&|+}1$J;K<6BsW)9@ zH|Z@staozFH~)8ccorx5`E1d)OWYFNGy8s+*<7|0Gc+&nH2y!^qWqraocTI_e)HbU zy_+8B`|zCF%+$DhD{n4HHz{<>30I8Se7|MxUC~n>3uo59>UnyFv+L`U`N>Y}PcPju zp~k=B&XxXCYDd^g_Mhg8p3k=+J-Xsn&cWtJW<~~v4S)863dr5hM4oPwT^hTflVQcP zsaJy+Dhgy=SrXYDmV2x8#g(Pe{c*LwcqL3yZgd1DAM>4Il6tErviO;9#FmtsU7^|6 za&K%&z1<gE{Y}=Q<jIZB;Pi8`J4&A3>WwacXIt^*$<6NY{Cl-OzC69%A7B5kwZ+xt z=n++wrAty%vRMuu4$@#?U^sBL{%6kZIXTx=Z*R->duF-&+}mA`*Hyn`XVDZ>3|g;K zy-1~V`syH0hYc394=vqYpc%ML;&9pA)4@+~O_g@d@|6mI-q;>wWAt_-XYjMLGfdO( z3ww*{tonXsX2|MkYb%~z*chR2^Ziox`aOXU)_%YD^TFctc0V7jX1@RD!(IRVe=jY( z5MTf2^Zkj!vDZv8w&WdeQ#*TWi}b}sS65_Q{q^NFCr4q{fdq#Phlm4jEhH=@cS`Jx z{Nq$xbHn?y5igGn?}9Dd-PxIecHvclNs+7KijoSo^1afs0<#L!3}+mlIwK~EY2pL_ zb2?snXMIn5u4VXa*=_Xsl46&aQE`w$7sKlt%^P=eFmNy=D{Np?c2U^CtX!}`#8p9I zgGNG{LRV<S1}4?S4UDeI%1IlTlwCG+axpV$Z{`*Nk%D5(Oxlr~STxvNgIqRq3$QS0 GGXMa2OV+yp literal 0 HcmV?d00001 diff --git a/docs/fonts/lato-v14-latin-regular.woff2 b/docs/fonts/lato-v14-latin-regular.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..b14c76cab32daeac6287bfad409cfd73c63504d5 GIT binary patch literal 23316 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 literal 0 HcmV?d00001 diff --git a/docs/icomoon-selection.json b/docs/icomoon-selection.json new file mode 100644 index 0000000..2696104 --- /dev/null +++ b/docs/icomoon-selection.json @@ -0,0 +1,96 @@ +{ + "IcoMoonType": "selection", + "icons": [ + { + "icon": { + "paths": [ + "M207.429 877.714l52-52-134.286-134.286-52 52v61.143h73.143v73.143h61.143zM506.286 347.429c0-7.429-5.143-12.571-12.571-12.571-3.429 0-6.857 1.143-9.714 4l-309.714 309.714c-2.857 2.857-4 6.286-4 9.714 0 7.429 5.143 12.571 12.571 12.571 3.429 0 6.857-1.143 9.714-4l309.714-309.714c2.857-2.857 4-6.286 4-9.714zM475.429 237.714l237.714 237.714-475.429 475.429h-237.714v-237.714zM865.714 292.571c0 19.429-8 38.286-21.143 51.429l-94.857 94.857-237.714-237.714 94.857-94.286c13.143-13.714 32-21.714 51.429-21.714s38.286 8 52 21.714l134.286 133.714c13.143 13.714 21.143 32.571 21.143 52z" + ], + "width": 865.7188571428571, + "attrs": [], + "isMulticolor": false, + "isMulticolor2": false, + "tags": [ + "pencil" + ], + "defaultCode": 61504, + "grid": 14 + }, + "attrs": [], + "properties": { + "name": "pencil", + "id": 64, + "order": 3, + "prevSize": 28, + "code": 61504 + }, + "setIdx": 0, + "setId": 0, + "iconIdx": 64 + }, + { + "icon": { + "paths": [ + "M832 694.857c0-14.857-5.714-28.571-16-38.857l-118.857-118.857c-10.286-10.286-24.571-16-38.857-16-16.571 0-29.714 6.286-41.143 18.286 18.857 18.857 41.143 34.857 41.143 64 0 30.286-24.571 54.857-54.857 54.857-29.143 0-45.143-22.286-64-41.143-12 11.429-18.857 24.571-18.857 41.714 0 14.286 5.714 28.571 16 38.857l117.714 118.286c10.286 10.286 24.571 15.429 38.857 15.429s28.571-5.143 38.857-14.857l84-83.429c10.286-10.286 16-24 16-38.286zM430.286 292c0-14.286-5.714-28.571-16-38.857l-117.714-118.286c-10.286-10.286-24.571-16-38.857-16s-28.571 5.714-38.857 15.429l-84 83.429c-10.286 10.286-16 24-16 38.286 0 14.857 5.714 28.571 16 38.857l118.857 118.857c10.286 10.286 24.571 15.429 38.857 15.429 16.571 0 29.714-5.714 41.143-17.714-18.857-18.857-41.143-34.857-41.143-64 0-30.286 24.571-54.857 54.857-54.857 29.143 0 45.143 22.286 64 41.143 12-11.429 18.857-24.571 18.857-41.714zM941.714 694.857c0 43.429-17.714 85.714-48.571 116l-84 83.429c-30.857 30.857-72.571 47.429-116 47.429-44 0-85.714-17.143-116.571-48.571l-117.714-118.286c-30.857-30.857-47.429-72.571-47.429-116 0-45.143 18.286-88 50.286-119.429l-50.286-50.286c-31.429 32-73.714 50.286-118.857 50.286-43.429 0-85.714-17.143-116.571-48l-118.857-118.857c-31.429-31.429-48-72.571-48-116.571 0-43.429 17.714-85.714 48.571-116l84-83.429c30.857-30.857 72.571-47.429 116-47.429 44 0 85.714 17.143 116.571 48.571l117.714 118.286c30.857 30.857 47.429 72.571 47.429 116 0 45.143-18.286 88-50.286 119.429l50.286 50.286c31.429-32 73.714-50.286 118.857-50.286 43.429 0 85.714 17.143 116.571 48l118.857 118.857c31.429 31.429 48 72.571 48 116.571z" + ], + "width": 950.8571428571428, + "attrs": [], + "isMulticolor": false, + "isMulticolor2": false, + "tags": [ + "chain", + "link" + ], + "defaultCode": 61633, + "grid": 14 + }, + "attrs": [], + "properties": { + "name": "chain, link", + "id": 170, + "order": 2, + "prevSize": 28, + "code": 61633 + }, + "setIdx": 0, + "setId": 0, + "iconIdx": 170 + } + ], + "height": 1024, + "metadata": { + "name": "FontAwesome" + }, + "preferences": { + "showGlyphs": true, + "showCodes": true, + "showQuickUse": true, + "showQuickUse2": true, + "showSVGs": true, + "fontPref": { + "prefix": "fa-", + "metadata": { + "fontFamily": "FontAwesome", + "majorVersion": 1, + "minorVersion": 0 + }, + "metrics": { + "emSize": 1024, + "baseline": 6.25, + "whitespace": 50 + }, + "embed": false, + "showSelector": true, + "selector": "class", + "classSelector": ".fa" + }, + "imagePref": { + "prefix": "icon-", + "png": true, + "useClassSelector": true, + "color": 0, + "bgColor": 16777215 + }, + "historySize": 100 + } +} \ No newline at end of file diff --git a/docs/img/article-footer.png b/docs/img/article-footer.png new file mode 100644 index 0000000000000000000000000000000000000000..eeb74411cd0b8dcc59d3684192ea7dd45e345d20 GIT binary patch literal 1581 zcmeAS@N?(olHy`uVBq!ia0y~yVA{jLz!1Q}%)r2KrZ#LL0|UeS0G|+725N)<R0WTD z&qOdVFqoDE`2{mDGBLBTvaxe;a&hzU^6?7@3JHsdiit}|O5v87<G%kQ0|P6Er;B4q z1>>9BjVnJ}@vvQ3Zn^7!#$4xohCsgQ{El@K876r6z5ONE^7zZO`v0E-&%al&s@mWD z{rvu_E1&Iu&G}IG(Jnh+YyIQR5?@Qtl$I%My;1zu`)<bl<1*J<6GJZONO`T@#VhsA z&h37^Tz>Ao@H3XLuN=DQq1gUr-r;?{+ZRROJ;HfhTc+~u+Q`ovgfAZWt#mJH_htb% zjizYtsn<@XEnz-wc-*dOyIJ1mjgp1UUfu<7*YGS5&Ma`ZaJc#XqnlZb&FNWli!R2k zIbgf~%yw2;M)Ak&66=oF7~Nn?IUU0-za>#(j)O$f?eDv8OL_->oBT1XPF$!yfotQ_ zvUe);U5YYKzm~kHYy8kjX7Zc#@O!873np$hp3isK_*q<<!nD^>R;}lr>?yq0u!5OS zUwNInUaqLtdZsjvvWb%E8y~o>2u-S2Nn_KT{?PXz`v)l&kBt@QvX9npdF-^KX}Qr1 znMT3+oF`Is%@b7l9=JvuYX}}&wQa$bY3}_8V-{W6wd7mBiPS@vhPCf4?iF3>kGE%K zS`#2XP4cnM3{QK9oA3IyO(Z{_yk2+q`_BsP%r~ieA9^|__Nkd3J)d#Re8X!~hw}-{ zU*~geoX+2PdSBc1)be?M%(&;r#Z=V$-e<4Z&DZj&WW4Vduk5+E=iTGK3l4H5#CEY< z+jwwg_2(CVa&@oYPU86S^^i|Ru!V(dzFh2|NAGuUKL0ziI)!`QgP3(|?dx10o)Q%( z`fH-0pL2QE&%5=VyLrX)6t7Md4>`T=Xw_?mz&Rfhb#C0QUH9Pcsnves49!cu&A1Be zieu;Q5Bq%UYC$REPIfJQ7WNuJjf8DJ+20I4dA*)=V!?gO9opAp(%(Jy{SvlI{h|8d zr}tgWoG!FKIP<J*`l@%ed;SRK1=YsZU)*o3Fu`WeLLv4NcaD$B6>rU-NAEL8nRQiP z;?d$#*7XPVnAb7wxlk>*e)6;I{pa-mWKSqPbKsocTf4izEAH<6cxzMXu9HDGS))04 zc2xCl+L~g~SYC3!{{Hu`r3<!Qa5w6IYY-u+x5CKM*<<bj=^wg0yz?R(=6;AuOnlaG z&Hj^VZhV$X<;}f4*T3>yI~{)M%x(8CjQ{m}Sc2E>^0;$gws?Gsz|mhOdk*M?J`gSJ z2sz|=`fjCPwO!nAUY6-faRR@y(vLFmcI?oYU8wNg;@*LLOZJct(todPyj1xn%Q}r= z+1)Gh`~SPW{aG>nmHr~0Df*WkCmI@QEVyW@p<OPYpRi+stInM?=Zy423p5K}osWom z9(P>2=j0`y8$E*It34M~9p50?o)^DoenE<#Q)%43@9`TYqu#|{tVw@!^!3G=28)-y zeZrKi9<(6V@#x-O@uvL~WPZ3FYnprG#`99{4Xy$!zx;f|yj}f+-WtU@`tGvv^ESJg zt+>{qf93Y;inrID&vNGHG>3W|KAT#$@Rsx2*ge0S?rN#;vrXPRA>@`X&v(0fF$L?+ zoNVn}dBj7%b3Sj}9>WLuT{qU=d6GHR{QDa>%Ui!IcqMiV{48IwC9rD$&79^pciwi3 zpYHgZAIv{z`MK!b54*qL?9k5N^NUmVlilKPN4wu&|C;ab{r+Ck#`)s!_dWRg>6gv& z9}R!Aem~m!V7;pRpL^a>;yd<L^xkNDZ*6vC`R-cn3(p_ynepm%*}sW#`|VyaTx)oz zx<^0le8#&^roSK6_P=j7-}5hjIiClpG-vr+f49%U+~twz$8Q@!0!WzgYei#Z`JSjR OATdu@KbLh*2~7ZzQu%uT literal 0 HcmV?d00001 diff --git a/docs/img/footer-arrow.png b/docs/img/footer-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..e06754b38983c0e038d755777c4f4feef4a78894 GIT binary patch literal 840 zcmeAS@N?(olHy`uVBq!ia0y~yVDMyMVA#dM%)r2Kb@hZu1_lP>0G|+71_p-zXKG18 zUXSd%7#J7?N`m}?85|k}_P@WMV4$$RVE+03fB*&22Mi2MTRdGHLn`9l&WgUYOo7Km zmE}X*yua(qR_*)LI`jXc8~lD#bsmVA%uPsYQ{`JET04E=E3woSR;vOuMFq2ba;&as z39WVzT_|=!>2A1(QpnVWNlX)^-57YYE-p-KP?)H-cv7uPn^A*G;cn&~O(v=g!3)Y5 zntWEtq^>KPxjG_cozwGGRwrUot-^aZu>_ZL9t+tip?33UOnJR)QT?uv)P@(Ql~23y zCtYmz`C@azMc-(7`dne3$1F2#Pw!r-?WJq9^pisHsuNRI?Rvqz>BVeQ(_WoQzg<1= zjLP3dDeq`W@xM}YjqSdkqiVv`31$i}S3QYMTNznqvuGXXha-<|-+r5<^DQj$&D%{^ zgi7D<I$g6NqgEir&LEQSn{Y$8#>~Y&-}G2wUvS3HcwX`O=cM*_&v%>F%U@O%d;8Rt zHS<#Jymc`j+SRP5Puz5l&B@tTM>-?^yleW|>reixW?pYS?QIpW>BH~ErvFlPugc%z ze}DdnEbcMsUv;@xWU61=T&1ZW!$VU4E-E@%-~8u_@7n$Qo;4P&JHP#plT@&sb>@cc z8BsxNrfe3ComHn5lXmInrS1P5HhmTi%rp;-zEqdWrO15Q+%wfHylM{XhkOswPe;`p ze;C>3I=ozc<QA6(!_QA&yndR$N!qt*>9yXs|I7~jzSW<*p=DmryvD7WYh=<tuX~r3 zqr0!v)=1bP(~4iYHg`VPnclft?O(0?%U9IQxn5;${M+%BjC5xHdWM$;5=(!l2mQOz z`SZr2`x?g{wWpjt;h%rH{I3AhftvMd-}@ibEibbaDf+N`vAT6YJ4;#lClw|hhaI+$ zC-PtVYyaCg;la~y6^j|!%JzSiZs1)L{?4pJ_)G(5RfNZ}nmNg=C-<j%em}{e;5+LT zgO6#@oY1cl4<nly>#pB@yM1ShWa))9tqMx5r2=bTvhV!Q5H0Con|s1!0|O|txca%Q Ib4q9e0Cyda`Tzg` literal 0 HcmV?d00001 diff --git a/docs/img/footer-logo.png b/docs/img/footer-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..e64790b38e2f7d55a5b42154261506cdfb3c5422 GIT binary patch literal 3688 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 NK~Gmdmvv4FO#n>n2O9tY literal 0 HcmV?d00001 diff --git a/docs/img/forestry-logo.svg b/docs/img/forestry-logo.svg new file mode 100644 index 0000000..4b4beda --- /dev/null +++ b/docs/img/forestry-logo.svg @@ -0,0 +1,19 @@ +<svg id="forestry_logo" data-name="Forestry Logotype" xmlns="http://www.w3.org/2000/svg" width="484.1" height="101.9" viewBox="0 0 484.1 101.9"> + <title>Forestry Logotype</title> + <polyline points="2.8 39.5 28.3 14 53.8 39.5" style="fill: none;stroke: #181919;stroke-miterlimit: 10;stroke-width: 8px;fill-rule: evenodd"/> + <polyline points="2.8 62.1 28.3 36.6 53.8 62.1" style="fill: none;stroke: #181919;stroke-miterlimit: 10;stroke-width: 8px;fill-rule: evenodd"/> + <polyline points="101.9 36.6 76.4 62.1 50.9 36.6" style="fill: none;stroke: #181919;stroke-miterlimit: 10;stroke-width: 8px;fill-rule: evenodd"/> + <polyline points="101.9 14 76.4 39.5 50.9 14" style="fill: none;stroke: #181919;stroke-miterlimit: 10;stroke-width: 8px;fill-rule: evenodd"/> + <line x1="28.3" y1="101.9" x2="28.3" style="fill: none;stroke: #181919;stroke-miterlimit: 10;stroke-width: 8px"/> + <line x1="76.4" y1="101.9" x2="76.4" style="fill: none;stroke: #181919;stroke-miterlimit: 10;stroke-width: 8px"/> + <g> + <polygon points="127.4 76.4 136.4 76.4 136.4 55.3 158.9 55.3 158.9 47.1 136.4 47.1 136.4 33.7 160.2 33.7 160.2 25.5 127.4 25.5 127.4 76.4"/> + <path d="M211.6,31.4a23.9,23.9,0,0,0-8.6-5.4,30.2,30.2,0,0,0-10.9-1.8,29.8,29.8,0,0,0-10.8,1.9,24.5,24.5,0,0,0-8.5,5.4,25,25,0,0,0-5.6,8.5,29.5,29.5,0,0,0-2,11.1,28.2,28.2,0,0,0,2,10.9,24.3,24.3,0,0,0,14.1,13.8,29.8,29.8,0,0,0,10.8,1.9,30.3,30.3,0,0,0,10.9-2,25.6,25.6,0,0,0,8.6-5.5,24.9,24.9,0,0,0,5.6-8.4,28.3,28.3,0,0,0,2-10.9,29.2,29.2,0,0,0-2-11.1A24.8,24.8,0,0,0,211.6,31.4Zm-3.1,26.8a17.6,17.6,0,0,1-3.6,6,16.4,16.4,0,0,1-5.5,4,17.5,17.5,0,0,1-7.2,1.4,17.2,17.2,0,0,1-7.2-1.4,16.5,16.5,0,0,1-5.5-4,17.6,17.6,0,0,1-3.6-6,21.9,21.9,0,0,1-1.3-7.5,19.9,19.9,0,0,1,1.3-7.1,17.6,17.6,0,0,1,3.6-5.8,16.4,16.4,0,0,1,5.5-3.9,17.6,17.6,0,0,1,7.2-1.4,17.9,17.9,0,0,1,7.2,1.4,16.3,16.3,0,0,1,5.5,3.9,17.6,17.6,0,0,1,3.6,5.8,19.8,19.8,0,0,1,1.3,7.1A21.8,21.8,0,0,1,208.5,58.2Z" transform="translate(0 0)"/> + <path d="M259.8,49.4a14.2,14.2,0,0,0,3.1-9.3,14.3,14.3,0,0,0-1.6-7.1,12.2,12.2,0,0,0-4.2-4.5,18.2,18.2,0,0,0-6.1-2.3,36,36,0,0,0-7.1-.7H226.2V76.4h9.1V54.9h6.6l11.8,21.6h10.9L251.1,53.9A13.1,13.1,0,0,0,259.8,49.4Zm-13.6-2.5-3.9.2h-7V33.3h7.8l3.6.2a11.2,11.2,0,0,1,3.3.9,5.8,5.8,0,0,1,2.4,2,6.3,6.3,0,0,1,.9,3.6,6.6,6.6,0,0,1-1,3.9,6.1,6.1,0,0,1-2.6,2.1A11.8,11.8,0,0,1,246.2,46.9Z" transform="translate(0 0)"/> + <polygon points="279.9 54.4 303.3 54.4 303.3 46.2 279.9 46.2 279.9 33.7 304.6 33.7 304.6 25.5 270.9 25.5 270.9 76.4 305.9 76.4 305.9 68.2 279.9 68.2 279.9 54.4"/> + <path d="M340,49.8a23.7,23.7,0,0,0-5.8-2.6l-5.8-1.9a17.5,17.5,0,0,1-4.5-2.4,4.7,4.7,0,0,1-1.8-4,5.9,5.9,0,0,1,.7-3,6,6,0,0,1,1.9-2,8.3,8.3,0,0,1,2.7-1.1,12.8,12.8,0,0,1,3.1-.4,13.5,13.5,0,0,1,5.1,1,8.4,8.4,0,0,1,3.8,3.1l6.6-7a18.2,18.2,0,0,0-6.8-4,25.1,25.1,0,0,0-7.8-1.2,25.8,25.8,0,0,0-6.9.9,18.6,18.6,0,0,0-6,2.8,14.9,14.9,0,0,0-4.2,4.8,13.7,13.7,0,0,0-1.6,6.8,12.9,12.9,0,0,0,1.8,7.2,14.1,14.1,0,0,0,4.5,4.3,24.5,24.5,0,0,0,5.8,2.6l5.8,2a15.8,15.8,0,0,1,4.5,2.6,5.3,5.3,0,0,1,1.8,4.3,5.9,5.9,0,0,1-.8,3.1,6.7,6.7,0,0,1-2.1,2.2,9.9,9.9,0,0,1-2.9,1.3,12.2,12.2,0,0,1-8.9-1,11.4,11.4,0,0,1-4.3-3.9L311,70.8a17.6,17.6,0,0,0,7.5,5.3,26.7,26.7,0,0,0,9.1,1.6,25,25,0,0,0,7.1-1,17.5,17.5,0,0,0,5.9-3,14.5,14.5,0,0,0,4.1-5.1,16,16,0,0,0,1.5-7.2,13.1,13.1,0,0,0-1.8-7.3A14.4,14.4,0,0,0,340,49.8Z" transform="translate(0 0)"/> + <polygon points="350.1 33.7 365.7 33.7 365.7 76.4 374.7 76.4 374.7 33.7 390.3 33.7 390.3 25.5 350.1 25.5 350.1 33.7"/> + <path d="M431.9,49.4a14.2,14.2,0,0,0,3.1-9.3,14.4,14.4,0,0,0-1.6-7.1,12.2,12.2,0,0,0-4.2-4.5,18.1,18.1,0,0,0-6.1-2.3,35.9,35.9,0,0,0-7.1-.7H398.3V76.4h9.1V54.9H414l11.8,21.6h10.9L423.2,53.9A13.1,13.1,0,0,0,431.9,49.4Zm-13.6-2.5-3.9.2h-7V33.3h7.8l3.6.2a11.3,11.3,0,0,1,3.3.9,5.8,5.8,0,0,1,2.4,2,6.3,6.3,0,0,1,.9,3.6,6.6,6.6,0,0,1-1,3.9,6.1,6.1,0,0,1-2.6,2.1A11.9,11.9,0,0,1,418.3,46.9Z" transform="translate(0 0)"/> + <polygon points="473.3 25.5 460.6 45.8 448.1 25.5 436.8 25.5 455.9 54.6 455.9 76.4 464.9 76.4 464.9 54.6 484.1 25.5 473.3 25.5"/> + </g> +</svg> diff --git a/docs/img/jekyll-og.png b/docs/img/jekyll-og.png new file mode 100644 index 0000000000000000000000000000000000000000..508c42a28e4f89e18fe1ba4f5564487261fdda4d GIT binary patch literal 68048 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 DhN8P> literal 0 HcmV?d00001 diff --git a/docs/img/jekyll-sticker.jpg b/docs/img/jekyll-sticker.jpg new file mode 100644 index 0000000000000000000000000000000000000000..b51680cdedbc9b0eb66da4192d25ee42c6c7a38f GIT binary patch literal 113785 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 bzIx-Fd)czKdd2h4TzqTxtKQ)L^8Ysh`Ta%w literal 0 HcmV?d00001 diff --git a/docs/img/jekyll-sticker.png b/docs/img/jekyll-sticker.png new file mode 100644 index 0000000000000000000000000000000000000000..09e5cbf5b1221d46d75716036401d1ba11abd593 GIT binary patch literal 15126 zcmeAS@N?(olHy`uVBq!ia0y~yV4TRnz_6EtnSp`9_1T6w3=B+P0(?ST8At?-jEry{ zjEsysckaA<_wI)eAAbG%_51hlfB*jd|NsB*-@jkJe0l%={h2doCQO(BQD0P4^y}BJ zOP4M!TC|9XiHU)M;o-xFfB*jd{{8#o$Bz#jIACI8!oa`~85zmIz~JfWxp3jag9i_O z{rdIGmoFbber#-PWMX1^`t<3#b?X=z8DGA9`St79YgexxIIw@m_U+rYY}vDC_mRVg zFPuAf|Ni}tA3ps1_wVJ)mosP1m^yXJ+_`fXESSG&(V`_wmMmYseATLz`}XdA{rdIa zzkeAR7!DsixOUB&_3PJd+OXlw=~K_1KKuRq_w(n^j~_dB<<h0v+8Qq}PdhtX4-faK zsL0~tq6rfw?Af(z|GvGCA3gf{^XJ{Wcm4hSOiYX|EX*S!!kd~Jj~zMk{rmT)Po9{X zn#9CJpFDp2?3ptcFI)hHysJxld;5zQFD_rc{QUXzo}TWTH*P$5@Zj_3&kPI<U%!6! z@$tTW`}Vy%cX#dFnUj-!`}XaMigG6>$5*djy?giW&6_t23=Eq$ZAwW=`Sj@%0|P^T ze%{-+Z$G?$&%nTN>g35cZ{A$JdUfvHxeN>p+qP|EU|_Jfw~vU3C@d^&YHDI;W{!)C z4Gj(1w{PE}Lx)&cSe`$B?&#<k6cl*s)Tu|09zA^c@cjAnYu2o3ZEdx-w$j(v_x1J3 z$jDGtRlRlVR$^j;pP%o`{z)%m5??M^{?y3gk*vzAnubY}COtWJ{E3I}eOZ;ecke#k zzVmk9#2d0Iw`EmsM#Wu~Re7SK^YrqSEA@?+WtCs#6&+i>=3-{<Sy|=d6DOUuvOg-T zd_q?Ff~@kMB})!BHXlex-eqjQTUKemtkNM_rBkxXTkW0J%P4G;QQRu4xI<QPpRCea zb*<Gh3P(3@K2%n*-pF)?jKZl!i}!>@oSHdnZ$i?}grsd=-Uqht*q@nwt#88o$jG+% zggq*ntLy7;&Yd3<6T`Y@FE0ZF!=I8MzhDMNCT12^Hg*n9ZXP}XArUb_NojG9{%!Z( z|N8&^{OWjP1zF9PP4<fN4WFN{iBeZJQ<4uYcyw#8i{{h<^#%X218u1AHYkma()<7a ze?C~k@CcY%1tPB)|NsBr?EnA&mjC~6X|U~au!>KQaQ^>)(j1>NA^(>g*|D-E_rL4^ z|2{iGszJEy|NsB{K@5_ykJ!5eAqEDstDY{7Ar*{ou3nubVkpq|uyxyxUjJ)%bJ?E% zTi?Bkr96{y+rGu`9)JI-EiLkO{&$Y*KMTL?Hw*|lrqT7O)-`Ye_e!2)jZ&ep*M0f+ zt>TLP6~6ESvjszQ;W<~neG<�g_CuHd;>vg0z;JEK|@tsJdFjb-o4*)9(z6=?#fJ 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 T>^7|i?LPH%^>bP0l+XkKDo|}P literal 0 HcmV?d00001 diff --git a/docs/img/jekylllayoutconcept.png b/docs/img/jekylllayoutconcept.png new file mode 100644 index 0000000000000000000000000000000000000000..e7fa6d7ada681e470757cc9a042ddfc867588f65 GIT binary patch literal 17121 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 A1^@s6 literal 0 HcmV?d00001 diff --git a/docs/img/logo-2x.png b/docs/img/logo-2x.png new file mode 100644 index 0000000000000000000000000000000000000000..57af994c61aed71f4c7ad11685d7baebd1fec373 GIT binary patch literal 45966 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%0AR^92$ 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⁢-1QVZy0wrRG8()70$`pbA0XQBaA;La6d6* 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 KKbLh*2~7YwL{;wq literal 0 HcmV?d00001 diff --git a/docs/img/logo-rss.png b/docs/img/logo-rss.png new file mode 100644 index 0000000000000000000000000000000000000000..8a38383bcfee0a7733fea8bdebadee5420fd6968 GIT binary patch literal 6022 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 literal 0 HcmV?d00001 diff --git a/docs/img/octojekyll.png b/docs/img/octojekyll.png new file mode 100644 index 0000000000000000000000000000000000000000..7c6d96d7ed4be64ab1f067c2d3b98f517ed96759 GIT binary patch literal 22360 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 I=akR{0HDp}od5s; literal 0 HcmV?d00001 diff --git a/docs/img/spacer.gif b/docs/img/spacer.gif new file mode 100644 index 0000000000000000000000000000000000000000..4bcc753a12e9854923af4b9b5b9a4b76f1bc53a6 GIT binary patch literal 42 ocmZ?wbhEHbWMp7uXkY+=|Ns9h{$ybUF?B!$NXCJQ(S^Yp0J?7nHvj+t literal 0 HcmV?d00001 diff --git a/docs/img/twitter-card.png b/docs/img/twitter-card.png new file mode 100644 index 0000000000000000000000000000000000000000..b828c1a9d11250f1fade78ddfb6f9d86327d0ae4 GIT binary patch literal 72287 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 literal 0 HcmV?d00001 diff --git a/docs/js/html5shiv.min.js b/docs/js/html5shiv.min.js new file mode 100644 index 0000000..1a01c94 --- /dev/null +++ b/docs/js/html5shiv.min.js @@ -0,0 +1,4 @@ +/** +* @preserve HTML5 Shiv 3.7.3 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed +*/ +!function(a,b){function c(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x<style>"+b+"</style>",d.insertBefore(c.lastChild,d.firstChild)}function d(){var a=t.elements;return"string"==typeof a?a.split(" "):a}function e(a,b){var c=t.elements;"string"!=typeof c&&(c=c.join(" ")),"string"!=typeof a&&(a=a.join(" ")),t.elements=c+" "+a,j(b)}function f(a){var b=s[a[q]];return b||(b={},r++,a[q]=r,s[r]=b),b}function g(a,c,d){if(c||(c=b),l)return c.createElement(a);d||(d=f(c));var e;return e=d.cache[a]?d.cache[a].cloneNode():p.test(a)?(d.cache[a]=d.createElem(a)).cloneNode():d.createElem(a),!e.canHaveChildren||o.test(a)||e.tagUrn?e:d.frag.appendChild(e)}function h(a,c){if(a||(a=b),l)return a.createDocumentFragment();c=c||f(a);for(var e=c.frag.cloneNode(),g=0,h=d(),i=h.length;i>g;g++)e.createElement(h[g]);return e}function i(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return t.shivMethods?g(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+d().join().replace(/[\w\-:]+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(t,b.frag)}function j(a){a||(a=b);var d=f(a);return!t.shivCSS||k||d.hasCSS||(d.hasCSS=!!c(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),l||i(a,d),a}var k,l,m="3.7.3",n=a.html5||{},o=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,p=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,q="_html5shiv",r=0,s={};!function(){try{var a=b.createElement("a");a.innerHTML="<xyz></xyz>",k="hidden"in a,l=1==a.childNodes.length||function(){b.createElement("a");var a=b.createDocumentFragment();return"undefined"==typeof a.cloneNode||"undefined"==typeof a.createDocumentFragment||"undefined"==typeof a.createElement}()}catch(c){k=!0,l=!0}}();var t={elements:n.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:m,shivCSS:n.shivCSS!==!1,supportsUnknownElements:l,shivMethods:n.shivMethods!==!1,type:"default",shivDocument:j,createElement:g,createDocumentFragment:h,addElements:e};a.html5=t,j(b),"object"==typeof module&&module.exports&&(module.exports=t)}("undefined"!=typeof window?window:this,document); diff --git a/docs/js/respond.min.js b/docs/js/respond.min.js new file mode 100644 index 0000000..80a7b69 --- /dev/null +++ b/docs/js/respond.min.js @@ -0,0 +1,5 @@ +/*! Respond.js v1.4.2: min/max-width media query polyfill * Copyright 2013 Scott Jehl + * Licensed under https://github.com/scottjehl/Respond/blob/master/LICENSE-MIT + * */ + +!function(a){"use strict";a.matchMedia=a.matchMedia||function(a){var b,c=a.documentElement,d=c.firstElementChild||c.firstChild,e=a.createElement("body"),f=a.createElement("div");return f.id="mq-test-1",f.style.cssText="position:absolute;top:-100em",e.style.background="none",e.appendChild(f),function(a){return f.innerHTML='­<style media="'+a+'"> #mq-test-1 { width: 42px; }</style>',c.insertBefore(e,d),b=42===f.offsetWidth,c.removeChild(e),{matches:b,media:a}}}(a.document)}(this),function(a){"use strict";function b(){u(!0)}var c={};a.respond=c,c.update=function(){};var d=[],e=function(){var b=!1;try{b=new a.XMLHttpRequest}catch(c){b=new a.ActiveXObject("Microsoft.XMLHTTP")}return function(){return b}}(),f=function(a,b){var c=e();c&&(c.open("GET",a,!0),c.onreadystatechange=function(){4!==c.readyState||200!==c.status&&304!==c.status||b(c.responseText)},4!==c.readyState&&c.send(null))};if(c.ajax=f,c.queue=d,c.regex={media:/@media[^\{]+\{([^\{\}]*\{[^\}\{]*\})+/gi,keyframes:/@(?:\-(?:o|moz|webkit)\-)?keyframes[^\{]+\{(?:[^\{\}]*\{[^\}\{]*\})+[^\}]*\}/gi,urls:/(url\()['"]?([^\/\)'"][^:\)'"]+)['"]?(\))/g,findStyles:/@media *([^\{]+)\{([\S\s]+?)$/,only:/(only\s+)?([a-zA-Z]+)\s?/,minw:/\([\s]*min\-width\s*:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/,maxw:/\([\s]*max\-width\s*:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/},c.mediaQueriesSupported=a.matchMedia&&null!==a.matchMedia("only all")&&a.matchMedia("only all").matches,!c.mediaQueriesSupported){var g,h,i,j=a.document,k=j.documentElement,l=[],m=[],n=[],o={},p=30,q=j.getElementsByTagName("head")[0]||k,r=j.getElementsByTagName("base")[0],s=q.getElementsByTagName("link"),t=function(){var a,b=j.createElement("div"),c=j.body,d=k.style.fontSize,e=c&&c.style.fontSize,f=!1;return b.style.cssText="position:absolute;font-size:1em;width:1em",c||(c=f=j.createElement("body"),c.style.background="none"),k.style.fontSize="100%",c.style.fontSize="100%",c.appendChild(b),f&&k.insertBefore(c,k.firstChild),a=b.offsetWidth,f?k.removeChild(c):c.removeChild(b),k.style.fontSize=d,e&&(c.style.fontSize=e),a=i=parseFloat(a)},u=function(b){var c="clientWidth",d=k[c],e="CSS1Compat"===j.compatMode&&d||j.body[c]||d,f={},o=s[s.length-1],r=(new Date).getTime();if(b&&g&&p>r-g)return a.clearTimeout(h),h=a.setTimeout(u,p),void 0;g=r;for(var v in l)if(l.hasOwnProperty(v)){var w=l[v],x=w.minw,y=w.maxw,z=null===x,A=null===y,B="em";x&&(x=parseFloat(x)*(x.indexOf(B)>-1?i||t():1)),y&&(y=parseFloat(y)*(y.indexOf(B)>-1?i||t():1)),w.hasquery&&(z&&A||!(z||e>=x)||!(A||y>=e))||(f[w.media]||(f[w.media]=[]),f[w.media].push(m[w.rules]))}for(var C in n)n.hasOwnProperty(C)&&n[C]&&n[C].parentNode===q&&q.removeChild(n[C]);n.length=0;for(var D in f)if(f.hasOwnProperty(D)){var E=j.createElement("style"),F=f[D].join("\n");E.type="text/css",E.media=D,q.insertBefore(E,o.nextSibling),E.styleSheet?E.styleSheet.cssText=F:E.appendChild(j.createTextNode(F)),n.push(E)}},v=function(a,b,d){var e=a.replace(c.regex.keyframes,"").match(c.regex.media),f=e&&e.length||0;b=b.substring(0,b.lastIndexOf("/"));var g=function(a){return a.replace(c.regex.urls,"$1"+b+"$2$3")},h=!f&&d;b.length&&(b+="/"),h&&(f=1);for(var i=0;f>i;i++){var j,k,n,o;h?(j=d,m.push(g(a))):(j=e[i].match(c.regex.findStyles)&&RegExp.$1,m.push(RegExp.$2&&g(RegExp.$2))),n=j.split(","),o=n.length;for(var p=0;o>p;p++)k=n[p],l.push({media:k.split("(")[0].match(c.regex.only)&&RegExp.$2||"all",rules:m.length-1,hasquery:k.indexOf("(")>-1,minw:k.match(c.regex.minw)&&parseFloat(RegExp.$1)+(RegExp.$2||""),maxw:k.match(c.regex.maxw)&&parseFloat(RegExp.$1)+(RegExp.$2||"")})}u()},w=function(){if(d.length){var b=d.shift();f(b.href,function(c){v(c,b.href,b.media),o[b.href]=!0,a.setTimeout(function(){w()},0)})}},x=function(){for(var b=0;b<s.length;b++){var c=s[b],e=c.href,f=c.media,g=c.rel&&"stylesheet"===c.rel.toLowerCase();e&&g&&!o[e]&&(c.styleSheet&&c.styleSheet.rawCssText?(v(c.styleSheet.rawCssText,e,f),o[e]=!0):(!/^([a-zA-Z:]*\/\/)/.test(e)&&!r||e.replace(RegExp.$1,"").split("/")[0]===a.location.host)&&("//"===e.substring(0,2)&&(e=a.location.protocol+e),d.push({href:e,media:f})))}w()};x(),c.update=x,c.getEmValue=t,a.addEventListener?a.addEventListener("resize",b,!1):a.attachEvent&&a.attachEvent("onresize",b)}}(this); \ No newline at end of file diff --git a/docs/latest_version.txt b/docs/latest_version.txt new file mode 100644 index 0000000..f77856a --- /dev/null +++ b/docs/latest_version.txt @@ -0,0 +1 @@ +4.3.1 diff --git a/docs/pages/404.html b/docs/pages/404.html new file mode 100644 index 0000000..c561ecd --- /dev/null +++ b/docs/pages/404.html @@ -0,0 +1,29 @@ +--- +layout: error +permalink: /404.html +sitemap: false +--- + +<section class="intro"> + <div class="grid"> + <div class="unit whole align-center"> + <p class="first">Huh. It seems that page is<br/>Hyde-ing...</p> + </div> + </div> +</section> + +<section class="error"> + <div class="grid"> + <div class="unit whole align-center"> + <p>The resource you requested was not found. Here are some links to help you find your way:</p> + <nav class="main-nav"> + <ul> + <li><a href="{{ '/' | relative_url }}">Home</a></li> + <li><a href="{{ '/docs/home/' | relative_url }}">Documentation</a></li> + <li><a href="{{ '/news/' | relative_url }}">News</a></li> + <li><a href="{{ '/help/' | relative_url }}">Help</a></li> + </ul> + </nav> + </div> + </div> +</section> diff --git a/docs/pages/index.html b/docs/pages/index.html new file mode 100644 index 0000000..971b818 --- /dev/null +++ b/docs/pages/index.html @@ -0,0 +1,90 @@ +--- +layout: default +overview: true +permalink: / +--- + +<section class="intro"> + <div class="grid"> + <div class="unit whole center-on-mobiles"> + <p class="first">Transform your plain text into static websites and blogs.</p> + </div> + </div> +</section> +<section class="features center-on-mobiles"> + <div class="grid"> + <div class="unit one-third"> + <h2>Simple</h2> + <p> + No more databases, comment moderation, or pesky updates to install—just <em>your content</em>. + </p> + <a href="{{ 'docs/usage/' | relative_url }}">How Jekyll works →</a> + </div> + <div class="unit one-third"> + <h2>Static</h2> + <p><a href="https://daringfireball.net/projects/markdown/">Markdown</a>, <a href="https://github.com/Shopify/liquid/wiki">Liquid</a>, HTML <span class="amp">&</span> CSS go in. Static sites come out ready for deployment.</p> + <a href="{{ 'docs/templates/' | relative_url }}">Jekyll template guide →</a> + </div> + <div class="unit one-third"> + <h2>Blog-aware</h2> + <p> + Permalinks, categories, pages, posts, and custom layouts are all first-class citizens here. + </p> + <a href="https://import.jekyllrb.com">Migrate your blog →</a> + </div> + <div class="clear"></div> + </div> +</section> +<section class="quickstart"> + <div class="grid"> + <div class="unit golden-small center-on-mobiles"> + <h3>Get up and running <em>in seconds</em>.</h3> + </div> + <div class="unit golden-large code"> + <p class="title">Quick-start Instructions</p> + <div class="shell"> + <p class="line"> + <span class="path">~</span> + <span class="prompt">$</span> + <span class="command">gem install bundler jekyll</span> + </p> + <p class="line"> + <span class="path">~</span> + <span class="prompt">$</span> + <span class="command">jekyll new my-awesome-site</span> + </p> + <p class="line"> + <span class="path">~</span> + <span class="prompt">$</span> + <span class="command">cd my-awesome-site</span> + </p> + <p class="line"> + <span class="path">~/my-awesome-site</span> + <span class="prompt">$</span> + <span class="command">bundle exec jekyll serve</span> + </p> + <p class="line"> + <span class="output"># => Now browse to http://localhost:4000</span> + </p> + </div> + </div> + <div class="clear"></div> + </div> +</section> +<section class="free-hosting"> + <div class="grid"> + <div class="unit whole"> + <div class="grid pane"> + <div class="unit whole center-on-mobiles"> + <img src="{{ 'img/octojekyll.png' | relative_url }}" width="300" height="251" alt="Free Jekyll hosting on GitHub Pages"> + <div class="pane-content"> + <h2 class="center-on-mobiles"><strong>Free hosting</strong> with GitHub Pages</h2> + <p>Sick of dealing with hosting companies? <a href="https://pages.github.com/">GitHub Pages</a> is <em>powered by Jekyll</em>, so you can easily deploy your site using GitHub for free—<a href="https://help.github.com/articles/about-supported-custom-domains/">custom domain name</a> and all.</p> + <a href="https://pages.github.com/">Learn more about GitHub Pages →</a> + </div> + </div> + <div class="clear"></div> + </div> + </div> + </div> +</section> diff --git a/docs/pages/jekyllconf.md b/docs/pages/jekyllconf.md new file mode 100644 index 0000000..2b6ac79 --- /dev/null +++ b/docs/pages/jekyllconf.md @@ -0,0 +1,25 @@ +--- +layout: page +title: JekyllConf +permalink: /jekyllconf/ +--- + +[JekyllConf](https://jekyllconf.com) is a free, online conference for all things Jekyll hosted by [CloudCannon](https://cloudcannon.com). Each year members of the Jekyll community speak about interesting use cases, tricks they've learned, or meta Jekyll topics. + +## Featured + +{% assign random = site.time | date: "%s%N" | modulo: site.data.jekyllconf-talks.size %} +{% assign featured = site.data.jekyllconf-talks[random] %} + +**{{ featured.topic }}** - [*{{ featured.speaker }}*](https://twitter.com/{{ featured.twitter_handle }}) +<div class="videoWrapper"> + <iframe width="420" height="315" src="https://www.youtube.com/embed/{{ featured.youtube_id }}" frameborder="0" allowfullscreen></iframe> +</div> + +{% assign talks = site.data.jekyllconf-talks | group_by: 'year' %} +{% for year in talks reversed %} +## {{ year.name }} +{% for talk in year.items %} + * [**{{ talk.topic }}**](https://youtu.be/{{ talk.youtube_id }}) - [*{{ talk.speaker }}*](https://twitter.com/{{ talk.twitter_handle }}) +{% endfor %} +{% endfor %} diff --git a/docs/pages/news.html b/docs/pages/news.html new file mode 100644 index 0000000..c010d2d --- /dev/null +++ b/docs/pages/news.html @@ -0,0 +1,14 @@ +--- +layout: news +title: News +permalink: /news/ +author: all +--- + +{% for post in site.posts -%} + {% if forloop.index == 1 -%} + {% include news_item.html -%} + {% else -%} + {% include news_item_archive.html -%} + {% endif -%} +{% endfor -%} diff --git a/docs/pages/philosophy.md b/docs/pages/philosophy.md new file mode 100644 index 0000000..93021fb --- /dev/null +++ b/docs/pages/philosophy.md @@ -0,0 +1,52 @@ +--- +layout: page +title: Philosophy +permalink: /philosophy/ +--- + +Jekyll offers a unique philosophy when approaching the problem of static +site generation. This core philosophy drives development and product +decisions. When a contributor, maintainer, or user asks herself what Jekyll +is about, the following principles should come to mind: + +### 1. No Magic + +Jekyll is not magic. A user should be able to understand the underlying +processes that make up the Jekyll build without much reading. It should +do only what you ask it to and nothing more. When a user takes a certain +action, the outcome should be easily understandable and focused. + +### 2. It "Just Works" + +The out-of-the-box experience should be that it "just works." Run +`gem install jekyll` and it should build any Jekyll site that it's given. +Features like auto-regeneration and settings like the markdown renderer +should represent sane defaults that work perfectly for the vast majority of +cases. The burden of initial configuration should not be placed on the user. + +### 3. Content is King + +Why is Jekyll so loved by content creators? It focuses on content first and +foremost, making the process of publishing content on the Web easy. Users +should find the management of their content enjoyable and simple. + +### 4. Stability + +If a user's site builds today, it should build tomorrow. +Backwards-compatibility should be strongly preferred over breaking changes. +Breaking changes should be made to support a strong practical goal, and +breaking changes should never be made to drive forward "purity" of the +codebase, or other changes purely to make the maintainers' lives easier. +Breaking changes provide a significant amount of friction between upgrades +and reduce the confidence of users in this software, and should thus be +avoided unless absolutely necessary. +Upon breaking changes, provide a clear path for users to upgrade. + +### 5. Small & Extensible + +The core of Jekyll should be simple and small, and extensibility should be +a first-class feature to provide added functionality from community +contributors. The core should be kept to features used by at least 90% of +users–everything else should be provided as a plugin. New features should +be shipped as plugins and focus should be put on creating extensible core +API's to support rich plugins. diff --git a/docs/pages/redirects/github.html b/docs/pages/redirects/github.html new file mode 100644 index 0000000..6042d0d --- /dev/null +++ b/docs/pages/redirects/github.html @@ -0,0 +1,4 @@ +--- +permalink: /github.html +redirect_to: https://github.com/jekyll/jekyll +--- diff --git a/docs/pages/redirects/issues.html b/docs/pages/redirects/issues.html new file mode 100644 index 0000000..6776051 --- /dev/null +++ b/docs/pages/redirects/issues.html @@ -0,0 +1,4 @@ +--- +permalink: /issues.html +redirect_to: https://github.com/jekyll/jekyll/issues +--- diff --git a/docs/pages/releases.html b/docs/pages/releases.html new file mode 100644 index 0000000..f8416c0 --- /dev/null +++ b/docs/pages/releases.html @@ -0,0 +1,14 @@ +--- +layout: news +title: Releases +permalink: /news/releases/ +author: all +--- + +{% for post in site.categories.release -%} + {% if forloop.index == 1 -%} + {% include news_item.html -%} + {% else -%} + {% include news_item_archive.html -%} + {% endif -%} +{% endfor -%} diff --git a/docs/pages/resources.md b/docs/pages/resources.md new file mode 100644 index 0000000..0b2be75 --- /dev/null +++ b/docs/pages/resources.md @@ -0,0 +1,99 @@ +--- +layout: page +title: Resources +permalink: /resources/ +redirect_from: + - /docs/resources/ +--- +Jekyll's growing community produces wide variety of themes, plugins, tutorials +and other resources that can be helpful. Below is a collection of links to +some of the most popular Jekyll resources. + +## Themes +- [GitHub.com #jekyll-theme repos](https://github.com/topics/jekyll-theme) +- [jamstackthemes.dev](https://jamstackthemes.dev/ssg/jekyll/) +- [jekyllthemes.org](http://jekyllthemes.org/) +- [jekyllthemes.io](https://jekyllthemes.io/) + +See also: [docs/themes](/docs/themes/). + +## Plugins +- [jekyll-plugin topic on GitHub](https://github.com/topics/jekyll-plugin) +- [Planet Jekyll](https://github.com/planetjekyll/awesome-jekyll-plugins) + +## Guides + +- [Community tutorials]({{ '/tutorials/home/' | relative_url }}) +- [Deploy Jekyll 4 on GitHub Pages]({{ '/docs/continuous-integration/github-actions/' | relative_url }}) +- [Deploy Jekyll on Vercel](https://github.com/vercel/vercel/tree/master/examples/jekyll) +- [Deploy Jekyll 4 on Netlify](https://www.netlify.com/blog/2020/04/02/a-step-by-step-guide-jekyll-4.0-on-netlify/) +- [CloudCannon Academy](https://learn.cloudcannon.com/) is a set of resources created by [CloudCannon](https://cloudcannon.com/) to help folks get up and running with Jekyll. They cover all skill levels, and even include some great video tutorials. +- [Jekyll Cheatsheet](https://learn.cloudcannon.com/jekyll-cheat-sheet/) is a single-page resource for Jekyll filters, variables, and the like. + +## Integrations + +Use a SaaS service as a backend for functionality on your Jekyll site + +### Comments + - [Staticman](https://staticman.net): Add user-generated content to a Jekyll site (free and open source) + - [Talkyard](https://www.talkyard.io/blog-comments): Embedded comments for Jekyll and others (free and open source, or hosted serverless) + +### Content Management + - [CloudCannon](https://cloudcannon.com/): The Cloud CMS for Jekyll + - [Contentful](https://github.com/contentful/jekyll-contentful-data-import): Content infrastructure for digital teams + - [Forestry.io](https://forestry.io/): A free Git-based responsive CMS, with content modeling and instant previews. + - [Netlify CMS](https://www.netlifycms.org/): Open source content management for your Git workflow + - [Siteleaf](https://www.siteleaf.com/): Built for developers, Loved by everyone + - [Kentico Kontent](https://github.com/Kentico/kontent-jekyll): A headless CMS with full control over content presentation + +### E-commerce + - [MemberSpace](https://www.memberspace.com/integrations/jekyll-membership/): Add memberships and paywall functionality to a Jekyll site + - [Snipcart](https://snipcart.com/blog/static-site-e-commerce-part-2-integrating-snipcart-with-jekyll): Add a shopping cart to a Jekyll site + +### Forms + - [Arengu](https://www.arengu.com) + - [Getform](https://getform.io) + - [99Inbound](https://www.99inbound.com) + - [Formcake](https://formcake.com) + - [Formcarry](https://formcarry.com) + - [Formingo](https://www.formingo.co/guides/jekyll?utm_source=github&utm_medium=jekyll-docs&utm_campaign=Jekyll%20Documentation) + - [FormKeep](https://formkeep.com/guides/contact-form-jekyll?utm_source=github&utm_medium=jekyll-docs&utm_campaign=contact-form-jekyll) + - [Formspark](https://formspark.io/) + - [Formspree (open source)](https://formspree.io/) + - [formX](https://formx.stream) + - [Simple Form](https://getsimpleform.com/) + - [SmartForms](https://smartforms.dev/) + - [Typeform](https://www.typeform.com/templates/c/forms/) + +### Search + - [Algolia](https://blog.algolia.com/instant-search-blog-documentation-jekyll-plugin/): Add a powerful instant search to your Jekyll site + - [Elastic Site Search](http://elastic.co/products/site-search/service?ultron=resources&blade=jekyll&hulk=referral): Another option for adding search to your Jekyll site, built on Elasticsearch + - [Bonsai Search](https://docs.bonsai.io/article/217-jekyll): The easiest way to use Elasticsearch for your Jekyll site + - [CloudSh](https://cloudsh.com/generators/how-to-setup-search-on-jekyll/): Website search with a few lines of JavaScript + +## Editors plugins + +- Visual Studio Code has [various jekyll related plugins](https://marketplace.visualstudio.com/search?term=tag%3Ajekyll&target=VSCode&category=All%20categories&sortBy=Installs) and supports [autocompletion for configuration file](http://json.schemastore.org/jekyll). +- [jekyll-atom](https://atom.io/packages/jekyll): A collection of snippets and tools for Jekyll in Atom +- [markdown-writer](https://atom.io/packages/markdown-writer): An Atom package for Jekyll. It can create new posts/drafts, manage tags/categories, insert link/images and add many useful key mappings. +- [sublime-jekyll](https://github.com/23maverick23/sublime-jekyll): A Sublime Text package for Jekyll static sites. This package should help creating Jekyll sites and posts easier by providing access to key template tags and filters, as well as common completions and a current date/datetime command (for dating posts). You can install this package manually via GitHub, or via [Package Control](https://packagecontrol.io/packages/Jekyll). +- [vim-jekyll](https://github.com/parkr/vim-jekyll): A vim plugin to generate new posts and run `jekyll build` all without leaving vim. +- [WordPress2Jekyll](https://wordpress.org/plugins/wp2jekyll/): A WordPress plugin that allows you to use WordPress as your editor and (automatically) export content in to Jekyll. WordPress2Jekyll attempts to marry these two systems together in order to make a site that can be easily managed from all devices. + +## Posts + +- [How I'm using Jekyll in 2016](https://mademistakes.com/articles/using-jekyll-2016/) +- [Talkyard comments instructions for Jekyll](https://jekyll-demo.talkyard.io/2018/01/09/installation-instructions.html) +- [Static Comments with Jekyll & Staticman](https://mademistakes.com/articles/improving-jekyll-static-comments/) +- [Adding Ajax pagination to Jekyll](https://eduardoboucas.com/blog/2014/11/05/adding-ajax-pagination-to-jekyll.html) +- ['About this Website', by Carter Allen](http://cartera.me/2010/08/12/about-this-website/) + + > "Jekyll is everything that I ever wanted in a blogging engine. Really. It isn't perfect, but what's excellent about it is that if there's something wrong, I know exactly how it works and how to fix it. It runs on the your machine only, and is essentially an added"build" step between you and the browser. I coded this entire site in TextMate using standard HTML5 and CSS3, and then at the end I added just a few little variables to the markup. Presto-chango, my site is built and I am at peace with the world." + +- A way to [extend Jekyll](https://github.com/rfelix/jekyll_ext) without forking and modifying the Jekyll gem codebase and some [portable Jekyll extensions](https://github.com/rfelix/jekyll_ext/wiki/Extensions) that can be reused and shared. +- [Using your Rails layouts in Jekyll](https://numbers.brighterplanet.com/2010/08/09/sharing-rails-views-with-jekyll) + +## Forks + +- [Time to Visit Bridgetown](https://www.bridgetownrb.com/news/time-to-visit-bridgetown/) +- [Creating a Faster Jekyll](https://sigpipe.macromates.com/2018/creating-a-faster-jekyll/) diff --git a/docs/pages/showcase.html b/docs/pages/showcase.html new file mode 100644 index 0000000..53f8281 --- /dev/null +++ b/docs/pages/showcase.html @@ -0,0 +1,23 @@ +--- +layout: page +title: Showcase +permalink: /showcase/ +redirect_from: + - /docs/sites/ +--- + +<p>Jekyll powers many company websites, here a few nice ones:</p> + +<ul class="showcase" id="showcase"> + {% for entry in site.data.showcase reversed -%} + <li> + <a href="{{ entry.url }}" target="_blank"> + <figure> + <img loading="lazy" src="{{ site.cloudinary_url }}/showcase/{{ entry.image }}" alt="{{ entry.name }}" width="404" height="253"> + <figcaption>{{ entry.name }}</figcaption> + </figure> + </a> + </li> + {% endfor -%} + <li class="spacer"></li> +</ul> diff --git a/docs/pages/team.md b/docs/pages/team.md new file mode 100644 index 0000000..901eb72 --- /dev/null +++ b/docs/pages/team.md @@ -0,0 +1,32 @@ +--- +layout: page +title: The Jekyll Team +permalink: /team/ +--- + +## Core Team + +_The Jekyll Core Team's responsibility is to ensure the development and +community around the Jekyll ecosystem thrive._ + +* Ashwin (@ashmaroli) +* Matt (@mattr-) + +## Security Team + +_The Jekyll Security Team's responsibility is to triage, validate, and +patch security vulnerabilities reported to them._ + +* Parker (@parkr) +* Ashwin (@ashmaroli) +* Matt (@mattr-) + +## Emeritus Core Team Members + +_Emeritus Core Team Members were once members of Jekyll's Core Team._ + +* Alfred (@alfredxing) +* Frank (@DirtyF) +* Nick (@qrush) +* Parker (@parkr) +* Tom (@mojombo) diff --git a/docs/readme.md b/docs/readme.md new file mode 100644 index 0000000..76cdd69 --- /dev/null +++ b/docs/readme.md @@ -0,0 +1,23 @@ +# Jekyll docs site + +This directory contains the code for the Jekyll docs site, [jekyllrb.com](https://jekyllrb.com/). + +## Contributing + +For information about contributing, see the [Contributing page](https://jekyllrb.com/docs/contributing/). + +## Running locally + +You can preview your contributions before opening a pull request by running from within the directory: + +1. `bundle install --without test test_legacy benchmark` +2. `bundle exec rake site:preview` + +It's just a jekyll site, afterall! :wink: + +## Updating Font Awesome + +1. Go to <https://icomoon.io/app/> +2. Choose Import Icons and load `icomoon-selection.json` +3. Choose Generate Font → Download +4. Copy the font files and adapt the CSS to the paths we use in Jekyll diff --git a/exe/jekyll b/exe/jekyll new file mode 100644 index 0000000..07e1371 --- /dev/null +++ b/exe/jekyll @@ -0,0 +1,57 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +STDOUT.sync = true + +$LOAD_PATH.unshift File.expand_path("../lib", __dir__) + +require "jekyll" +require "mercenary" + +Jekyll::PluginManager.require_from_bundler + +Jekyll::Deprecator.process(ARGV) + +Mercenary.program(:jekyll) do |p| + p.version Jekyll::VERSION + p.description "Jekyll is a blog-aware, static site generator in Ruby" + p.syntax "jekyll <subcommand> [options]" + + p.option "source", "-s", "--source [DIR]", "Source directory (defaults to ./)" + p.option "destination", "-d", "--destination [DIR]", + "Destination directory (defaults to ./_site)" + p.option "safe", "--safe", "Safe mode (defaults to false)" + p.option "plugins_dir", "-p", "--plugins PLUGINS_DIR1[,PLUGINS_DIR2[,...]]", Array, + "Plugins directory (defaults to ./_plugins)" + p.option "layouts_dir", "--layouts DIR", String, + "Layouts directory (defaults to ./_layouts)" + p.option "profile", "--profile", "Generate a Liquid rendering profile" + + Jekyll::External.require_if_present(Jekyll::External.blessed_gems) do |g, ver_constraint| + cmd = g.split("-").last + p.command(cmd.to_sym) do |c| + c.syntax cmd + c.action do + Jekyll.logger.abort_with "You must install the '#{g}' gem" \ + " version #{ver_constraint} to use the 'jekyll #{cmd}' command." + end + end + end + + Jekyll::Command.subclasses.each { |c| c.init_with_program(p) } + + p.action do |args, _| + if args.empty? + Jekyll.logger.error "A subcommand is required." + puts p + abort + else + subcommand = args.first + unless p.has_command? subcommand + Jekyll.logger.abort_with "fatal: 'jekyll #{args.first}' could not" \ + " be found. You may need to install the jekyll-#{args.first} gem" \ + " or a related gem to be able to use this subcommand." + end + end + end +end diff --git a/features/cache.feature b/features/cache.feature new file mode 100644 index 0000000..156b35c --- /dev/null +++ b/features/cache.feature @@ -0,0 +1,46 @@ +Feature: Cache + As a developer who likes to create plugins + I want to be able to cache certain aspects across multiple builds + And retrieve the cached aspects when needed + + Scenario: Default Cache directory + Given I have an "index.md" page that contains "{{ site.title }}" + And I have a configuration file with "title" set to "Hello World" + When I run jekyll build + Then I should get a zero exit status + And the .jekyll-cache directory should exist + And the .jekyll-cache/Jekyll/Cache/Jekyll--Cache directory should exist + And the _site directory should exist + And I should see "<p>Hello World</p>" in "_site/index.html" + + Scenario: Custom Cache directory + Given I have an "index.md" page that contains "{{ site.title }}" + And I have a configuration file with: + | key | value | + | title | Hello World | + | cache_dir | .foo-cache | + When I run jekyll build + Then I should get a zero exit status + And the .foo-cache directory should exist + And the .foo-cache/Jekyll/Cache/Jekyll--Cache directory should exist + But the .jekyll-cache directory should not exist + And the _site directory should exist + And I should see "<p>Hello World</p>" in "_site/index.html" + + Scenario: Disk usage in safe mode + Given I have an "index.md" page that contains "{{ site.title }}" + And I have a configuration file with "title" set to "Hello World" + When I run jekyll build --safe + Then I should get a zero exit status + But the .jekyll-cache directory should not exist + And the _site directory should exist + And I should see "<p>Hello World</p>" in "_site/index.html" + + Scenario: Disabling disk usage in non-safe mode + Given I have an "index.md" page that contains "{{ site.title }}" + And I have a configuration file with "title" set to "Hello World" + When I run jekyll build --disable-disk-cache + Then I should get a zero exit status + And the _site directory should exist + And I should see "<p>Hello World</p>" in "_site/index.html" + But the .jekyll-cache directory should not exist diff --git a/features/collections.feature b/features/collections.feature new file mode 100644 index 0000000..8b8813b --- /dev/null +++ b/features/collections.feature @@ -0,0 +1,659 @@ +Feature: Collections + As a hacker who likes to structure content + I want to be able to create collections of similar information + And render them + + Scenario: Unrendered collection + Given I have an "index.html" page that contains "Collections: {{ site.methods }}" + And I have fixture collections + And I have a "_methods/static-file.txt" file that contains "Static Content {{ site.title }}" + And I have a configuration file with "collections" set to "['methods']" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + But the _site/methods directory should not exist + And the "_site/methods/configuration.html" file should not exist + And the "_site/methods/static-file.txt" file should not exist + + Scenario: Rendered collection + Given I have an "index.html" page that contains "Collections: output => {{ site.collections[0].output }} label => {{ site.collections[0].label }}" + And I have an "collection_metadata.html" page that contains "Methods metadata: {{ site.collections[0].foo }} {{ site.collections[0] }}" + And I have fixture collections + And I have a "_methods/static-file.txt" file that contains "Static Content {{ site.title }}" + And I have a "_config.yml" file with content: + """ + collections: + methods: + output: true + foo: bar + """ + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "Collections: output => true" in "_site/index.html" + And I should see "label => methods" in "_site/index.html" + And I should see "Methods metadata: bar" in "_site/collection_metadata.html" + And I should see "<p>Whatever: foo.bar</p>" in "_site/methods/configuration.html" + And I should see "Static Content {{ site.title }}" in "_site/methods/static-file.txt" + + Scenario: Rendered collection at a custom URL + Given I have an "index.html" page that contains "Collections: {{ site.collections }}" + And I have fixture collections + And I have a "_config.yml" file with content: + """ + collections: + methods: + output: true + permalink: /:collection/:path/ + """ + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "<p>Whatever: foo.bar</p>" in "_site/methods/configuration/index.html" + + Scenario: Rendered document in a layout + Given I have an "index.html" page that contains "Collections: output => {{ site.collections[0].output }} label => {{ site.collections[0].label }} foo => {{ site.collections[0].foo }}" + And I have a default layout that contains "<div class='title'>Tom Preston-Werner</div> {{content}}" + And I have fixture collections + And I have a "_config.yml" file with content: + """ + collections: + methods: + output: true + foo: bar + """ + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "Collections: output => true" in "_site/index.html" + And I should see "label => methods" in "_site/index.html" + And I should see "foo => bar" in "_site/index.html" + And I should see "<p>Run your generators! default</p>" in "_site/methods/site/generate.html" + And I should see "<div class='title'>Tom Preston-Werner</div>" in "_site/methods/site/generate.html" + + Scenario: Collections specified as an array + Given I have an "index.html" page that contains "Collections: {% for method in site.methods %}{{ method.relative_path }} {% endfor %}" + And I have fixture collections + And I have a "_config.yml" file with content: + """ + collections: + - methods + """ + When I run jekyll build + Then I should get a zero exit status + Then the _site directory should exist + And I should see "Collections: _methods/3940394-21-9393050-fifif1323-test.md _methods/collection/entries _methods/configuration.md _methods/escape-\+ #%20\[\].md _methods/sanitized_path.md _methods/site/generate.md _methods/site/initialize.md _methods/trailing-dots...md _methods/um_hi.md" in "_site/index.html" unless Windows + And I should see "Collections: _methods/3940394-21-9393050-fifif1323-test.md _methods/collection/entries _methods/configuration.md _methods/escape-\+ #%20\[\].md _methods/sanitized_path.md _methods/site/generate.md _methods/site/initialize.md _methods/trailing-dots...md _methods/yaml_with_dots.md" in "_site/index.html" if on Windows + + Scenario: Collections specified as an hash + Given I have an "index.html" page that contains "Collections: {% for method in site.methods %}{{ method.relative_path }} {% endfor %}" + And I have fixture collections + And I have a "_config.yml" file with content: + """ + collections: + - methods + """ + When I run jekyll build + Then I should get a zero exit status + Then the _site directory should exist + And I should see "Collections: _methods/3940394-21-9393050-fifif1323-test.md _methods/collection/entries _methods/configuration.md _methods/escape-\+ #%20\[\].md _methods/sanitized_path.md _methods/site/generate.md _methods/site/initialize.md _methods/trailing-dots...md _methods/um_hi.md" in "_site/index.html" unless Windows + And I should see "Collections: _methods/3940394-21-9393050-fifif1323-test.md _methods/collection/entries _methods/configuration.md _methods/escape-\+ #%20\[\].md _methods/sanitized_path.md _methods/site/generate.md _methods/site/initialize.md _methods/trailing-dots...md _methods/yaml_with_dots.md" in "_site/index.html" if on Windows + + Scenario: Rendered collection with document with future date + Given I have a _puppies directory + And I have the following documents under the puppies collection: + | title | date | content | + | Rover | 2007-12-31 | content for Rover. | + | Fido | 2120-12-31 | content for Fido. | + And I have a "_config.yml" file with content: + """ + collections: + puppies: + output: true + """ + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "content for Rover" in "_site/puppies/rover.html" + And the "_site/puppies/fido.html" file should not exist + When I run jekyll build --future + Then I should get a zero exit status + And the _site directory should exist + And the "_site/puppies/fido.html" file should exist + + Scenario: Access rendered collection with future dated document via Liquid + Given I have a _puppies directory + And I have the following documents under the puppies collection: + | title | date | content | + | Rover | 2007-12-31 | content for Rover. | + | Fido | 2120-12-31 | content for Fido. | + And I have a "_config.yml" file with content: + """ + collections: + puppies: + output: true + """ + And I have a "index.html" page that contains "Newest puppy: {% assign puppy = site.puppies.last %}{{ puppy.title }}" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "Newest puppy: Fido" in "_site/index.html" + But the "_site/puppies/fido.html" file should not exist + When I run jekyll build --future + Then I should get a zero exit status + And the _site directory should exist + And I should see "Newest puppy: Fido" in "_site/index.html" + And the "_site/puppies/fido.html" file should exist + + Scenario: Access rendered and published collection documents via Liquid + Given I have a _puppies directory + And I have the following documents under the puppies collection: + | title | date | content | published | + | Rover | 2007-12-31 | content for Rover. | true | + | Figor | 2007-12-31 | content for Figor. | false | + | Snowy | 2199-12-31 | content for Snowy. | true | + | Hardy | 2199-12-31 | content for Hardy. | false | + And I have a "_config.yml" file with content: + """ + collections: + puppies: + output: true + """ + And I have a "index.md" page that contains "{% for puppy in site.puppies %}<div>{{ puppy.title }}</div>{% endfor %}" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "<div>Rover</div>" in "_site/index.html" + But I should see "<div>Snowy</div>" in "_site/index.html" + And I should not see "<div>Figor</div>" in "_site/index.html" + And I should not see "<div>Hardy</div>" in "_site/index.html" + And the "_site/puppies/rover.html" file should exist + And the "_site/puppies/figor.html" file should not exist + And the "_site/puppies/snowy.html" file should not exist + And the "_site/puppies/hardy.html" file should not exist + When I run jekyll build --future + Then I should get a zero exit status + And the _site directory should exist + And I should see "<div>Rover</div>" in "_site/index.html" + And I should see "<div>Snowy</div>" in "_site/index.html" + And I should not see "<div>Figor</div>" in "_site/index.html" + But I should not see "<div>Hardy</div>" in "_site/index.html" + And the "_site/puppies/rover.html" file should exist + And the "_site/puppies/figor.html" file should not exist + And the "_site/puppies/hardy.html" file should not exist + But the "_site/puppies/snowy.html" file should exist + + Scenario: Unrendered collection with future dated document + Given I have a _puppies directory + And I have the following documents under the puppies collection: + | title | date | content | + | Rover | 2007-12-31 | content for Rover. | + | Fido | 2120-12-31 | content for Fido. | + And I have a "_config.yml" file with content: + """ + collections: + puppies: + output: false + """ + And I have a "foo.txt" file that contains "random static file" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And the "_site/puppies/rover.html" file should not exist + And the "_site/puppies/fido.html" file should not exist + When I run jekyll build --future + Then I should get a zero exit status + And the _site directory should exist + And the "_site/puppies/fido.html" file should not exist + + Scenario: Access unrendered collection with future dated document via Liquid + Given I have a _puppies directory + And I have the following documents under the puppies collection: + | title | date | content | + | Rover | 2007-12-31 | content for Rover. | + | Fido | 2120-12-31 | content for Fido. | + And I have a "_config.yml" file with content: + """ + collections: + puppies: + output: false + """ + And I have a "index.html" page that contains "Newest puppy: {% assign puppy = site.puppies.last %}{{ puppy.title }}" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "Newest puppy: Fido" in "_site/index.html" + But the "_site/puppies/fido.html" file should not exist + When I run jekyll build --future + Then I should get a zero exit status + And the _site directory should exist + And I should see "Newest puppy: Fido" in "_site/index.html" + And the "_site/puppies/fido.html" file should not exist + + Scenario: Access unrendered but publishable collection documents via Liquid + Given I have a _puppies directory + And I have the following documents under the puppies collection: + | title | date | content | published | + | Rover | 2007-12-31 | content for Rover. | true | + | Figor | 2007-12-31 | content for Figor. | false | + | Snowy | 2199-12-31 | content for Snowy. | true | + | Hardy | 2199-12-31 | content for Hardy. | false | + And I have a "_config.yml" file with content: + """ + collections: + puppies: + output: false + """ + And I have a "index.md" page that contains "{% for puppy in site.puppies %}<div>{{ puppy.title }}</div>{% endfor %}" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "<div>Rover</div>" in "_site/index.html" + But I should see "<div>Snowy</div>" in "_site/index.html" + And I should not see "<div>Figor</div>" in "_site/index.html" + And I should not see "<div>Hardy</div>" in "_site/index.html" + And the "_site/puppies/rover.html" file should not exist + And the "_site/puppies/figor.html" file should not exist + And the "_site/puppies/snowy.html" file should not exist + And the "_site/puppies/hardy.html" file should not exist + When I run jekyll build --future + Then I should get a zero exit status + And the _site directory should exist + And I should see "<div>Rover</div>" in "_site/index.html" + And I should see "<div>Snowy</div>" in "_site/index.html" + And I should not see "<div>Figor</div>" in "_site/index.html" + But I should not see "<div>Hardy</div>" in "_site/index.html" + And the "_site/puppies/rover.html" file should not exist + And the "_site/puppies/figor.html" file should not exist + And the "_site/puppies/snowy.html" file should not exist + And the "_site/puppies/hardy.html" file should not exist + + Scenario: Access rendered collection with future date and unpublished flag via Liquid + Given I have a _puppies directory + And I have the following documents under the puppies collection: + | title | date | content | published | + | Rover | 2007-12-31 | content for Rover. | true | + | Figor | 2007-12-31 | content for Figor. | false | + | Snowy | 2199-12-31 | content for Snowy. | true | + | Hardy | 2199-12-31 | content for Hardy. | false | + And I have a "_config.yml" file with content: + """ + collections: + puppies: + output: true + """ + And I have a "index.md" page that contains "{% for puppy in site.puppies %}<div>{{ puppy.title }}</div>{% endfor %}" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "<div>Rover</div>" in "_site/index.html" + But I should see "<div>Snowy</div>" in "_site/index.html" + And I should not see "<div>Figor</div>" in "_site/index.html" + And I should not see "<div>Hardy</div>" in "_site/index.html" + And the "_site/puppies/rover.html" file should exist + And the "_site/puppies/snowy.html" file should not exist + And the "_site/puppies/figor.html" file should not exist + And the "_site/puppies/hardy.html" file should not exist + When I run jekyll build --unpublished + Then I should get a zero exit status + And the _site directory should exist + And I should see "<div>Rover</div>" in "_site/index.html" + And I should see "<div>Snowy</div>" in "_site/index.html" + And I should see "<div>Figor</div>" in "_site/index.html" + But I should see "<div>Hardy</div>" in "_site/index.html" + And the "_site/puppies/rover.html" file should exist + And the "_site/puppies/snowy.html" file should not exist + And the "_site/puppies/figor.html" file should exist + And the "_site/puppies/hardy.html" file should not exist + When I run jekyll build --unpublished --future + Then I should get a zero exit status + And the _site directory should exist + And I should see "<div>Rover</div>" in "_site/index.html" + And I should see "<div>Snowy</div>" in "_site/index.html" + And I should see "<div>Figor</div>" in "_site/index.html" + But I should see "<div>Hardy</div>" in "_site/index.html" + And the "_site/puppies/rover.html" file should exist + And the "_site/puppies/snowy.html" file should exist + And the "_site/puppies/figor.html" file should exist + And the "_site/puppies/hardy.html" file should exist + + Scenario: Access unrendered collection with future date and unpublished flag via Liquid + Given I have a _puppies directory + And I have the following documents under the puppies collection: + | title | date | content | published | + | Rover | 2007-12-31 | content for Rover. | true | + | Figor | 2007-12-31 | content for Figor. | false | + | Snowy | 2199-12-31 | content for Snowy. | true | + | Hardy | 2199-12-31 | content for Hardy. | false | + And I have a "_config.yml" file with content: + """ + collections: + puppies: + output: false + """ + And I have a "index.md" page that contains "{% for puppy in site.puppies %}<div>{{ puppy.title }}</div>{% endfor %}" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "<div>Rover</div>" in "_site/index.html" + But I should see "<div>Snowy</div>" in "_site/index.html" + And I should not see "<div>Figor</div>" in "_site/index.html" + And I should not see "<div>Hardy</div>" in "_site/index.html" + And the "_site/puppies/rover.html" file should not exist + And the "_site/puppies/snowy.html" file should not exist + And the "_site/puppies/figor.html" file should not exist + And the "_site/puppies/hardy.html" file should not exist + When I run jekyll build --unpublished + Then I should get a zero exit status + And the _site directory should exist + And I should see "<div>Rover</div>" in "_site/index.html" + And I should see "<div>Snowy</div>" in "_site/index.html" + And I should see "<div>Figor</div>" in "_site/index.html" + But I should see "<div>Hardy</div>" in "_site/index.html" + And the "_site/puppies/rover.html" file should not exist + And the "_site/puppies/snowy.html" file should not exist + And the "_site/puppies/figor.html" file should not exist + And the "_site/puppies/hardy.html" file should not exist + When I run jekyll build --unpublished --future + Then I should get a zero exit status + And the _site directory should exist + And I should see "<div>Rover</div>" in "_site/index.html" + And I should see "<div>Snowy</div>" in "_site/index.html" + And I should see "<div>Figor</div>" in "_site/index.html" + But I should see "<div>Hardy</div>" in "_site/index.html" + And the "_site/puppies/rover.html" file should not exist + And the "_site/puppies/snowy.html" file should not exist + And the "_site/puppies/figor.html" file should not exist + And the "_site/puppies/hardy.html" file should not exist + + Scenario: All the documents + Given I have an "index.html" page that contains "All documents: {% for doc in site.documents %}{{ doc.relative_path }} {% endfor %}" + And I have fixture collections + And I have a "_config.yml" file with content: + """ + collections: + - methods + """ + When I run jekyll build + Then I should get a zero exit status + Then the _site directory should exist + And I should see "All documents: _methods/3940394-21-9393050-fifif1323-test.md _methods/collection/entries _methods/configuration.md _methods/escape-\+ #%20\[\].md _methods/sanitized_path.md _methods/site/generate.md _methods/site/initialize.md _methods/trailing-dots...md _methods/um_hi.md" in "_site/index.html" unless Windows + And I should see "All documents: _methods/3940394-21-9393050-fifif1323-test.md _methods/collection/entries _methods/configuration.md _methods/escape-\+ #%20\[\].md _methods/sanitized_path.md _methods/site/generate.md _methods/site/initialize.md _methods/trailing-dots...md _methods/yaml_with_dots.md" in "_site/index.html" if on Windows + + Scenario: Documents have an output attribute, which is the converted HTML + Given I have an "index.html" page that contains "Second document's output: {{ site.documents[2].output }}" + And I have fixture collections + And I have a "_config.yml" file with content: + """ + collections: + - methods + """ + When I run jekyll build + Then I should get a zero exit status + Then the _site directory should exist + And I should see "Second document's output: <p>Use <code class=\"language-plaintext highlighter-rouge\">Jekyll.configuration</code> to build a full configuration for use w/Jekyll.</p>\n\n<p>Whatever: foo.bar</p>" in "_site/index.html" + + Scenario: Documents have an output attribute, which is the converted HTML based on site.config + Given I have an "index.html" page that contains "Second document's output: {{ site.documents[2].output }}" + And I have fixture collections + And I have a "_config.yml" file with content: + """ + kramdown: + guess_lang: false + collections: + - methods + """ + When I run jekyll build + Then I should get a zero exit status + Then the _site directory should exist + And I should see "Second document's output: <p>Use <code>Jekyll.configuration</code> to build a full configuration for use w/Jekyll.</p>\n\n<p>Whatever: foo.bar</p>" in "_site/index.html" + + Scenario: Filter documents by where + Given I have an "index.html" page that contains "{% assign items = site.methods | where: 'whatever','foo.bar' %}Item count: {{ items.size }}" + And I have fixture collections + And I have a "_config.yml" file with content: + """ + collections: + - methods + """ + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "Item count: 2" in "_site/index.html" + + Scenario: Sort by title + Given I have an "index.html" page that contains "{% assign items = site.methods | sort: 'title' %}2. of {{ items.size }}: {{ items[2].output }}" + And I have fixture collections + And I have a "_config.yml" file with content: + """ + collections: + - methods + """ + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "2. of 10: <p>Page without title.</p>" in "_site/index.html" unless Windows + And I should see "2. of 9: <p>Page without title.</p>" in "_site/index.html" if on Windows + + Scenario: Sort by relative_path + Given I have an "index.html" page that contains "Collections: {% assign methods = site.methods | sort: 'relative_path' %}{{ methods | map:"title" | join: ", " }}" + And I have fixture collections + And I have a "_config.yml" file with content: + """ + collections: + - methods + """ + When I run jekyll build + Then I should get a zero exit status + Then the _site directory should exist + And I should see "Collections: this is a test!, Collection#entries, Jekyll.configuration, Jekyll.escape, Jekyll.sanitized_path, Site#generate, Initialize, Ellipsis Path, Site#generate, YAML with Dots" in "_site/index.html" unless Windows + And I should see "Collections: this is a test!, Collection#entries, Jekyll.configuration, Jekyll.escape, Jekyll.sanitized_path, Site#generate, Initialize, Ellipsis Path, YAML with Dots" in "_site/index.html" if on Windows + + Scenario: Sort all entries by a Front Matter key defined in all entries + Given I have an "index.html" page that contains "Collections: {{ site.tutorials | map: 'title' | join: ', ' }}" + And I have fixture collections + And I have a _layouts directory + And I have a "_layouts/tutorial.html" file with content: + """ + {% if page.previous %}Previous: {{ page.previous.title }}{% endif %} + + {% if page.next %}Next: {{ page.next.title }}{% endif %} + """ + And I have a "_config.yml" file with content: + """ + collections: + tutorials: + output: true + sort_by: lesson + + defaults: + - scope: + path: "" + type: tutorials + values: + layout: tutorial + + """ + When I run jekyll build + Then I should get a zero exit status + Then the _site directory should exist + And I should see "Collections: Getting Started, Let's Roll!, Dive-In and Publish Already!, Tip of the Iceberg, Extending with Plugins, Graduation Day" in "_site/index.html" + And I should not see "Previous: Graduation Day" in "_site/tutorials/lets-roll.html" + And I should not see "Next: Tip of the Iceberg" in "_site/tutorials/lets-roll.html" + But I should see "Previous: Getting Started" in "_site/tutorials/lets-roll.html" + And I should see "Next: Dive-In and Publish Already!" in "_site/tutorials/lets-roll.html" + + Scenario: Sort all entries by a Front Matter key defined in only some entries + Given I have an "index.html" page that contains "Collections: {{ site.tutorials | map: 'title' | join: ', ' }}" + And I have fixture collections + And I have a _layouts directory + And I have a "_layouts/tutorial.html" file with content: + """ + {% if page.previous %}Previous: {{ page.previous.title }}{% endif %} + + {% if page.next %}Next: {{ page.next.title }}{% endif %} + """ + And I have a "_config.yml" file with content: + """ + collections: + tutorials: + output: true + sort_by: approx_time + + defaults: + - scope: + path: "" + type: tutorials + values: + layout: tutorial + + """ + When I run jekyll build + Then I should get a zero exit status + Then the _site directory should exist + And I should see "'approx_time' not defined" in the build output + And I should see "Collections: Extending with Plugins, Let's Roll!, Getting Started, Graduation Day, Dive-In and Publish Already!, Tip of the Iceberg" in "_site/index.html" + And I should see "Previous: Getting Started" in "_site/tutorials/graduation-day.html" + And I should see "Next: Dive-In and Publish Already!" in "_site/tutorials/graduation-day.html" + + Scenario: Manually sort entries + Given I have an "index.html" page that contains "Collections: {{ site.tutorials | map: 'title' | join: ', ' }}" + And I have fixture collections + And I have a _layouts directory + And I have a "_layouts/tutorial.html" file with content: + """ + {% if page.previous %}Previous: {{ page.previous.title }}{% endif %} + + {% if page.next %}Next: {{ page.next.title }}{% endif %} + """ + And I have a "_config.yml" file with content: + """ + collections: + tutorials: + output: true + order: + - getting-started.md + - tip-of-the-iceberg.md + - lets-roll.md + - dive-in-and-publish-already.md + - graduation-day.md + - random-plugins.md + + defaults: + - scope: + path: "" + type: tutorials + values: + layout: tutorial + + """ + When I run jekyll build + Then I should get a zero exit status + Then the _site directory should exist + And I should see "Collections: Getting Started, Tip of the Iceberg, Let's Roll!, Dive-In and Publish Already!, Graduation Day, Extending with Plugins" in "_site/index.html" + And I should not see "Previous: Graduation Day" in "_site/tutorials/lets-roll.html" + And I should not see "Next: Tip of the Iceberg" in "_site/tutorials/lets-roll.html" + But I should see "Previous: Tip of the Iceberg" in "_site/tutorials/lets-roll.html" + And I should see "Next: Dive-In and Publish Already!" in "_site/tutorials/lets-roll.html" + + Scenario: Manually sort some entries + Given I have an "index.html" page that contains "Collections: {{ site.tutorials | map: 'title' | join: ', ' }}" + And I have fixture collections + And I have a _layouts directory + And I have a "_layouts/tutorial.html" file with content: + """ + {% if page.previous %}Previous: {{ page.previous.title }}{% endif %} + + {% if page.next %}Next: {{ page.next.title }}{% endif %} + """ + And I have a "_config.yml" file with content: + """ + collections: + tutorials: + output: true + order: + - getting-started.md + - lets-roll.md + - dive-in-and-publish-already.md + - graduation-day.md + + defaults: + - scope: + path: "" + type: tutorials + values: + layout: tutorial + + """ + When I run jekyll build + Then I should get a zero exit status + Then the _site directory should exist + And I should see "Collections: Getting Started, Let's Roll!, Dive-In and Publish Already!, Graduation Day, Extending with Plugins, Tip of the Iceberg" in "_site/index.html" + And I should not see "Previous: Graduation Day" in "_site/tutorials/lets-roll.html" + And I should not see "Previous: Tip of the Iceberg" in "_site/tutorials/lets-roll.html" + And I should not see "Next: Tip of the Iceberg" in "_site/tutorials/lets-roll.html" + But I should see "Previous: Getting Started" in "_site/tutorials/lets-roll.html" + And I should see "Next: Dive-In and Publish Already!" in "_site/tutorials/lets-roll.html" + + Scenario: Rendered collection with date/dateless filename + Given I have an "index.html" page that contains "Collections: {% for method in site.thanksgiving %}{{ method.title }} {% endfor %}" + And I have fixture collections + And I have a "_config.yml" file with content: + """ + collections: + thanksgiving: + output: true + """ + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "Thanksgiving Black Friday" in "_site/index.html" + And I should see "Happy Thanksgiving" in "_site/thanksgiving/2015-11-26-thanksgiving.html" + And I should see "Black Friday" in "_site/thanksgiving/black-friday.html" + + Scenario: Rendered collection with custom permalinks and static file contents + Given I have fixture collections + And I have a "_config.yml" file with content: + """ + collections: + methods: + output: true + permalink: /:collection/:name + """ + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "I have no front matter." in "_site/methods/extensionless_static_file" + + Scenario: Rendered collection with an extensionless document + Given I have fixture collections + And I have a "_config.yml" file with content: + """ + collections: + methods: + output: true + """ + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "I have no file extension but I should still be a part of the collection." in "_site/methods/collection/entries" + + Scenario: Rendered collection with an extensionless document in a strict site + Given I have fixture collections + And I have a _posts directory + And I have an "_posts/2019-12-26-extensioned.md" file that contains "Hello!" + And I have an "_posts/2019-12-26-extensionless" file that contains "Aloha!" + And I have an "index.md" page that contains "{{ site.posts | map: 'title' }}" + And I have a "_config.yml" file with content: + """ + strict_front_matter: true + collections: + methods: + output: true + """ + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "I have no file extension but I should still be a part of the collection." in "_site/methods/collection/entries" + And I should see "Extensioned" in "_site/index.html" + But I should not see "Extensionless" in "_site/index.html" diff --git a/features/collections_dir.feature b/features/collections_dir.feature new file mode 100644 index 0000000..712b211 --- /dev/null +++ b/features/collections_dir.feature @@ -0,0 +1,435 @@ +Feature: Collections Directory + As a hacker who likes to structure content without clutter + I want to be able to organize my collections under a single directory + And render them from there + + Scenario: Custom collections_dir containing only posts + And I have a collections/_posts directory + And I have the following post within the "collections" directory: + | title | date | content | + | Gathered Post | 2009-03-27 | Random Content. | + And I have a "_config.yml" file with content: + """ + collections_dir: collections + """ + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "Random Content." in "_site/2009/03/27/gathered-post.html" + + Scenario: Rendered collection in custom collections_dir also containing posts + Given I have a collections/_puppies directory + And I have the following document under the "puppies" collection within the "collections" directory: + | title | date | content | + | Rover | 2007-12-31 | content for Rover. | + And I have a collections/_posts directory + And I have the following post within the "collections" directory: + | title | date | content | + | Gathered Post | 2009-03-27 | Random Content. | + And I have a "_config.yml" file with content: + """ + collections: + puppies: + output: true + + collections_dir: collections + """ + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And the "_site/puppies/rover.html" file should exist + And I should see "Random Content." in "_site/2009/03/27/gathered-post.html" + + Scenario: Rendered collection in custom collections_dir with posts at the site root + Given I have a collections/_puppies directory + And I have the following document under the "puppies" collection within the "collections" directory: + | title | date | content | + | Rover | 2007-12-31 | content for Rover. | + And I have a _posts directory + And I have the following post: + | title | date | content | + | Post At Root | 2009-03-27 | Random Content. | + And I have a "_config.yml" file with content: + """ + collections: + puppies: + output: true + + collections_dir: collections + """ + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And the "_site/puppies/rover.html" file should exist + And the "_site/2009/03/27/post-at-root.html" file should not exist + And the _site/_posts directory should not exist + + Scenario: Rendered collection in custom collections_dir also containing drafts + Given I have a collections/_puppies directory + And I have the following document under the "puppies" collection within the "collections" directory: + | title | date | content | + | Rover | 2007-12-31 | content for Rover. | + And I have a collections/_drafts directory + And I have the following draft within the "collections" directory: + | title | date | content | + | Gathered Draft | 2009-03-27 | Random Content. | + And I have a "_config.yml" file with content: + """ + collections: + puppies: + output: true + + collections_dir: collections + """ + When I run jekyll build --drafts + Then I should get a zero exit status + And the _site directory should exist + And the "_site/puppies/rover.html" file should exist + And I should see "Random Content." in "_site/2009/03/27/gathered-draft.html" + And the _site/collections directory should not exist + + Scenario: Rendered collection in custom collections_dir with drafts at the site root + Given I have a collections/_puppies directory + And I have the following document under the "puppies" collection within the "collections" directory: + | title | date | content | + | Rover | 2007-12-31 | content for Rover. | + And I have a _drafts directory + And I have the following draft: + | title | date | content | + | Draft At Root | 2009-03-27 | Random Content. | + And I have a "_config.yml" file with content: + """ + collections: + puppies: + output: true + + collections_dir: collections + """ + When I run jekyll build --drafts + Then I should get a zero exit status + And the _site directory should exist + And the "_site/puppies/rover.html" file should exist + And the "_site/2009/03/27/draft-at-root.html" file should not exist + + Scenario: A complex site with collections posts and drafts at various locations + Given I have a gathering/_puppies directory + And I have a gathering/_posts directory + And I have a gathering/_drafts directory + And I have a _puppies directory + And I have a _posts directory + And I have a _drafts directory + And I have the following document under the "puppies" collection within the "gathering" directory: + | title | date | content | + | Rover in Gathering | 2007-12-31 | content for Rover. | + And I have the following document under the puppies collection: + | title | date | content | + | Rover At Root | 2007-12-31 | content for Rover. | + And I have the following post within the "gathering" directory: + | title | date | content | + | Post in Gathering | 2009-03-27 | Totally nothing. | + And I have the following post: + | title | date | content | + | Post At Root | 2009-03-27 | Totally nothing. | + And I have the following draft within the "gathering" directory: + | title | date | content | + | Draft In Gathering | 2009-03-27 | This is a draft. | + And I have the following draft: + | title | date | content | + | Draft At Root | 2009-03-27 | This is a draft. | + And I have a "_config.yml" file with content: + """ + collections: + puppies: + output: true + + collections_dir: gathering + """ + And I have a "gathering/_puppies/static_file.txt" file that contains "Static content." + And I have a gathering/_puppies/nested directory + And I have a "gathering/_puppies/nested/static_file.txt" file that contains "Nested Static content." + When I run jekyll build --drafts + Then I should get a zero exit status + And the _site directory should exist + And the "_site/puppies/rover-in-gathering.html" file should exist + And the "_site/2009/03/27/post-in-gathering.html" file should exist + And the "_site/2009/03/27/draft-in-gathering.html" file should exist + And the "_site/2009/03/27/draft-at-root.html" file should not exist + And the "_site/puppies/rover-at-root.html" file should not exist + And I should see exactly "Static content." in "_site/puppies/static_file.txt" + And I should see exactly "Nested Static content." in "_site/puppies/nested/static_file.txt" + And the _site/gathering directory should not exist + And the _site/_posts directory should not exist + + Scenario: Rendered collection with a document that includes a relative document + Given I have a _puppies directory + And I have the following documents under the puppies collection: + | title | date | content | + | INTRO | 2007-12-31 | excerpt for all docs. | + | Rover | 2007-12-31 | {% include_relative intro.md %} | + And I have a _posts directory + And I have the following post: + | title | date | content | + | Gathered Post | 2009-03-27 | Random Content. | + And I have a "_config.yml" file with content: + """ + collections: + puppies: + output: true + """ + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And the "_site/puppies/rover.html" file should exist + And I should see "excerpt for all docs." in "_site/puppies/rover.html" + And I should see "Random Content." in "_site/2009/03/27/gathered-post.html" + + Scenario: Rendered collection in custom collections_dir with a document that includes a relative document + Given I have a collections/_puppies directory + And I have the following documents under the "puppies" collection within the "collections" directory: + | title | date | content | + | INTRO | 2007-12-31 | excerpt for all docs. | + | Rover | 2007-12-31 | {% include_relative intro.md %} | + And I have a collections/_posts directory + And I have the following post within the "collections" directory: + | title | date | content | + | Gathered Post | 2009-03-27 | Random Content. | + And I have a "_config.yml" file with content: + """ + collections: + puppies: + output: true + + collections_dir: collections + """ + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And the "_site/puppies/rover.html" file should exist + And I should see "excerpt for all docs." in "_site/puppies/rover.html" + And I should see "Random Content." in "_site/2009/03/27/gathered-post.html" + + Scenario: Front matter defaults and custom collections directory + Given I have a gathering/_players/managers directory + And I have a gathering/_players/recruits directory + And I have a gathering/_players/standby directory + And I have the following documents nested inside "managers" directory under the "players" collection within the "gathering" directory: + | title | content | + | Tony Stark | content for Tony. | + | Steve Rogers | content for Steve. | + And I have the following documents nested inside "recruits" directory under the "players" collection within the "gathering" directory: + | title | content | + | Peter Parker | content for Peter. | + | Wanda Maximoff | content for Wanda. | + And I have the following documents nested inside "standby" directory under the "players" collection within the "gathering" directory: + | title | content | + | Thanos | content for Thanos. | + | Loki | content for Loki. | + And I have a "_config.yml" file with content: + """ + collections_dir: gathering + collections: ["players"] + defaults: + - scope: + path: "" + type: players + values: + recruit: false + manager: false + villain: false + - scope: + path: gathering/_players/standby/thanos.md + type: players + values: + villain: true + - scope: + path: gathering/_players/managers/* + type: players + values: + manager: true + - scope: + path: gathering/_players/recruits/* + type: players + values: + recruit: true + """ + And I have a "index.md" file with content: + """ + --- + --- + {% for player in site.players %} + <p>{{ player.title }}: Manager: {{ player.manager }}</p> + <p>{{ player.title }}: Recruit: {{ player.recruit }}</p> + <p>{{ player.title }}: Villain: {{ player.villain }}</p> + {% endfor %} + """ + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "<p>Tony Stark: Manager: true</p>" in "_site/index.html" + And I should see "<p>Tony Stark: Recruit: false</p>" in "_site/index.html" + And I should see "<p>Tony Stark: Villain: false</p>" in "_site/index.html" + And I should see "<p>Peter Parker: Manager: false</p>" in "_site/index.html" + And I should see "<p>Peter Parker: Recruit: true</p>" in "_site/index.html" + And I should see "<p>Peter Parker: Villain: false</p>" in "_site/index.html" + And I should see "<p>Steve Rogers: Manager: true</p>" in "_site/index.html" + And I should see "<p>Steve Rogers: Recruit: false</p>" in "_site/index.html" + And I should see "<p>Steve Rogers: Villain: false</p>" in "_site/index.html" + And I should see "<p>Wanda Maximoff: Manager: false</p>" in "_site/index.html" + And I should see "<p>Wanda Maximoff: Recruit: true</p>" in "_site/index.html" + And I should see "<p>Wanda Maximoff: Villain: false</p>" in "_site/index.html" + And I should see "<p>Thanos: Manager: false</p>" in "_site/index.html" + And I should see "<p>Thanos: Recruit: false</p>" in "_site/index.html" + And I should see "<p>Thanos: Villain: true</p>" in "_site/index.html" + And I should see "<p>Loki: Manager: false</p>" in "_site/index.html" + And I should see "<p>Loki: Recruit: false</p>" in "_site/index.html" + And I should see "<p>Loki: Villain: false</p>" in "_site/index.html" + + Scenario: Sort all entries by a Front Matter key defined in all entries + Given I have an "index.html" page that contains "Collections: {{ site.tutorials | map: 'title' | join: ', ' }}" + And I have fixture collections in "gathering" directory + And I have a _layouts directory + And I have a "_layouts/tutorial.html" file with content: + """ + {% if page.previous %}Previous: {{ page.previous.title }}{% endif %} + + {% if page.next %}Next: {{ page.next.title }}{% endif %} + """ + And I have a "_config.yml" file with content: + """ + collections_dir: gathering + collections: + tutorials: + output: true + sort_by: lesson + + defaults: + - scope: + path: "" + type: tutorials + values: + layout: tutorial + + """ + When I run jekyll build + Then I should get a zero exit status + Then the _site directory should exist + And I should see "Collections: Getting Started, Let's Roll!, Dive-In and Publish Already!, Tip of the Iceberg, Extending with Plugins, Graduation Day" in "_site/index.html" + And I should not see "Previous: Graduation Day" in "_site/tutorials/lets-roll.html" + And I should not see "Next: Tip of the Iceberg" in "_site/tutorials/lets-roll.html" + But I should see "Previous: Getting Started" in "_site/tutorials/lets-roll.html" + And I should see "Next: Dive-In and Publish Already!" in "_site/tutorials/lets-roll.html" + + Scenario: Sort all entries by a Front Matter key defined in only some entries + Given I have an "index.html" page that contains "Collections: {{ site.tutorials | map: 'title' | join: ', ' }}" + And I have fixture collections in "gathering" directory + And I have a _layouts directory + And I have a "_layouts/tutorial.html" file with content: + """ + {% if page.previous %}Previous: {{ page.previous.title }}{% endif %} + + {% if page.next %}Next: {{ page.next.title }}{% endif %} + """ + And I have a "_config.yml" file with content: + """ + collections_dir: gathering + collections: + tutorials: + output: true + sort_by: approx_time + + defaults: + - scope: + path: "" + type: tutorials + values: + layout: tutorial + + """ + When I run jekyll build + Then I should get a zero exit status + Then the _site directory should exist + And I should see "'approx_time' not defined" in the build output + And I should see "Collections: Extending with Plugins, Let's Roll!, Getting Started, Graduation Day, Dive-In and Publish Already!, Tip of the Iceberg" in "_site/index.html" + And I should see "Previous: Getting Started" in "_site/tutorials/graduation-day.html" + And I should see "Next: Dive-In and Publish Already!" in "_site/tutorials/graduation-day.html" + + Scenario: Manually sort entries + Given I have an "index.html" page that contains "Collections: {{ site.tutorials | map: 'title' | join: ', ' }}" + And I have fixture collections in "gathering" directory + And I have a _layouts directory + And I have a "_layouts/tutorial.html" file with content: + """ + {% if page.previous %}Previous: {{ page.previous.title }}{% endif %} + + {% if page.next %}Next: {{ page.next.title }}{% endif %} + """ + And I have a "_config.yml" file with content: + """ + collections_dir: gathering + collections: + tutorials: + output: true + order: + - getting-started.md + - tip-of-the-iceberg.md + - lets-roll.md + - dive-in-and-publish-already.md + - graduation-day.md + - random-plugins.md + + defaults: + - scope: + path: "" + type: tutorials + values: + layout: tutorial + + """ + When I run jekyll build + Then I should get a zero exit status + Then the _site directory should exist + And I should see "Collections: Getting Started, Tip of the Iceberg, Let's Roll!, Dive-In and Publish Already!, Graduation Day, Extending with Plugins" in "_site/index.html" + And I should not see "Previous: Graduation Day" in "_site/tutorials/lets-roll.html" + And I should not see "Next: Tip of the Iceberg" in "_site/tutorials/lets-roll.html" + But I should see "Previous: Tip of the Iceberg" in "_site/tutorials/lets-roll.html" + And I should see "Next: Dive-In and Publish Already!" in "_site/tutorials/lets-roll.html" + + Scenario: Manually sort some entries + Given I have an "index.html" page that contains "Collections: {{ site.tutorials | map: 'title' | join: ', ' }}" + And I have fixture collections in "gathering" directory + And I have a _layouts directory + And I have a "_layouts/tutorial.html" file with content: + """ + {% if page.previous %}Previous: {{ page.previous.title }}{% endif %} + + {% if page.next %}Next: {{ page.next.title }}{% endif %} + """ + And I have a "_config.yml" file with content: + """ + collections_dir: gathering + collections: + tutorials: + output: true + order: + - getting-started.md + - lets-roll.md + - dive-in-and-publish-already.md + - graduation-day.md + + defaults: + - scope: + path: "" + type: tutorials + values: + layout: tutorial + + """ + When I run jekyll build + Then I should get a zero exit status + Then the _site directory should exist + And I should see "Collections: Getting Started, Let's Roll!, Dive-In and Publish Already!, Graduation Day, Extending with Plugins, Tip of the Iceberg" in "_site/index.html" + And I should not see "Previous: Graduation Day" in "_site/tutorials/lets-roll.html" + And I should not see "Previous: Tip of the Iceberg" in "_site/tutorials/lets-roll.html" + And I should not see "Next: Tip of the Iceberg" in "_site/tutorials/lets-roll.html" + But I should see "Previous: Getting Started" in "_site/tutorials/lets-roll.html" + And I should see "Next: Dive-In and Publish Already!" in "_site/tutorials/lets-roll.html" diff --git a/features/create_sites.feature b/features/create_sites.feature new file mode 100644 index 0000000..cd6e1b6 --- /dev/null +++ b/features/create_sites.feature @@ -0,0 +1,214 @@ +Feature: Create sites + As a hacker who likes to blog + I want to be able to make a static site + In order to share my awesome ideas with the interwebs + + Scenario: Blank site + Given I do not have a "test_blank" directory + When I run jekyll new test_blank --blank + Then the test_blank/_data directory should exist + And the test_blank/_drafts directory should exist + And the test_blank/_includes directory should exist + And the test_blank/_layouts directory should exist + And the test_blank/_posts directory should exist + And the test_blank/_sass directory should exist + And the test_blank/assets/css directory should exist + And the "test_blank/_layouts/default.html" file should exist + And the "test_blank/_sass/main.scss" file should exist + And the "test_blank/assets/css/main.scss" file should exist + And the "test_blank/_config.yml" file should exist + And the "test_blank/index.md" file should exist + + Scenario: Basic site + Given I have an "index.html" file that contains "Basic Site" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "Basic Site" in "_site/index.html" + + Scenario: Basic site with a post + Given I have a _posts directory + And I have the following post: + | title | date | content | + | Hackers | 2009-03-27 | My First Exploit | + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "My First Exploit" in "_site/2009/03/27/hackers.html" + + Scenario: Basic site with layout and a page + Given I have a _layouts directory + And I have an "index.html" page with layout "default" that contains "Basic Site with Layout" + And I have a default layout that contains "Page Layout: {{ content }}" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "Page Layout: Basic Site with Layout" in "_site/index.html" + + Scenario: Basic site with layout and a post + Given I have a _layouts directory + And I have a _posts directory + And I have the following posts: + | title | date | layout | content | + | Wargames | 2009-03-27 | default | The only winning move is not to play. | + And I have a default layout that contains "Post Layout: {{ content }}" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "Post Layout: <p>The only winning move is not to play.</p>" in "_site/2009/03/27/wargames.html" + + Scenario: Basic site with layout inside a subfolder and a post + Given I have a _layouts directory + And I have a _posts directory + And I have the following posts: + | title | date | layout | content | + | Wargames | 2009-03-27 | post/simple | The only winning move is not to play. | + And I have a post/simple layout that contains "Post Layout: {{ content }}" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "Post Layout: <p>The only winning move is not to play.</p>" in "_site/2009/03/27/wargames.html" + + Scenario: Basic site with layouts, pages, posts and files + Given I have a _layouts directory + And I have a page layout that contains "Page {{ page.title }}: {{ content }}" + And I have a post layout that contains "Post {{ page.title }}: {{ content }}" + And I have an "index.html" page with layout "page" that contains "Site contains {{ site.pages.size }} pages and {{ site.posts.size }} posts" + And I have a blog directory + And I have a "blog/index.html" page with layout "page" that contains "blog category index page" + And I have an "about.html" file that contains "No replacement {{ site.posts.size }}" + And I have an "another_file" file that contains "" + And I have a _posts directory + And I have the following posts: + | title | date | layout | content | + | entry1 | 2009-03-27 | post | content for entry1. | + | entry2 | 2009-04-27 | post | content for entry2. | + And I have a category/_posts directory + And I have the following posts in "category": + | title | date | layout | content | + | entry3 | 2009-05-27 | post | content for entry3. | + | entry4 | 2009-06-27 | post | content for entry4. | + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "Page : Site contains 2 pages and 4 posts" in "_site/index.html" + And I should see "No replacement \{\{ site.posts.size \}\}" in "_site/about.html" + And I should see "" in "_site/another_file" + And I should see "Page : blog category index page" in "_site/blog/index.html" + And I should see "Post entry1: <p>content for entry1.</p>" in "_site/2009/03/27/entry1.html" + And I should see "Post entry2: <p>content for entry2.</p>" in "_site/2009/04/27/entry2.html" + And I should see "Post entry3: <p>content for entry3.</p>" in "_site/category/2009/05/27/entry3.html" + And I should see "Post entry4: <p>content for entry4.</p>" in "_site/category/2009/06/27/entry4.html" + + Scenario: Basic site with include tag + Given I have a _includes directory + And I have an "index.html" page that contains "Basic Site with include tag: {% include about.textile %}" + And I have an "_includes/about.textile" file that contains "Generated by Jekyll" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "Basic Site with include tag: Generated by Jekyll" in "_site/index.html" + + Scenario: Basic site with subdir include tag + Given I have a _includes directory + And I have an "_includes/about.textile" file that contains "Generated by Jekyll" + And I have an info directory + And I have an "info/index.html" page that contains "Basic Site with subdir include tag: {% include about.textile %}" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "Basic Site with subdir include tag: Generated by Jekyll" in "_site/info/index.html" + + Scenario: Basic site with nested include tag + Given I have a _includes directory + And I have an "_includes/about.textile" file that contains "Generated by {% include jekyll.textile %}" + And I have an "_includes/jekyll.textile" file that contains "Jekyll" + And I have an "index.html" page that contains "Basic Site with include tag: {% include about.textile %}" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "Basic Site with include tag: Generated by Jekyll" in "_site/index.html" + + Scenario: Basic site with internal post linking + Given I have an "index.html" page that contains "URL: {% post_url 2008-01-01-entry2 %}" + And I have a configuration file with "permalink" set to "pretty" + And I have a _posts directory + And I have the following posts: + | title | date | layout | content | + | entry1 | 2007-12-31 | post | content for entry1. | + | entry2 | 2008-01-01 | post | content for entry2. | + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "URL: /2008/01/01/entry2/" in "_site/index.html" + + Scenario: Basic site with whitelisted dotfile + Given I have an ".htaccess" file that contains "SomeDirective" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "SomeDirective" in "_site/.htaccess" + + Scenario: File was replaced by a directory + Given I have a "test" file that contains "some stuff" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + When I delete the file "test" + Given I have a test directory + And I have a "test/index.html" file that contains "some other stuff" + When I run jekyll build + Then the _site/test directory should exist + And I should see "some other stuff" in "_site/test/index.html" + + Scenario: Basic site with unpublished page + Given I have an "index.html" page with title "index" that contains "Published page" + And I have a "public.html" page with published "true" that contains "Explicitly published page" + And I have a "secret.html" page with published "false" that contains "Unpublished page" + + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And the "_site/index.html" file should exist + And the "_site/public.html" file should exist + But the "_site/secret.html" file should not exist + + When I run jekyll build --unpublished + Then I should get a zero exit status + And the _site directory should exist + And the "_site/index.html" file should exist + And the "_site/public.html" file should exist + And the "_site/secret.html" file should exist + + Scenario: Basic site with page with future date + Given I have a _posts directory + And I have the following post: + | title | date | layout | content | + | entry1 | 2040-12-31 | post | content for entry1. | + | entry2 | 2007-12-31 | post | content for entry2. | + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "content for entry2" in "_site/2007/12/31/entry2.html" + And the "_site/2040/12/31/entry1.html" file should not exist + When I run jekyll build --future + Then I should get a zero exit status + And the _site directory should exist + And the "_site/2040/12/31/entry1.html" file should exist + + Scenario: Basic site with layouts, posts and related posts + Given I have a _layouts directory + And I have a page layout that contains "Page {{ page.title }}: {{ content }}" + And I have a post layout that contains "Post {{ page.title }}: {{ content }}Related posts: {{ site.related_posts | size }}" + And I have an "index.html" page with layout "page" that contains "Site contains {{ site.pages.size }} pages and {{ site.posts.size }} posts; Related posts: {{ site.related_posts | size }}" + And I have a _posts directory + And I have the following posts: + | title | date | layout | content | + | entry1 | 2009-03-27 | post | content for entry1. | + | entry2 | 2009-04-27 | post | content for entry2. | + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "Page : Site contains 1 pages and 2 posts; Related posts: 0" in "_site/index.html" + And I should see "Post entry1: <p>content for entry1.</p>\nRelated posts: 1" in "_site/2009/03/27/entry1.html" + And I should see "Post entry2: <p>content for entry2.</p>\nRelated posts: 1" in "_site/2009/04/27/entry2.html" diff --git a/features/data.feature b/features/data.feature new file mode 100644 index 0000000..41f476a --- /dev/null +++ b/features/data.feature @@ -0,0 +1,133 @@ +Feature: Data + In order to use well-formatted data in my blog + As a blog's user + I want to use _data directory in my site + + Scenario: autoload *.yaml files in _data directory + Given I have a _data directory + And I have a "_data/products.yaml" file with content: + """ + - name: sugar + price: 5.3 + - name: salt + price: 2.5 + """ + And I have an "index.html" page that contains "{% for product in site.data.products %}{{product.name}}{% endfor %}" + When I run jekyll build + Then the "_site/index.html" file should exist + And I should see "sugar" in "_site/index.html" + And I should see "salt" in "_site/index.html" + + Scenario: autoload *.yml files in _data directory + Given I have a _data directory + And I have a "_data/members.yml" file with content: + """ + - name: Jack + age: 28 + - name: Leon + age: 34 + """ + And I have an "index.html" page that contains "{% for member in site.data.members %}{{member.name}}{% endfor %}" + When I run jekyll build + Then the "_site/index.html" file should exist + And I should see "Jack" in "_site/index.html" + And I should see "Leon" in "_site/index.html" + + Scenario: autoload *.json files in _data directory + Given I have a _data directory + And I have a "_data/members.json" file with content: + """ + [{"name": "Jack", "age": 28},{"name": "Leon", "age": 34}] + """ + And I have an "index.html" page that contains "{% for member in site.data.members %}{{member.name}}{% endfor %}" + When I run jekyll build + Then the "_site/index.html" file should exist + And I should see "Jack" in "_site/index.html" + And I should see "Leon" in "_site/index.html" + + Scenario: autoload *.csv files in _data directory + Given I have a _data directory + And I have a "_data/members.csv" file with content: + """ + name,age + Jack,28 + Leon,34 + """ + And I have an "index.html" page that contains "{% for member in site.data.members %}{{member.name}}{% endfor %}" + When I run jekyll build + Then the "_site/index.html" file should exist + And I should see "Jack" in "_site/index.html" + And I should see "Leon" in "_site/index.html" + + Scenario: autoload *.tsv files in _data directory + Given I have a _data directory + And I have a "_data/members.tsv" file with content: + """ + name age + Jack 28 + Leon 34 + """ + And I have an "index.html" page that contains "{% for member in site.data.members %}{{member.name}}{% endfor %}" + When I run jekyll build + Then the "_site/index.html" file should exist + And I should see "Jack" in "_site/index.html" + And I should see "Leon" in "_site/index.html" + + Scenario: autoload *.yml files in _data directory with space in file name + Given I have a _data directory + And I have a "_data/team members.yml" file with content: + """ + - name: Jack + age: 28 + - name: Leon + age: 34 + """ + And I have an "index.html" page that contains "{% for member in site.data.team_members %}{{member.name}}{% endfor %}" + When I run jekyll build + Then the "_site/index.html" file should exist + And I should see "Jack" in "_site/index.html" + And I should see "Leon" in "_site/index.html" + + Scenario: autoload *.yaml files in subdirectories in _data directory + Given I have a _data directory + And I have a _data/categories directory + And I have a "_data/categories/dairy.yaml" file with content: + """ + name: Dairy Products + """ + And I have an "index.html" page that contains "{{ site.data.categories.dairy.name }}" + When I run jekyll build + Then the "_site/index.html" file should exist + And I should see "Dairy Products" in "_site/index.html" + + Scenario: folders should have precedence over files with the same name + Given I have a _data directory + And I have a _data/categories directory + And I have a "_data/categories/dairy.yaml" file with content: + """ + name: Dairy Products + """ + And I have a "_data/categories.yaml" file with content: + """ + dairy: + name: Should not display this + """ + And I have an "index.html" page that contains "{{ site.data.categories.dairy.name }}" + When I run jekyll build + Then the "_site/index.html" file should exist + And I should see "Dairy Products" in "_site/index.html" + + Scenario: should be backward compatible with site.data in _config.yml + Given I have a "_config.yml" file with content: + """ + data: + - name: Jack + age: 28 + - name: Leon + age: 34 + """ + And I have an "index.html" page that contains "{% for member in site.data %}{{member.name}}{% endfor %}" + When I run jekyll build + Then the "_site/index.html" file should exist + And I should see "Jack" in "_site/index.html" + And I should see "Leon" in "_site/index.html" diff --git a/features/drafts.feature b/features/drafts.feature new file mode 100644 index 0000000..5a7f49f --- /dev/null +++ b/features/drafts.feature @@ -0,0 +1,50 @@ +Feature: Draft Posts + As a hacker who likes to blog + I want to be able to preview drafts locally + In order to see if they look alright before publishing + + Scenario: Preview a draft + Given I have a configuration file with "permalink" set to "none" + And I have a _drafts directory + And I have the following draft: + | title | date | layout | content | + | Recipe | 2009-03-27 | default | Not baked yet. | + When I run jekyll build --drafts + Then I should get a zero exit status + And the _site directory should exist + And I should see "Not baked yet." in "_site/recipe.html" + + Scenario: Don't preview a draft + Given I have a configuration file with "permalink" set to "none" + And I have an "index.html" page that contains "Totally index" + And I have a _drafts directory + And I have the following draft: + | title | date | layout | content | + | Recipe | 2009-03-27 | default | Not baked yet. | + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And the "_site/recipe.html" file should not exist + + Scenario: Don't preview a draft that is not published + Given I have a configuration file with "permalink" set to "none" + And I have an "index.html" page that contains "Totally index" + And I have a _drafts directory + And I have the following draft: + | title | date | layout | published | content | + | Recipe | 2009-03-27 | default | false | Not baked yet. | + When I run jekyll build --drafts + Then I should get a zero exit status + And the _site directory should exist + And the "_site/recipe.html" file should not exist + + Scenario: Use page.path variable + Given I have a configuration file with "permalink" set to "none" + And I have a _drafts directory + And I have the following draft: + | title | date | layout | content | + | Recipe | 2009-03-27 | simple | Post path: {{ page.path }} | + When I run jekyll build --drafts + Then I should get a zero exit status + And the _site directory should exist + And I should see "Post path: _drafts/recipe.markdown" in "_site/recipe.html" diff --git a/features/embed_filters.feature b/features/embed_filters.feature new file mode 100644 index 0000000..32f0fe7 --- /dev/null +++ b/features/embed_filters.feature @@ -0,0 +1,161 @@ +Feature: Embed filters + As a hacker who likes to blog + I want to be able to transform text inside a post or page + In order to perform cool stuff in my posts + + Scenario: Convert date to XML schema + Given I have a _posts directory + And I have a _layouts directory + And I have the following post: + | title | date | layout | content | + | Star Wars | 2009-03-27 | default | These aren't the droids you're looking for. | + And I have a default layout that contains "{{ site.time | date_to_xmlschema }}" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see today's date in "_site/2009/03/27/star-wars.html" + + Scenario: Escape text for XML + Given I have a _posts directory + And I have a _layouts directory + And I have the following post: + | title | date | layout | content | + | Star & Wars | 2009-03-27 | default | These aren't the droids you're looking for. | + And I have a default layout that contains "{{ page.title | xml_escape }}" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "Star & Wars" in "_site/2009/03/27/star-wars.html" + + Scenario: Calculate number of words + Given I have a _posts directory + And I have a _layouts directory + And I have the following post: + | title | date | layout | content | + | Star Wars | 2009-03-27 | default | These aren't the droids you're looking for. | + And I have a default layout that contains "{{ content | number_of_words }}" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "7" in "_site/2009/03/27/star-wars.html" + + Scenario: Convert an array into a sentence + Given I have a _posts directory + And I have a _layouts directory + And I have the following post: + | title | date | layout | tags | content | + | Star Wars | 2009-03-27 | default | [scifi, movies, force] | These aren't the droids you're looking for. | + And I have a default layout that contains "{{ page.tags | array_to_sentence_string }}" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "scifi, movies, and force" in "_site/2009/03/27/star-wars.html" + + Scenario: Markdownify a given string + Given I have a _posts directory + And I have a _layouts directory + And I have the following post: + | title | date | layout | content | + | Star Wars | 2009-03-27 | default | These aren't the droids you're looking for. | + And I have a default layout that contains "By {{ '_Obi-wan_' | markdownify }}" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "By <p><em>Obi-wan</em></p>" in "_site/2009/03/27/star-wars.html" + + Scenario: Sort by an arbitrary variable + Given I have a _layouts directory + And I have the following page: + | title | layout | value | content | + | Page-1 | default | 8 | Something | + And I have the following page: + | title | layout | value | content | + | Page-2 | default | 6 | Something | + And I have a default layout that contains "{{ site.pages | sort:'value' | map:'title' | join:', ' }}" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see exactly "Page-2, Page-1" in "_site/page-1.html" + And I should see exactly "Page-2, Page-1" in "_site/page-2.html" + + Scenario: Sort pages by the title + Given I have a _layouts directory + And I have the following pages: + | title | layout | content | + | Dog | default | Run | + | Bird | default | Fly | + And I have the following page: + | layout | content | + | default | Jump | + And I have a default layout that contains "{% assign sorted_pages = site.pages | sort: 'title' %}The rule of {{ sorted_pages.size }}: {% for p in sorted_pages %}{{ p.content | strip_html | strip_newlines }}, {% endfor %}" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see exactly "The rule of 3: Jump, Fly, Run," in "_site/bird.html" + + Scenario: Sort pages by the title ordering pages without title last + Given I have a _layouts directory + And I have the following pages: + | title | layout | content | + | Dog | default | Run | + | Bird | default | Fly | + And I have the following page: + | layout | content | + | default | Jump | + And I have a default layout that contains "{% assign sorted_pages = site.pages | sort: 'title', 'last' %}The rule of {{ sorted_pages.size }}: {% for p in sorted_pages %}{{ p.content | strip_html | strip_newlines }}, {% endfor %}" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see exactly "The rule of 3: Fly, Run, Jump," in "_site/bird.html" + + Scenario: Filter posts by given property and value + Given I have a _posts directory + And I have the following posts: + | title | date | content | property | + | Bird | 2019-03-13 | Chirp | [nature, sounds] | + | Cat | 2019-03-14 | Meow | [sounds] | + | Dog | 2019-03-15 | Bark | | + | Elephant | 2019-03-16 | Asiatic | wildlife | + | Goat | 2019-03-17 | Mountains | "" | + | Horse | 2019-03-18 | Mustang | [] | + | Iguana | 2019-03-19 | Reptile | {} | + | Jaguar | 2019-03-20 | Reptile | {foo: lorem, bar: nature} | + And I have a "string-value.md" page with content: + """ + {% assign pool = site.posts | reverse | where: 'property', 'wildlife' %} + {{ pool | map: 'title' | join: ', ' }} + """ + And I have a "string-value-array.md" page with content: + """ + {% assign pool = site.posts | reverse | where: 'property', 'sounds' %} + {{ pool | map: 'title' | join: ', ' }} + """ + And I have a "string-value-hash.md" page with content: + """ + {% assign pool = site.posts | reverse | where: 'property', 'nature' %} + {{ pool | map: 'title' | join: ', ' }} + """ + And I have a "nil-value.md" page with content: + """ + {% assign pool = site.posts | reverse | where: 'property', nil %} + {{ pool | map: 'title' | join: ', ' }} + """ + And I have an "empty-liquid-literal.md" page with content: + """ + {% assign pool = site.posts | reverse | where: 'property', empty %} + {{ pool | map: 'title' | join: ', ' }} + """ + And I have a "blank-liquid-literal.md" page with content: + """ + {% assign pool = site.posts | reverse | where: 'property', blank %} + {{ pool | map: 'title' | join: ', ' }} + """ + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see exactly "<p>Elephant</p>" in "_site/string-value.html" + And I should see exactly "<p>Bird, Cat</p>" in "_site/string-value-array.html" + And I should see exactly "<p>Bird</p>" in "_site/string-value-hash.html" + And I should see exactly "<p>Dog</p>" in "_site/nil-value.html" + And I should see exactly "<p>Dog, Goat, Horse, Iguana</p>" in "_site/empty-liquid-literal.html" + And I should see exactly "<p>Dog, Goat, Horse, Iguana</p>" in "_site/blank-liquid-literal.html" diff --git a/features/frontmatter_defaults.feature b/features/frontmatter_defaults.feature new file mode 100644 index 0000000..47d81ab --- /dev/null +++ b/features/frontmatter_defaults.feature @@ -0,0 +1,195 @@ +Feature: frontmatter defaults + Scenario: Use default for frontmatter variables internally + Given I have a _layouts directory + And I have a pretty layout that contains "THIS IS THE LAYOUT: {{content}}" + + And I have a _posts directory + And I have the following post: + | title | date | content | + | default layout | 2013-09-11 | just some post | + And I have an "index.html" page with title "some title" that contains "just some page" + + And I have a configuration file with "defaults" set to "[{scope: {path: ""}, values: {layout: "pretty"}}]" + + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "THIS IS THE LAYOUT: <p>just some post</p>" in "_site/2013/09/11/default-layout.html" + And I should see "THIS IS THE LAYOUT: just some page" in "_site/index.html" + + Scenario: Use default for frontmatter variables in Liquid + Given I have a _posts directory + And I have the following post: + | title | date | content | + | default data | 2013-09-11 | <p>{{page.custom}}</p><div>{{page.author}}</div> | + And I have an "index.html" page that contains "just {{page.custom}} by {{page.author}}" + And I have a configuration file with "defaults" set to "[{scope: {path: ""}, values: {custom: "some special data", author: "Ben"}}]" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "<p>some special data</p>\n<div>Ben</div>" in "_site/2013/09/11/default-data.html" + And I should see "just some special data by Ben" in "_site/index.html" + + Scenario: Override frontmatter defaults by path + Given I have a _layouts directory + And I have a root layout that contains "root: {{ content }}" + And I have a subfolder layout that contains "subfolder: {{ content }}" + + And I have a _posts directory + And I have the following post: + | title | date | content | + | about | 2013-10-14 | info on {{page.description}} | + And I have a special/_posts directory + And I have the following post in "special": + | title | date | path | content | + | about | 2013-10-14 | local | info on {{page.description}} | + + And I have an "index.html" page with title "overview" that contains "Overview for {{page.description}}" + And I have an "special/index.html" page with title "section overview" that contains "Overview for {{page.description}}" + + And I have a configuration file with "defaults" set to "[{scope: {path: "special"}, values: {layout: "subfolder", description: "the special section"}}, {scope: {path: ""}, values: {layout: "root", description: "the webpage"}}]" + + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "root: <p>info on the webpage</p>" in "_site/2013/10/14/about.html" + And I should see "subfolder: <p>info on the special section</p>" in "_site/special/2013/10/14/about.html" + And I should see "root: Overview for the webpage" in "_site/index.html" + And I should see "subfolder: Overview for the special section" in "_site/special/index.html" + + Scenario: Use frontmatter variables by relative path + Given I have a _layouts directory + And I have a main layout that contains "main: {{ content }}" + + And I have a _posts directory + And I have the following post: + | title | date | content | + | about | 2013-10-14 | content of site/2013/10/14/about.html | + And I have a special/_posts directory + And I have the following post in "special": + | title | date | path | content | + | about1 | 2013-10-14 | local | content of site/special/2013/10/14/about1.html | + | about2 | 2013-10-14 | local | content of site/special/2013/10/14/about2.html | + + And I have a configuration file with "defaults" set to "[{scope: {path: "special"}, values: {layout: "main"}}, {scope: {path: "special/_posts"}, values: {layout: "main"}}, {scope: {path: "_posts"}, values: {layout: "main"}}]" + + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "main: <p>content of site/2013/10/14/about.html</p>" in "_site/2013/10/14/about.html" + And I should see "main: <p>content of site/special/2013/10/14/about1.html</p>" in "_site/special/2013/10/14/about1.html" + And I should see "main: <p>content of site/special/2013/10/14/about2.html</p>" in "_site/special/2013/10/14/about2.html" + + Scenario: Use frontmatter scopes for subdirectories + Given I have a _layouts directory + And I have a main layout that contains "main: {{ content }}" + + And I have a _posts/en directory + And I have the following post under "en": + | title | date | content | + | helloworld | 2014-09-01 | {{page.lang}} is the current language | + And I have a _posts/de directory + And I have the following post under "de": + | title | date | content | + | hallowelt | 2014-09-01 | {{page.lang}} is the current language | + + And I have a configuration file with "defaults" set to "[{scope: {path: "_posts/en"}, values: {layout: "main", lang: "en"}}, {scope: {path: "_posts/de"}, values: {layout: "main", lang: "de"}}]" + + When I run jekyll build + Then the _site directory should exist + And I should see "main: <p>en is the current language</p>" in "_site/2014/09/01/helloworld.html" + And I should see "main: <p>de is the current language</p>" in "_site/2014/09/01/hallowelt.html" + + Scenario: Override frontmatter defaults by type + Given I have a _posts directory + And I have the following post: + | title | date | content | + | this is a post | 2013-10-14 | blabla | + And I have an "index.html" page that contains "interesting stuff" + And I have a configuration file with "defaults" set to "[{scope: {path: "", type: "post"}, values: {permalink: "/post.html"}}, {scope: {path: "", type: "page"}, values: {permalink: "/page.html"}}, {scope: {path: ""}, values: {permalink: "/perma.html"}}]" + When I run jekyll build + Then I should see "blabla" in "_site/post.html" + And I should see "interesting stuff" in "_site/page.html" + But the "_site/perma.html" file should not exist + + Scenario: Actual frontmatter overrides defaults + Given I have a _posts directory + And I have the following post: + | title | date | permalink | author | content | + | override | 2013-10-14 | /frontmatter.html | some guy | a blog by {{page.author}} | + And I have an "index.html" page with permalink "override.html" that contains "nothing" + And I have a configuration file with "defaults" set to "[{scope: {path: ""}, values: {permalink: "/perma.html", author: "Chris"}}]" + When I run jekyll build + Then I should see "a blog by some guy" in "_site/frontmatter.html" + And I should see "nothing" in "_site/override.html" + But the "_site/perma.html" file should not exist + + Scenario: Define permalink default for posts + Given I have a _posts directory + And I have the following post: + | title | date | category | content | + | testpost | 2013-10-14 | blog | blabla | + And I have a configuration file with "defaults" set to "[{scope: {path: "", type: "posts"}, values: {permalink: "/:categories/:title/"}}]" + When I run jekyll build + Then I should see "blabla" in "_site/blog/testpost/index.html" + + Scenario: Use frontmatter defaults in collections + Given I have a _slides directory + And I have a "index.html" file that contains "nothing" + And I have a "_slides/slide1.html" file with content: + """ + --- + --- + Value: {{ page.myval }} + """ + And I have a "_config.yml" file with content: + """ + collections: + slides: + output: true + defaults: + - + scope: + path: "" + type: slides + values: + myval: "Test" + """ + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "Value: Test" in "_site/slides/slide1.html" + + Scenario: Override frontmatter defaults inside a collection + Given I have a _slides directory + And I have a "index.html" file that contains "nothing" + And I have a "_slides/slide2.html" file with content: + """ + --- + myval: Override + --- + Value: {{ page.myval }} + """ + And I have a "_config.yml" file with content: + """ + collections: + slides: + output: true + defaults: + - + scope: + path: "" + type: slides + values: + myval: "Test" + """ + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "Value: Override" in "_site/slides/slide2.html" + + Scenario: Deep merge frontmatter defaults + Given I have an "index.html" page with fruit "{orange: 1}" that contains "Fruits: {{ page.fruit.orange | plus: page.fruit.apple }}" + And I have a configuration file with "defaults" set to "[{scope: {path: ""}, values: {fruit: {apple: 2}}}]" + When I run jekyll build + Then I should see "Fruits: 3" in "_site/index.html" diff --git a/features/highlighting.feature b/features/highlighting.feature new file mode 100644 index 0000000..c9f2b00 --- /dev/null +++ b/features/highlighting.feature @@ -0,0 +1,33 @@ +Feature: Syntax Highlighting + As a hacker who likes to blog + I want to share code snippets in my blog + And make them pretty for all the world to see + + Scenario: highlighting an apache configuration + Given I have an "index.html" file with content: + """ + --- + --- + + {% highlight apache %} + RewriteEngine On + RewriteCond %{REQUEST_FILENAME} !-f + RewriteCond %{REQUEST_FILENAME} !-d + RewriteRule ^(.*)$ index.php [QSA,L] + {% endhighlight %} + + ```apache + RewriteEngine On + RewriteCond %{REQUEST_FILENAME} !-f + RewriteCond %{REQUEST_FILENAME} !-d + RewriteRule ^(.*)$ index.php [QSA,L] + ``` + """ + And I have a "_config.yml" file with content: + """ + kramdown: + input: GFM + """ + When I run jekyll build + Then I should get a zero exit-status + And I should see "<span class="nc">RewriteCond</span>" in "_site/index.html" diff --git a/features/hooks.feature b/features/hooks.feature new file mode 100644 index 0000000..1deb7f9 --- /dev/null +++ b/features/hooks.feature @@ -0,0 +1,489 @@ +Feature: Hooks + As a plugin author + I want to be able to run code during various stages of the build process + + Scenario: Run some code after site reset + Given I have a _plugins directory + And I have a "_plugins/ext.rb" file with content: + """ + Jekyll::Hooks.register :site, :after_reset do |site| + pageklass = Class.new(Jekyll::Page) do + def initialize(site, base) + @site = site + @base = base + @data = {} + @dir = '/' + @name = 'foo.html' + @content = 'mytinypage' + + self.process(@name) + end + end + + site.pages << pageklass.new(site, site.source) + end + """ + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "mytinypage" in "_site/foo.html" + + Scenario: Modify the payload before rendering the site + Given I have a _plugins directory + And I have a "index.html" page that contains "{{ site.injected }}!" + And I have a "_plugins/ext.rb" file with content: + """ + Jekyll::Hooks.register :site, :pre_render do |site, payload| + payload['site']['injected'] = 'myparam' + end + """ + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "myparam!" in "_site/index.html" + + Scenario: Modify the site contents after reading + Given I have a _plugins directory + And I have a "page1.html" page that contains "page1" + And I have a "page2.html" page that contains "page2" + And I have a "_plugins/ext.rb" file with content: + """ + Jekyll::Hooks.register :site, :post_read do |site| + site.pages.delete_if { |p| p.name == 'page1.html' } + end + """ + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And the "_site/page1.html" file should not exist + And I should see "page2" in "_site/page2.html" + + Scenario: Work with the site files after they've been written to disk + Given I have a _plugins directory + And I have a "_plugins/ext.rb" file with content: + """ + Jekyll::Hooks.register :site, :post_write do |site| + firstpage = site.pages.first + content = File.read firstpage.destination(site.dest) + File.write(File.join(site.dest, 'firstpage.html'), content) + end + """ + And I have a "page1.html" page that contains "page1" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "page1" in "_site/firstpage.html" + + Scenario: Alter a page right after it is initialized + Given I have a _plugins directory + And I have a "_plugins/ext.rb" file with content: + """ + Jekyll::Hooks.register :pages, :post_init do |page| + page.name = 'renamed.html' + page.process(page.name) + end + """ + And I have a "page1.html" page that contains "page1" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "page1" in "_site/renamed.html" + + Scenario: Alter the payload for one page but not another + Given I have a _plugins directory + And I have a "_plugins/ext.rb" file with content: + """ + Jekyll::Hooks.register :pages, :pre_render do |page, payload| + payload['page']['myparam'] = 'special' if page.name == 'page1.html' + end + """ + And I have a "page1.html" page that contains "{{ page.myparam }}" + And I have a "page2.html" page that contains "{{ page.myparam }}" + When I run jekyll build + Then I should see "special" in "_site/page1.html" + And I should not see "special" in "_site/page2.html" + + Scenario: Modify the converted HTML content of a page before rendering layout + Given I have a _layouts directory + And I have a "_layouts/page.html" file with content: + """ + <h3>Page heading</h3> + {{ content }} + """ + And I have a "page.md" page with layout "page" that contains "### Heading" + And I have a _plugins directory + And I have a "_plugins/ext.rb" file with content: + """ + Jekyll::Hooks.register :pages, :post_convert do |page| + page.content = page.content.gsub('h3', 'h4') + end + """ + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "<h3>Page heading</h3>" in "_site/page.html" + And I should see "<h4 id=\"heading\">Heading</h4>" in "_site/page.html" + + Scenario: Modify page contents before writing to disk + Given I have a _plugins directory + And I have a "index.html" page that contains "WRAP ME" + And I have a "_plugins/ext.rb" file with content: + """ + Jekyll::Hooks.register :pages, :post_render do |page| + page.output = "{{{{{ #{page.output.chomp} }}}}}" + end + """ + When I run jekyll build + Then I should see "{{{{{ WRAP ME }}}}}" in "_site/index.html" + + Scenario: Work with a page after writing it to disk + Given I have a _plugins directory + And I have a "index.html" page that contains "HELLO FROM A PAGE" + And I have a "_plugins/ext.rb" file with content: + """ + Jekyll::Hooks.register :pages, :post_write do |page| + require 'fileutils' + filename = page.destination(page.site.dest) + FileUtils.mv(filename, "#{filename}.moved") + end + """ + When I run jekyll build + Then I should see "HELLO FROM A PAGE" in "_site/index.html.moved" + + Scenario: Alter a post right after it is initialized + Given I have a _plugins directory + And I have a "_plugins/ext.rb" file with content: + """ + Jekyll::Hooks.register :posts, :post_init do |post| + post.data['harold'] = "content for entry1.".tr!('abcdefghijklmnopqrstuvwxyz', + 'nopqrstuvwxyzabcdefghijklm') + end + """ + And I have a _posts directory + And I have the following posts: + | title | date | layout | content | + | entry1 | 2015-03-14 | nil | {{ page.harold }} | + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "pbagrag sbe ragel1." in "_site/2015/03/14/entry1.html" + + Scenario: Alter the payload for certain posts + Given I have a _plugins directory + And I have a "_plugins/ext.rb" file with content: + """ + # Add myvar = 'old' to posts before 2015-03-15, and myvar = 'new' for + # others + Jekyll::Hooks.register :posts, :pre_render do |post, payload| + if post.date < Time.new(2015, 3, 15) + payload['myvar'] = 'old' + else + payload['myvar'] = 'new' + end + end + """ + And I have a _posts directory + And I have the following posts: + | title | date | layout | content | + | entry1 | 2015-03-14 | nil | {{ myvar }} post | + | entry2 | 2015-03-15 | nil | {{ myvar }} post | + When I run jekyll build + Then I should see "old post" in "_site/2015/03/14/entry1.html" + And I should see "new post" in "_site/2015/03/15/entry2.html" + + Scenario: Modify the converted HTML content of a post before rendering layout + Given I have a _layouts directory + And I have a "_layouts/post.html" file with content: + """ + <h3>Page heading</h3> + {{ content }} + """ + And I have a _posts directory + And I have a "_posts/2016-01-01-example.md" file with content: + """ + --- + layout: post + --- + ### Heading + """ + And I have a _plugins directory + And I have a "_plugins/ext.rb" file with content: + """ + Jekyll::Hooks.register :posts, :post_convert do |post| + post.content = post.content.gsub('h3', 'h4') + end + """ + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "<h3>Page heading</h3>" in "_site/2016/01/01/example.html" + And I should see "<h4 id=\"heading\">Heading</h4>" in "_site/2016/01/01/example.html" + + Scenario: Modify post contents before writing to disk + Given I have a _plugins directory + And I have a "_plugins/ext.rb" file with content: + """ + # Replace content after rendering + Jekyll::Hooks.register :posts, :post_render do |post| + post.output.gsub! /42/, 'the answer to life, the universe and everything' + end + """ + And I have a _posts directory + And I have the following posts: + | title | date | layout | content | + | entry1 | 2015-03-14 | nil | {{ 6 \| times: 7 }} | + | entry2 | 2015-03-15 | nil | {{ 6 \| times: 8 }} | + When I run jekyll build + Then I should see "the answer to life, the universe and everything" in "_site/2015/03/14/entry1.html" + And I should see "48" in "_site/2015/03/15/entry2.html" + + Scenario: Work with a post after writing it to disk + Given I have a _plugins directory + And I have a "_plugins/ext.rb" file with content: + """ + # Log all post filesystem writes + Jekyll::Hooks.register :posts, :post_write do |post| + filename = post.destination(post.site.dest) + open('_site/post-build.log', 'a') do |f| + f.puts "Wrote #{filename} at #{Time.now}" + end + end + """ + And I have a _posts directory + And I have the following posts: + | title | date | layout | content | + | entry1 | 2015-03-14 | nil | entry one | + | entry2 | 2015-03-15 | nil | entry two | + When I run jekyll build + Then I should see "_site/2015/03/14/entry1.html at" in "_site/post-build.log" + Then I should see "_site/2015/03/15/entry2.html at" in "_site/post-build.log" + + Scenario: Register a hook on multiple owners at the same time + Given I have a _plugins directory + And I have a "_plugins/ext.rb" file with content: + """ + Jekyll::Hooks.register [:pages, :posts], :post_render do |owner| + owner.output = "{{{{{ #{owner.output.chomp} }}}}}" + end + """ + And I have a "index.html" page that contains "WRAP ME" + And I have a _posts directory + And I have the following posts: + | title | date | layout | content | + | entry1 | 2015-03-14 | nil | entry one | + When I run jekyll build + Then I should see "{{{{{ WRAP ME }}}}}" in "_site/index.html" + And I should see "{{{{{ <p>entry one</p> }}}}}" in "_site/2015/03/14/entry1.html" + + Scenario: Allow hooks to have a named priority + Given I have a _plugins directory + And I have a "_plugins/ext.rb" file with content: + """ + Jekyll::Hooks.register :pages, :post_render, priority: :normal do |owner| + # first normal runs second + owner.output = "1 #{owner.output.chomp}" + end + Jekyll::Hooks.register :pages, :post_render, priority: :high do |owner| + # high runs first + owner.output = "2 #{owner.output.chomp}" + end + Jekyll::Hooks.register :pages, :post_render do |owner| + # second normal runs third (normal is default) + owner.output = "3 #{owner.output.chomp}" + end + Jekyll::Hooks.register :pages, :post_render, priority: :low do |owner| + # low runs last + owner.output = "4 #{owner.output.chomp}" + end + """ + And I have a "index.html" page that contains "WRAP ME" + When I run jekyll build + Then I should see "4 3 1 2 WRAP ME" in "_site/index.html" + + Scenario: Alter a document right after it is initialized + Given I have a _plugins directory + And I have a "_plugins/ext.rb" file with content: + """ + Jekyll::Hooks.register :documents, :pre_render do |doc, payload| + doc.data['text'] = doc.data['text'] << ' are belong to us' + end + """ + And I have a "_config.yml" file that contains "collections: [ memes ]" + And I have a _memes directory + And I have a "_memes/doc1.md" file with content: + """ + --- + text: all your base + --- + """ + And I have an "index.md" file with content: + """ + --- + --- + {{ site.memes.first.text }} + """ + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "all your base are belong to us" in "_site/index.html" + + Scenario: Modify the converted HTML content of a document before rendering layout + Given I have a _layouts directory + And I have a "_layouts/meme.html" file with content: + """ + <h3>Page heading</h3> + {{ content }} + """ + And I have a "_config.yml" file with content: + """ + collections: + memes: + output: true + """ + And I have a _memes directory + And I have a "_memes/doc1.md" file with content: + """ + --- + layout: meme + text: all your base + --- + ### {{ page.text }} + """ + And I have a _plugins directory + And I have a "_plugins/ext.rb" file with content: + """ + Jekyll::Hooks.register :documents, :post_convert do |document| + document.content = document.content.gsub('h3', 'h4') + end + """ + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "<h3>Page heading</h3>" in "_site/memes/doc1.html" + And I should see "<h4 id=\"all-your-base\">all your base</h4>" in "_site/memes/doc1.html" + + Scenario: Modify the converted HTML content of document of a particular collection before rendering layout + Given I have a _layouts directory + And I have a "_layouts/meme.html" file with content: + """ + <h3>Page heading</h3> + {{ content }} + """ + And I have a "_config.yml" file with content: + """ + collections: + memes: + output: true + """ + And I have a _memes directory + And I have a "_memes/doc1.md" file with content: + """ + --- + layout: meme + text: all your base + --- + ### {{ page.text }} + """ + And I have a _posts directory + And I have a "_posts/2016-01-01-example.md" file with content: + """ + --- + layout: meme + text: all your base + --- + ### {{ page.text }} + """ + And I have a _plugins directory + And I have a "_plugins/ext.rb" file with content: + """ + Jekyll::Hooks.register :memes, :post_convert do |document| + document.content = document.content.gsub('h3', 'h4') + end + """ + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "<h3>Page heading</h3>" in "_site/memes/doc1.html" + And I should see "<h4 id=\"all-your-base\">all your base</h4>" in "_site/memes/doc1.html" + But I should see "<h3 id=\"all-your-base\">all your base</h3>" in "_site/2016/01/01/example.html" + + Scenario: Update a document after rendering it, but before writing it to disk + Given I have a _plugins directory + And I have a "_plugins/ext.rb" file with content: + """ + Jekyll::Hooks.register :documents, :post_render do |doc| + doc.output.gsub! /<p>/, '<p class="meme">' + end + """ + And I have a "_config.yml" file with content: + """ + collections: + memes: + output: true + """ + And I have a _memes directory + And I have a "_memes/doc1.md" file with content: + """ + --- + text: all your base are belong to us + --- + {{ page.text }} + """ + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "<p class=\"meme\">all your base are belong to us" in "_site/memes/doc1.html" + + Scenario: Perform an action after every document is written + Given I have a _plugins directory + And I have a "_plugins/ext.rb" file with content: + """ + Jekyll::Hooks.register :documents, :post_write do |doc| + open('_site/document-build.log', 'a') do |f| + f.puts "Wrote document #{doc.collection.docs.index doc} at #{Time.now}" + end + end + """ + And I have a "_config.yml" file with content: + """ + collections: + memes: + output: true + """ + And I have a _memes directory + And I have a "_memes/doc1.md" file with content: + """ + --- + text: all your base are belong to us + --- + {{ page.text }} + """ + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "Wrote document 0" in "_site/document-build.log" + + Scenario: Set a custom payload['page'] property + Given I have a _plugins directory + And I have a "_plugins/ext.rb" file with content: + """ + Jekyll::Hooks.register :pages, :pre_render do |page, payload| + payload['page']['foo'] = "hello world" + end + """ + And I have a _layouts directory + And I have a "_layouts/custom.html" file with content: + """ + --- + --- + {{ content }} {% include foo.html %} + """ + And I have a _includes directory + And I have a "_includes/foo.html" file with content: + """ + {{page.foo}} + """ + And I have an "index.html" page with layout "custom" that contains "page content" + When I run jekyll build + Then the "_site/index.html" file should exist + And I should see "page content\n hello world" in "_site/index.html" diff --git a/features/include_relative_tag.feature b/features/include_relative_tag.feature new file mode 100644 index 0000000..9cbdcf1 --- /dev/null +++ b/features/include_relative_tag.feature @@ -0,0 +1,60 @@ +Feature: include_relative Tag + In order to share content across several closely related pages + As a hacker who likes to blog + I want to be able to include snippets in my site's pages and documents relative to current file + + Scenario: Include a file relative to a post + Given I have a _posts directory + And I have a _posts/snippets directory + And I have the following post: + | title | date | content | + | Star Wars | 2018-09-02 | {% include_relative snippets/welcome_para.md %} | + And I have an "_posts/snippets/welcome_para.md" file that contains "Welcome back Dear Reader!" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "Welcome back Dear Reader!" in "_site/2018/09/02/star-wars.html" + + Scenario: Include a nested file relative to a post + Given I have a _posts directory + And I have a _posts/snippets directory + And I have a _posts/snippets/welcome_para directory + And I have the following post: + | title | date | content | + | Star Wars | 2018-09-02 | {% include_relative snippets/welcome_para.md %} | + And I have an "_posts/snippets/welcome_para.md" file that contains "{% include_relative snippets/welcome_para/greeting.md %} Dear Reader!" + And I have an "_posts/snippets/welcome_para/greeting.md" file that contains "Welcome back" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "Welcome back Dear Reader!" in "_site/2018/09/02/star-wars.html" + + Scenario: Include a nested file relative to a post as an excerpt + Given I have a _posts directory + And I have a _posts/snippets directory + And I have a _posts/snippets/welcome_para directory + And I have a "_posts/2018-09-02-star-wars.md" file with content: + """ + {% include_relative snippets/welcome_para.md %} + + Hello World + """ + And I have an "_posts/snippets/welcome_para.md" file that contains "{% include_relative snippets/welcome_para/greeting.md %} Dear Reader!" + And I have an "_posts/snippets/welcome_para/greeting.md" file that contains "Welcome back" + And I have an "index.md" page that contains "{% for post in site.posts %}{{ post.excerpt }}{% endfor %}" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "Welcome back Dear Reader!" in "_site/2018/09/02/star-wars.html" + And I should see "Welcome back Dear Reader!" in "_site/index.html" + + Scenario: Include a nested file relative to a page at root + Given I have a snippets directory + And I have a snippets/welcome_para directory + And I have a "index.md" page that contains "{% include_relative snippets/welcome_para.md %}" + And I have a "snippets/welcome_para.md" file that contains "{% include_relative snippets/welcome_para/greeting.md %} Dear Reader!" + And I have a "snippets/welcome_para/greeting.md" file that contains "Welcome back" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "Welcome back Dear Reader!" in "_site/index.html" diff --git a/features/include_tag.feature b/features/include_tag.feature new file mode 100644 index 0000000..f4d8b81 --- /dev/null +++ b/features/include_tag.feature @@ -0,0 +1,131 @@ +Feature: Include tags + In order to share their content across several pages + As a hacker who likes to blog + I want to be able to include files in my blog posts + + Scenario: Include a file with parameters + Given I have an _includes directory + And I have an "_includes/header.html" file that contains "<header>My awesome blog header: {{include.param}}</header>" + And I have an "_includes/params.html" file that contains "Parameters:<ul>{% for param in include %}<li>{{param[0]}} = {{param[1]}}</li>{% endfor %}</ul>" + And I have an "_includes/ignore.html" file that contains "<footer>My blog footer</footer>" + And I have a _posts directory + And I have the following posts: + | title | date | type | content | + | Include Files | 2013-03-21 | html | {% include header.html param="myparam" %} | + | Ignore params if unused | 2013-03-21 | html | {% include ignore.html date="today" %} | + | List multiple parameters | 2013-03-21 | html | {% include params.html date="today" start="tomorrow" %} | + | Dont keep parameters | 2013-03-21 | html | {% include ignore.html param="test" %}\n{% include header.html %} | + | Allow params with spaces and quotes | 2013-04-07 | html | {% include params.html cool="param with spaces" super="\\"quoted\\"" single='has "quotes"' escaped='\\'single\\' quotes' %} | + | Parameter syntax | 2013-04-12 | html | {% include params.html param1_or_2="value" %} | + | Pass a variable | 2013-06-22 | html | {% assign var = 'some text' %}{% include params.html local=var title=page.title %} | + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "<header>My awesome blog header: myparam</header>" in "_site/2013/03/21/include-files.html" + And I should not see "myparam" in "_site/2013/03/21/ignore-params-if-unused.html" + And I should see "<li>date = today</li>" in "_site/2013/03/21/list-multiple-parameters.html" + And I should see "<li>start = tomorrow</li>" in "_site/2013/03/21/list-multiple-parameters.html" + And I should not see "<header>My awesome blog header: myparam</header>" in "_site/2013/03/21/dont-keep-parameters.html" + But I should see "<header>My awesome blog header: </header>" in "_site/2013/03/21/dont-keep-parameters.html" + And I should see "<li>cool = param with spaces</li>" in "_site/2013/04/07/allow-params-with-spaces-and-quotes.html" + And I should see "<li>super = \"quoted\"</li>" in "_site/2013/04/07/allow-params-with-spaces-and-quotes.html" + And I should see "<li>single = has \"quotes\"</li>" in "_site/2013/04/07/allow-params-with-spaces-and-quotes.html" + And I should see "<li>escaped = 'single' quotes</li>" in "_site/2013/04/07/allow-params-with-spaces-and-quotes.html" + And I should see "<li>param1_or_2 = value</li>" in "_site/2013/04/12/parameter-syntax.html" + And I should see "<li>local = some text</li>" in "_site/2013/06/22/pass-a-variable.html" + And I should see "<li>title = Pass a variable</li>" in "_site/2013/06/22/pass-a-variable.html" + + Scenario: Include a file from a variable + Given I have an _includes directory + And I have an "_includes/snippet.html" file that contains "a snippet" + And I have an "_includes/parametrized.html" file that contains "works with {{include.what}}" + And I have a configuration file with: + | key | value | + | include_file1 | snippet.html | + | include_file2 | parametrized.html | + And I have an "index.html" page that contains "{% include {{site.include_file1}} %} that {% include {{site.include_file2}} what='parameters' %}" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "a snippet that works with parameters" in "_site/index.html" + + Scenario: Include a variable file in a loop + Given I have an _includes directory + And I have an "_includes/one.html" file that contains "one" + And I have an "_includes/two.html" file that contains "two" + And I have an "index.html" page with files "[one.html, two.html]" that contains "{% for file in page.files %}{% include {{file}} %} {% endfor %}" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "one two" in "_site/index.html" + + Scenario: Include a file with variables and filters + Given I have an _includes directory + And I have an "_includes/one.html" file that contains "one included" + And I have a configuration file with: + | key | value | + | include_file | one | + And I have an "index.html" page that contains "{% include {{ site.include_file | append: '.html' }} %}" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "one included" in "_site/index.html" + + Scenario: Include a file with partial variables + Given I have an _includes directory + And I have an "_includes/one.html" file that contains "one included" + And I have a configuration file with: + | key | value | + | include_file | one | + And I have an "index.html" page that contains "{% include {{ site.include_file }}.html %}" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "one included" in "_site/index.html" + + Scenario: Include a file and rebuild when include content is changed + Given I have an _includes directory + And I have an "_includes/one.html" file that contains "include" + And I have an "index.html" page that contains "{% include one.html %}" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "include" in "_site/index.html" + When I wait 1 second + Then I have an "_includes/one.html" file that contains "include content changed" + When I run jekyll build + Then I should see "include content changed" in "_site/index.html" + + Scenario: Include a file with multiple variables + Given I have an _includes directory + And I have an "_includes/header-en.html" file that contains "include" + And I have an "index.html" page that contains "{% assign name = 'header' %}{% assign locale = 'en' %}{% include {{name}}-{{locale}}.html %}" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "include" in "_site/index.html" + + Scenario: Include a file-path with non-alphanumeric character sequences + Given I have an _includes directory + And I have an "_includes/header-en.html" file that contains "include" + And I have an "index.html" page that contains "{% include ./header-en.html %}" + When I run jekyll build + Then I should get a non-zero exit status + And I should see "Invalid syntax for include tag." in the build output + When I have an "index.html" page that contains "{% include foo/.header-en.html %}" + When I run jekyll build + Then I should get a non-zero exit status + And I should see "Invalid syntax for include tag." in the build output + When I have an "index.html" page that contains "{% include //header-en.html %}" + When I run jekyll build + Then I should get a non-zero exit status + And I should see "Invalid syntax for include tag." in the build output + When I have an "index.html" page that contains "{% include ..header-en.html %}" + When I run jekyll build + Then I should get a non-zero exit status + And I should see "Invalid syntax for include tag." in the build output + When I have an "index.html" page that contains "{% include header-en.html %}" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "include" in "_site/index.html" diff --git a/features/incremental_rebuild.feature b/features/incremental_rebuild.feature new file mode 100644 index 0000000..2d22d2e --- /dev/null +++ b/features/incremental_rebuild.feature @@ -0,0 +1,104 @@ +Feature: Incremental rebuild + As an impatient hacker who likes to blog + I want to be able to make a static site + Without waiting too long for it to build + + Scenario: Produce correct output site + Given I have a _layouts directory + And I have a _posts directory + And I have the following posts: + | title | date | layout | content | + | Wargames | 2009-03-27 | default | The only winning move is not to play. | + And I have a default layout that contains "Post Layout: {{ content }}" + When I run jekyll build -I + Then I should get a zero exit status + And the _site directory should exist + And I should see "Post Layout: <p>The only winning move is not to play.</p>" in "_site/2009/03/27/wargames.html" + When I run jekyll build -I + Then I should get a zero exit status + And the _site directory should exist + And I should see "Post Layout: <p>The only winning move is not to play.</p>" in "_site/2009/03/27/wargames.html" + + Scenario: Generate a metadata file + Given I have an "index.html" file that contains "Basic Site" + When I run jekyll build -I + Then the ".jekyll-metadata" file should exist + + Scenario: Rebuild when content is changed + Given I have an "index.html" file that contains "Basic Site" + When I run jekyll build -I + Then I should get a zero exit status + And the _site directory should exist + And I should see "Basic Site" in "_site/index.html" + When I wait 1 second + Then I have an "index.html" file that contains "Bacon Site" + When I run jekyll build -I + Then I should get a zero exit status + And the _site directory should exist + And I should see "Bacon Site" in "_site/index.html" + + Scenario: Rebuild when layout is changed + Given I have a _layouts directory + And I have an "index.html" page with layout "default" that contains "Basic Site with Layout" + And I have a default layout that contains "Page Layout: {{ content }}" + When I run jekyll build -I + Then I should get a zero exit status + And the _site directory should exist + And I should see "Page Layout: Basic Site with Layout" in "_site/index.html" + When I wait 1 second + Then I have a default layout that contains "Page Layout Changed: {{ content }}" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "Page Layout Changed: Basic Site with Layout" in "_site/index.html" + + Scenario: Rebuild when an include is changed + Given I have a _includes directory + And I have an "index.html" page that contains "Basic Site with include tag: {% include about.textile %}" + And I have an "_includes/about.textile" file that contains "Generated by Jekyll" + When I run jekyll build -I + Then I should get a zero exit status + And the _site directory should exist + And I should see "Basic Site with include tag: Generated by Jekyll" in "_site/index.html" + When I wait 1 second + Then I have an "_includes/about.textile" file that contains "Regenerated by Jekyll" + When I run jekyll build -I + Then I should get a zero exit status + And the _site directory should exist + And I should see "Basic Site with include tag: Regenerated by Jekyll" in "_site/index.html" + + Scenario: Rebuild when a dependency of document in custom collection_dir is changed + Given I have a _includes directory + And I have a configuration file with "collections_dir" set to "collections" + And I have a collections/_posts directory + And I have the following post within the "collections" directory: + | title | date | layout | content | + | Wargames | 2009-03-27 | default | Basic Site with include tag: {% include about.html %} | + And I have an "_includes/about.html" file that contains "Generated by Jekyll" + When I run jekyll build -I + Then I should get a zero exit status + And the _site directory should exist + And I should see "Basic Site with include tag: Generated by Jekyll" in "_site/2009/03/27/wargames.html" + When I wait 1 second + Then I have an "_includes/about.html" file that contains "Regenerated by Jekyll" + When I run jekyll build -I + Then I should get a zero exit status + And the _site directory should exist + And I should see "Basic Site with include tag: Regenerated by Jekyll" in "_site/2009/03/27/wargames.html" + + Scenario: A themed-site and incremental regeneration + Given I have a configuration file with "theme" set to "test-theme" + And I have an "index.md" page that contains "Themed site" + When I run jekyll build --incremental --verbose + Then I should get a zero exit status + And the _site directory should exist + And I should see "Rendering: index.md" in the build output + And I should see "Themed site" in "_site/index.html" + When I wait 1 second + And I have an "about.md" page that contains "About Themed site" + When I run jekyll build --incremental --verbose + Then I should get a zero exit status + And the _site directory should exist + And I should not see "Rendering: index.md" in the build output + But I should see "Themed site" in "_site/index.html" + And I should see "About Themed site" in "_site/about.html" diff --git a/features/layout_data.feature b/features/layout_data.feature new file mode 100644 index 0000000..8a10ce8 --- /dev/null +++ b/features/layout_data.feature @@ -0,0 +1,90 @@ +Feature: Layout data + As a hacker who likes to avoid repetition + I want to be able to embed data into my layouts + In order to make the layouts slightly dynamic + + Scenario: Use custom layout data + Given I have a _layouts directory + And I have a "_layouts/999.html" file with content: + """ + --- + --- + {{ content }} layout content + """ + And I have an "index.html" page with layout "custom" that contains "page content" + And I have an "index.html" file with content: + """ + --- + layout: 999 + --- + page content + """ + When I run jekyll build + Then the "_site/index.html" file should exist + And I should see "page content layout content" in "_site/index.html" + + Scenario: Use custom layout data + Given I have a _layouts directory + And I have a "_layouts/custom.html" file with content: + """ + --- + foo: my custom data + --- + {{ content }} foo: {{ layout.foo }} + """ + And I have an "index.html" page with layout "custom" that contains "page content" + When I run jekyll build + Then the "_site/index.html" file should exist + And I should see "page content\n foo: my custom data" in "_site/index.html" + + Scenario: Inherit custom layout data + Given I have a _layouts directory + And I have a "_layouts/custom.html" file with content: + """ + --- + layout: base + foo: my custom data + --- + {{ content }} + """ + And I have a "_layouts/base.html" file with content: + """ + {{ content }} foo: {{ layout.foo }} + """ + And I have an "index.html" page with layout "custom" that contains "page content" + When I run jekyll build + Then the "_site/index.html" file should exist + And I should see "page content\n foo: my custom data" in "_site/index.html" + + Scenario: Inherit custom layout data and clear when not present + Given I have a _layouts directory + And I have a "_layouts/default.html" file with content: + """ + --- + bar: i'm default + --- + {{ content }} foo: '{{ layout.foo }}' bar: '{{ layout.bar }}' + """ + And I have a "_layouts/special.html" file with content: + """ + --- + layout: default + foo: my special data + bar: im special + --- + {{ content }} + """ + And I have a "_layouts/page.html" file with content: + """ + --- + layout: default + bar: im page + --- + {{ content }} + """ + And I have an "index.html" page with layout "special" that contains "page content" + And I have an "jekyll.html" page with layout "page" that contains "page content" + When I run jekyll build + Then the "_site/index.html" file should exist + And I should see "page content\n foo: 'my special data' bar: 'im special'" in "_site/index.html" + And I should see "page content\n foo: '' bar: 'im page'" in "_site/jekyll.html" diff --git a/features/link_tag.feature b/features/link_tag.feature new file mode 100644 index 0000000..3ea8cf6 --- /dev/null +++ b/features/link_tag.feature @@ -0,0 +1,78 @@ +Feature: Link Tag + As a hacker who likes to write a variety of content + I want to be able to link to pages and documents + And render them without much hassle + + Scenario: Basic site with two pages + Given I have an "index.md" page that contains "[About my projects]({% link about.md %})" + And I have an "about.md" page that contains "[Home]({% link index.md %})" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "<p><a href=\"/about.html\">About my projects</a></p>" in "_site/index.html" + And I should see "<p><a href=\"/\">Home</a></p>" in "_site/about.html" + + Scenario: Basic site with custom page-permalinks + Given I have an "index.md" page that contains "[About my projects]({% link about.md %})" + And I have an "about.md" page with permalink "/about/" that contains "[Home]({% link index.md %})" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "<p><a href=\"/about/\">About my projects</a></p>" in "_site/index.html" + And I should see "<p><a href=\"/\">Home</a></p>" in "_site/about/index.html" + + Scenario: Basic site with custom site-wide-permalinks + Given I have an "index.md" page that contains "[About my projects]({% link about.md %})" + And I have an "about.md" page that contains "[Home]({% link index.md %})" + And I have a configuration file with "permalink" set to "pretty" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "<p><a href=\"/about/\">About my projects</a></p>" in "_site/index.html" + And I should see "<p><a href=\"/\">Home</a></p>" in "_site/about/index.html" + + Scenario: Basic site with two pages and custom baseurl + Given I have an "index.md" page that contains "[About my projects]({% link about.md %})" + And I have an "about.md" page that contains "[Home]({% link index.md %})" + And I have a configuration file with "baseurl" set to "/blog" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "<p><a href=\"/blog/about.html\">About my projects</a></p>" in "_site/index.html" + And I should see "<p><a href=\"/blog/\">Home</a></p>" in "_site/about.html" + + Scenario: Basic site with two pages and custom baseurl and permalinks + Given I have an "index.md" page that contains "[About my projects]({% link about.md %})" + And I have an "about.md" page that contains "[Home]({% link index.md %})" + And I have a "_config.yml" file with content: + """ + baseurl: /blog + permalink: pretty + """ + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "<p><a href=\"/blog/about/\">About my projects</a></p>" in "_site/index.html" + And I should see "<p><a href=\"/blog/\">Home</a></p>" in "_site/about/index.html" + + Scenario: Linking to a ghost file + Given I have an "index.md" page that contains "[About my projects]({% link about.md %})" + And I have an "about.md" page that contains "[Contact]({% link contact.md %})" + When I run jekyll build + Then I should get a non-zero exit status + And the _site directory should not exist + And I should see "Could not find document 'contact.md' in tag 'link'" in the build output + + Scenario: Complex site with a variety of files + Given I have an "index.md" page that contains "[About my projects]({% link about.md %})" + And I have an "about.md" page that contains "[Latest Hack]({% link _posts/2018-02-15-metaprogramming.md %})" + And I have a _posts directory + And I have an "_posts/2018-02-15-metaprogramming.md" page that contains "[Download This]({% link script.txt %})" + And I have a "script.txt" file that contains "Static Alert!" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "<p><a href=\"/about.html\">About my projects</a></p>" in "_site/index.html" + And I should see "<p><a href=\"/2018/02/15/metaprogramming.html\">Latest Hack</a></p>" in "_site/about.html" + And I should see "<p><a href=\"/script.txt\">Download This</a></p>" in "_site/2018/02/15/metaprogramming.html" + And I should see "Static Alert!" in "_site/script.txt" diff --git a/features/markdown.feature b/features/markdown.feature new file mode 100644 index 0000000..71b13ad --- /dev/null +++ b/features/markdown.feature @@ -0,0 +1,34 @@ +Feature: Markdown + As a hacker who likes to blog + I want to be able to make a static site + In order to share my awesome ideas with the interwebs + + Scenario: Markdown in list on index + Given I have a configuration file with "paginate" set to "5" + And I have an "index.html" page that contains "Index - {% for post in site.posts %} {{ post.content }} {% endfor %}" + And I have a _posts directory + And I have the following post: + | title | date | content | type | + | Hackers | 2009-03-27 | # My Title | markdown | + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "Index" in "_site/index.html" + And I should see "<h1 id=\"my-title\">My Title</h1>" in "_site/2009/03/27/hackers.html" + And I should see "<h1 id=\"my-title\">My Title</h1>" in "_site/index.html" + + Scenario: Markdown in pagination on index + Given I have a configuration file with: + | key | value | + | paginate | 5 | + | plugins | [jekyll-paginate] | + And I have an "index.html" page that contains "Index - {% for post in paginator.posts %} {{ post.content }} {% endfor %}" + And I have a _posts directory + And I have the following post: + | title | date | content | type | + | Hackers | 2009-03-27 | # My Title | markdown | + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "Index" in "_site/index.html" + And I should see "<h1 id=\"my-title\">My Title</h1>" in "_site/index.html" diff --git a/features/pagination.feature b/features/pagination.feature new file mode 100644 index 0000000..e818985 --- /dev/null +++ b/features/pagination.feature @@ -0,0 +1,87 @@ +Feature: Site pagination + In order to paginate my blog + As a blog's user + I want divide the posts in several pages + + Scenario Outline: Paginate with N posts per page + Given I have a configuration file with: + | key | value | + | paginate | <num> | + | plugins | [jekyll-paginate] | + And I have a _layouts directory + And I have an "index.html" page that contains "{{ paginator.posts.size }}" + And I have a _posts directory + And I have the following posts: + | title | date | layout | content | + | Wargames | 2009-03-27 | default | The only winning move is not to play. | + | Wargames2 | 2009-04-27 | default | The only winning move is not to play2. | + | Wargames3 | 2009-05-27 | default | The only winning move is not to play3. | + | Wargames4 | 2009-06-27 | default | The only winning move is not to play4. | + When I run jekyll build + Then the _site/page<exist> directory should exist + And the "_site/page<exist>/index.html" file should exist + And I should see "<posts>" in "_site/page<exist>/index.html" + And the "_site/page<not_exist>/index.html" file should not exist + + Examples: + | num | exist | posts | not_exist | + | 1 | 4 | 1 | 5 | + | 2 | 2 | 2 | 3 | + | 3 | 2 | 1 | 3 | + + Scenario Outline: Setting a custom pagination path + Given I have a configuration file with: + | key | value | + | paginate | 1 | + | paginate_path | /blog/page-:num | + | permalink | /blog/:year/:month/:day/:title | + | plugins | [jekyll-paginate] | + And I have a blog directory + And I have an "blog/index.html" page that contains "{{ paginator.posts.size }}" + And I have a _posts directory + And I have the following posts: + | title | date | layout | content | + | Wargames | 2009-03-27 | default | The only winning move is not to play. | + | Wargames2 | 2009-04-27 | default | The only winning move is not to play2. | + | Wargames3 | 2009-05-27 | default | The only winning move is not to play3. | + | Wargames4 | 2009-06-27 | default | The only winning move is not to play4. | + When I run jekyll build + Then the _site/blog/page-<exist> directory should exist + And the "_site/blog/page-<exist>/index.html" file should exist + And I should see "<posts>" in "_site/blog/page-<exist>/index.html" + And the "_site/blog/page-<not_exist>/index.html" file should not exist + + Examples: + | exist | posts | not_exist | + | 2 | 1 | 5 | + | 3 | 1 | 6 | + | 4 | 1 | 7 | + + Scenario Outline: Setting a custom pagination path without an index.html in it + Given I have a configuration file with: + | key | value | + | paginate | 1 | + | paginate_path | /blog/page/:num | + | permalink | /blog/:year/:month/:day/:title | + | plugins | [jekyll-paginate] | + And I have a blog directory + And I have an "blog/index.html" page that contains "{{ paginator.posts.size }}" + And I have an "index.html" page that contains "Don't pick me!" + And I have a _posts directory + And I have the following posts: + | title | date | layout | content | + | Wargames | 2009-03-27 | default | The only winning move is not to play. | + | Wargames2 | 2009-04-27 | default | The only winning move is not to play2. | + | Wargames3 | 2009-05-27 | default | The only winning move is not to play3. | + | Wargames4 | 2009-06-27 | default | The only winning move is not to play4. | + When I run jekyll build + Then the _site/blog/page/<exist> directory should exist + And the "_site/blog/page/<exist>/index.html" file should exist + And I should see "<posts>" in "_site/blog/page/<exist>/index.html" + And the "_site/blog/page/<not_exist>/index.html" file should not exist + + Examples: + | exist | posts | not_exist | + | 2 | 1 | 5 | + | 3 | 1 | 6 | + | 4 | 1 | 7 | diff --git a/features/permalinks.feature b/features/permalinks.feature new file mode 100644 index 0000000..86c2c94 --- /dev/null +++ b/features/permalinks.feature @@ -0,0 +1,198 @@ +Feature: Fancy permalinks + As a hacker who likes to blog + I want to be able to set permalinks + In order to make my blog URLs awesome + + Scenario: Use none permalink schema + Given I have a _posts directory + And I have the following post: + | title | date | content | + | None Permalink Schema | 2009-03-27 | Totally nothing. | + And I have a configuration file with "permalink" set to "none" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "Totally nothing." in "_site/none-permalink-schema.html" + + Scenario: Use pretty permalink schema + Given I have a _posts directory + And I have the following post: + | title | date | content | + | Pretty Permalink Schema | 2009-03-27 | Totally wordpress. | + And I have a configuration file with "permalink" set to "pretty" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "Totally wordpress." in "_site/2009/03/27/pretty-permalink-schema/index.html" + + Scenario: Use pretty permalink schema for pages + Given I have an "index.html" page that contains "Totally index" + And I have an "awesome.html" page that contains "Totally awesome" + And I have an "sitemap.xml" page that contains "Totally uhm, sitemap" + And I have a configuration file with "permalink" set to "pretty" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "Totally index" in "_site/index.html" + And I should see "Totally awesome" in "_site/awesome/index.html" + And I should see "Totally uhm, sitemap" in "_site/sitemap.xml" + + Scenario: Use custom permalink schema with prefix + Given I have a _posts directory + And I have the following post: + | title | category | date | content | + | Custom Permalink Schema | stuff | 2009-03-27 | Totally custom. | + And I have a configuration file with "permalink" set to "/blog/:year/:month/:day/:title/" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "Totally custom." in "_site/blog/2009/03/27/custom-permalink-schema/index.html" + + Scenario: Use custom permalink schema with category + Given I have a _posts directory + And I have the following post: + | title | category | date | content | + | Custom Permalink Schema | stuff | 2009-03-27 | Totally custom. | + And I have a configuration file with "permalink" set to "/:categories/:title.html" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "Totally custom." in "_site/stuff/custom-permalink-schema.html" + + Scenario: Use custom permalink schema with squished date + Given I have a _posts directory + And I have the following post: + | title | category | date | content | + | Custom Permalink Schema | stuff | 2009-03-27 | Totally custom. | + And I have a configuration file with "permalink" set to "/:month-:day-:year/:title.html" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "Totally custom." in "_site/03-27-2009/custom-permalink-schema.html" + + Scenario: Use custom permalink schema with date and time + Given I have a configuration file with: + | key | value | + | permalink | "/:year:month:day:hour:minute:second.html" | + | timezone | UTC | + And I have a _posts directory + And I have the following post: + | title | category | date | content | + | Custom Permalink Schema | stuff | 2009-03-27 22:31:07 | Totally custom. | + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "Totally custom." in "_site/20090327223107.html" + + Scenario: Use per-post permalink + Given I have a _posts directory + And I have the following post: + | title | date | permalink | content | + | Some post | 2013-04-14 | /custom/posts/1/ | bla bla | + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And the _site/custom/posts/1 directory should exist + And I should see "bla bla" in "_site/custom/posts/1/index.html" + + Scenario: Use per-post ending in .html + Given I have a _posts directory + And I have the following post: + | title | date | permalink | content | + | Some post | 2013-04-14 | /custom/posts/some.html | bla bla | + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And the _site/custom/posts directory should exist + And I should see "bla bla" in "_site/custom/posts/some.html" + + Scenario: Use pretty permalink schema with cased file name + Given I have a _posts directory + And I have an "_posts/2009-03-27-Pretty-Permalink-Schema.md" page that contains "Totally wordpress" + And I have a configuration file with "permalink" set to "pretty" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "Totally wordpress." in "_site/2009/03/27/Pretty-Permalink-Schema/index.html" + + Scenario: Use custom permalink schema with cased file name + Given I have a _posts directory + And I have an "_posts/2009-03-27-Custom-Schema.md" page with title "Custom Schema" that contains "Totally awesome" + And I have a configuration file with "permalink" set to "/:year/:month/:day/:slug/" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "Totally awesome" in "_site/2009/03/27/custom-schema/index.html" + + Scenario: Use pretty permalink schema with title containing underscore + Given I have a _posts directory + And I have an "_posts/2009-03-27-Custom_Schema.md" page with title "Custom Schema" that contains "Totally awesome" + And I have a configuration file with "permalink" set to "pretty" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "Totally awesome" in "_site/2009/03/27/Custom_Schema/index.html" + + Scenario: Use a non-HTML file extension in the permalink + Given I have a _posts directory + And I have an "_posts/2016-01-18-i-am-php.md" page with permalink "/2016/i-am-php.php" that contains "I am PHP" + And I have a "i-am-also-php.md" page with permalink "/i-am-also-php.php" that contains "I am also PHP" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "I am PHP" in "_site/2016/i-am-php.php" + And I should see "I am also PHP" in "_site/i-am-also-php.php" + + Scenario: Using the same permalink twice + Given I have a "cool.md" page with permalink "/amazing.html" that contains "I am cool" + And I have an "awesome.md" page with permalink "/amazing.html" that contains "I am also awesome" + And I have an "amazing.html" file with content: + """ + Hello World + I'm a static file + """ + And I have a "_config.yml" file with content: + """ + collections: + puppies: + output: true + permalink: /:collection/:year/:month/:day/:title:output_ext + """ + And I have a _puppies directory + And I have the following documents under the puppies collection: + | title | date | content | + | Rover | 2009-03-27 | content for Rover. | + And I have a _posts directory + And I have the following post: + | title | date | layout | category | content | + | Rover | 2009-03-27 | none | puppies | Luke, I am your father. | + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "Conflict: The following destination is shared by multiple files." in the build output + And I should see "_site/amazing.html" in the build output + And I should see "awesome.md" in the build output + And I should see "cool.md" in the build output + And I should see "amazing.html" in the build output + And I should see "_site/puppies/2009/03/27/rover.html" in the build output + And I should see "_posts/2009-03-27-rover.markdown" in the build output + And I should see "_puppies/rover.md" in the build output + + Scenario: Redirecting from an existing permalink + Given I have a configuration file with "plugins" set to "[jekyll-redirect-from]" + And I have a "deals.html" file with content: + """ + --- + permalink: /deals/ + redirect_from: + - /offers/ + --- + """ + And I have a "offers.html" page with permalink "/offers/" that contains "Hurry! Limited time only!" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should not see "Conflict: The following destination is shared by multiple files." in the build output + And I should not see "_site/offers/index.html" in the build output + And I should not see "offers.html" in the build output + And I should not see "redirect.html" in the build output diff --git a/features/plugins.feature b/features/plugins.feature new file mode 100644 index 0000000..e903783 --- /dev/null +++ b/features/plugins.feature @@ -0,0 +1,37 @@ +Feature: Configuring and using plugins + As a hacker + I want to specify my own plugins that can modify Jekyll's behaviour + + Scenario: Add a gem-based plugin + Given I have an "index.html" file that contains "Whatever" + And I have a configuration file with "plugins" set to "[jekyll_test_plugin]" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "Whatever" in "_site/index.html" + And I should see "this is a test" in "_site/test.txt" + + Scenario: Add an empty whitelist to restrict all gems + Given I have an "index.html" file that contains "Whatever" + And I have a configuration file with: + | key | value | + | plugins | [jekyll_test_plugin] | + | whitelist | [] | + When I run jekyll build --safe + Then I should get a zero exit status + And the _site directory should exist + And I should see "Whatever" in "_site/index.html" + And the "_site/test.txt" file should not exist + + Scenario: Add a whitelist to restrict some gems but allow others + Given I have an "index.html" file that contains "Whatever" + And I have a configuration file with: + | key | value | + | plugins | [jekyll_test_plugin, jekyll_test_plugin_malicious] | + | whitelist | [jekyll_test_plugin] | + When I run jekyll build --safe + Then I should get a zero exit status + And the _site directory should exist + And I should see "Whatever" in "_site/index.html" + And the "_site/test.txt" file should exist + And I should see "this is a test" in "_site/test.txt" diff --git a/features/post_data.feature b/features/post_data.feature new file mode 100644 index 0000000..e807f9b --- /dev/null +++ b/features/post_data.feature @@ -0,0 +1,428 @@ +Feature: Post data + As a hacker who likes to blog + I want to be able to embed data into my posts + In order to make the posts slightly dynamic + + Scenario: Use post.title variable + Given I have a _posts directory + And I have a _layouts directory + And I have the following post: + | title | date | layout | content | + | Star Wars | 2009-03-27 | simple | Luke, I am your father. | + And I have a simple layout that contains "Post title: {{ page.title }}" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "Post title: Star Wars" in "_site/2009/03/27/star-wars.html" + + Scenario: Use post.url variable + Given I have a _posts directory + And I have a _layouts directory + And I have the following post: + | title | date | layout | content | + | Star Wars | 2009-03-27 | simple | Luke, I am your father. | + And I have a simple layout that contains "Post url: {{ page.url }}" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "Post url: /2009/03/27/star-wars.html" in "_site/2009/03/27/star-wars.html" + + Scenario: Use page.name variable + Given I have a _posts directory + And I have a _layouts directory + And I have the following post: + | title | date | layout | content | + | Star Wars | 2009-03-27 | simple | Luke, I am your father. | + And I have a simple layout that contains "Page name: {{ page.name }}" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "Page name: 2009-03-27-star-wars.markdown" in "_site/2009/03/27/star-wars.html" + + Scenario: Use post.date variable + Given I have a _posts directory + And I have a _layouts directory + And I have the following post: + | title | date | layout | content | + | Star Wars | 2009-03-27 | simple | Luke, I am your father. | + And I have a simple layout that contains "Post date: {{ page.date | date_to_string }}" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "Post date: 27 Mar 2009" in "_site/2009/03/27/star-wars.html" + + Scenario: Use post.date variable with invalid + Given I have a _posts directory + And I have a "_posts/2016-01-01-test.md" page with date "tuesday" that contains "I have a bad date." + When I run jekyll build + Then the _site directory should not exist + And I should see "Document '_posts/2016-01-01-test.md' does not have a valid date in the YAML front matter." in the build output + + Scenario: Invalid date in filename + Given I have a _posts directory + And I have a "_posts/2016-22-01-test.md" page that contains "I have a bad date." + When I run jekyll build + Then the _site directory should not exist + And I should see "Document '_posts/2016-22-01-test.md' does not have a valid date in the filename." in the build output + + Scenario: Use post.id variable + Given I have a _posts directory + And I have a _layouts directory + And I have the following post: + | title | date | layout | content | + | Star Wars | 2009-03-27 | simple | Luke, I am your father. | + And I have a simple layout that contains "Post id: {{ page.id }}" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "Post id: /2009/03/27/star-wars" in "_site/2009/03/27/star-wars.html" + + Scenario: Use post.content variable + Given I have a _posts directory + And I have a _layouts directory + And I have the following post: + | title | date | layout | content | + | Star Wars | 2009-03-27 | simple | Luke, I am your father. | + And I have a simple layout that contains "Post content: {{ content }}" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "Post content: <p>Luke, I am your father.</p>" in "_site/2009/03/27/star-wars.html" + + Scenario: Use post.categories variable when category is in a folder + Given I have a movies directory + And I have a movies/_posts directory + And I have a _layouts directory + And I have the following post in "movies": + | title | date | layout | content | + | Star Wars | 2009-03-27 | simple | Luke, I am your father. | + And I have a simple layout that contains "Post category: {{ page.categories }}" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "Post category: movies" in "_site/movies/2009/03/27/star-wars.html" + + Scenario: Use post.categories variable when category is in a folder and has category in YAML + Given I have a movies directory + And I have a movies/_posts directory + And I have a _layouts directory + And I have the following post in "movies": + | title | date | layout | category | content | + | Star Wars | 2009-03-27 | simple | film | Luke, I am your father. | + And I have a simple layout that contains "Post category: {{ page.categories }}" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "Post category: movies" in "_site/movies/film/2009/03/27/star-wars.html" + + Scenario: Use post.categories variable when category is in a folder and has categories in YAML + Given I have a movies directory + And I have a movies/_posts directory + And I have a _layouts directory + And I have the following post in "movies": + | title | date | layout | categories | content | + | Star Wars | 2009-03-27 | simple | [film, scifi] | Luke, I am your father. | + And I have a simple layout that contains "Post category: {{ page.categories }}" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "Post category: movies" in "_site/movies/film/scifi/2009/03/27/star-wars.html" + + Scenario: Use post.categories variable when category is in a folder and duplicated category is in YAML + Given I have a movies directory + And I have a movies/_posts directory + And I have a _layouts directory + And I have the following post in "movies": + | title | date | layout | category | content | + | Star Wars | 2009-03-27 | simple | movies | Luke, I am your father. | + And I have a simple layout that contains "Post category: {{ page.categories }}" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "Post category: movies" in "_site/movies/2009/03/27/star-wars.html" + + Scenario: Use post.categories when category is a composite of multiple words + Given I have a Sci-Fi Movi3s directory + And I have a Sci-Fi Movi3s/_posts directory + And I have a _layouts directory + And I have the following post in "Sci-Fi Movi3s": + | title | date | layout | category | content | + | Star Wars | 2020-04-03 | simple | vintage | Luke, I am your father. | + And I have a "_layouts/simple.html" file with content: + """ + Post categories: {{ page.categories | join: ', ' }} + Post URL: {{ page.url }} + """ + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "Post categories: Sci-Fi Movi3s, vintage" in "_site/sci-fi movi3s/vintage/2020/04/03/star-wars.html" + And I should see "Post URL: /sci-fi%20movi3s/vintage/2020/04/03/star-wars.html" in "_site/sci-fi movi3s/vintage/2020/04/03/star-wars.html" + + Scenario: Use post.slugified_categories to generate URL when category is a composite of multiple words + Given I have a Sci-Fi Movi3s directory + And I have a Sci-Fi Movi3s/_posts directory + And I have a _layouts directory + And I have the following post in "Sci-Fi Movi3s": + | title | date | layout | category | content | + | Star Wars | 2020-04-03 | simple | vintage | Luke, I am your father. | + And I have a "_layouts/simple.html" file with content: + """ + Post categories: {{ page.categories | join: ', ' }} + Post URL: {{ page.url }} + """ + And I have a "_config.yml" file with content: + """ + collections: + posts: + permalink: "/:slugified_categories/:year/:month/:day/:title:output_ext" + """ + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "Post categories: Sci-Fi Movi3s, vintage" in "_site/sci-fi-movi3s/vintage/2020/04/03/star-wars.html" + And I should see "Post URL: /sci-fi-movi3s/vintage/2020/04/03/star-wars.html" in "_site/sci-fi-movi3s/vintage/2020/04/03/star-wars.html" + + Scenario: Use post.tags variable + Given I have a _posts directory + And I have a _layouts directory + And I have the following post: + | title | date | layout | tag | content | + | Star Wars | 2009-05-18 | simple | twist | Luke, I am your father. | + And I have a simple layout that contains "Post tags: {{ page.tags }}" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "Post tags: twist" in "_site/2009/05/18/star-wars.html" + + Scenario: Use post.categories variable when categories are in folders + Given I have a scifi directory + And I have a scifi/movies directory + And I have a scifi/movies/_posts directory + And I have a _layouts directory + And I have the following post in "scifi/movies": + | title | date | layout | content | + | Star Wars | 2009-03-27 | simple | Luke, I am your father. | + And I have a simple layout that contains "Post categories: {{ page.categories | array_to_sentence_string }}" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "Post categories: scifi and movies" in "_site/scifi/movies/2009/03/27/star-wars.html" + + Scenario: Use post.categories variable when categories are in folders with mixed case + Given I have a scifi directory + And I have a scifi/Movies directory + And I have a scifi/Movies/_posts directory + And I have a _layouts directory + And I have the following post in "scifi/Movies": + | title | date | layout | content | + | Star Wars | 2009-03-27 | simple | Luke, I am your father. | + And I have a simple layout that contains "Post categories: {{ page.categories | array_to_sentence_string }}" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "Post categories: scifi and Movies" in "_site/scifi/movies/2009/03/27/star-wars.html" + + Scenario: Use post.categories variable when category is in YAML + Given I have a _posts directory + And I have a _layouts directory + And I have the following post: + | title | date | layout | category | content | + | Star Wars | 2009-03-27 | simple | movies | Luke, I am your father. | + And I have a simple layout that contains "Post category: {{ page.categories }}" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "Post category: movies" in "_site/movies/2009/03/27/star-wars.html" + + Scenario: Use post.categories variable when category is in YAML and is mixed-case + Given I have a _posts directory + And I have a _layouts directory + And I have the following post: + | title | date | layout | category | content | + | Star Wars | 2009-03-27 | simple | Movies | Luke, I am your father. | + And I have a simple layout that contains "Post category: {{ page.categories }}" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "Post category: Movies" in "_site/movies/2009/03/27/star-wars.html" + + Scenario: Use post.categories variable when categories are in YAML + Given I have a _posts directory + And I have a _layouts directory + And I have the following post: + | title | date | layout | categories | content | + | Star Wars | 2009-03-27 | simple | ['scifi', 'movies'] | Luke, I am your father. | + And I have a simple layout that contains "Post categories: {{ page.categories | array_to_sentence_string }}" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "Post categories: scifi and movies" in "_site/scifi/movies/2009/03/27/star-wars.html" + + Scenario: Use post.categories variable when categories are in YAML and are duplicated + Given I have a _posts directory + And I have a _layouts directory + And I have the following post: + | title | date | layout | categories | content | + | Star Wars | 2009-03-27 | simple | ['movies', 'movies'] | Luke, I am your father. | + And I have a simple layout that contains "Post category: {{ page.categories }}" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "Post category: movies" in "_site/movies/2009/03/27/star-wars.html" + + Scenario: Superdirectories of _posts applied to post.categories + Given I have a movies/_posts directory + And I have a "movies/_posts/2009-03-27-star-wars.html" page with layout "simple" that contains "hi" + And I have a _layouts directory + And I have a simple layout that contains "Post category: {{ page.categories }}" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "Post category: movies" in "_site/movies/2009/03/27/star-wars.html" + + Scenario: Subdirectories of _posts not applied to post.categories + Given I have a movies/_posts/scifi directory + And I have a "movies/_posts/scifi/2009-03-27-star-wars.html" page with layout "simple" that contains "hi" + And I have a _layouts directory + And I have a simple layout that contains "Post category: {{ page.categories }}" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "Post category: movies" in "_site/movies/2009/03/27/star-wars.html" + + Scenario: Use post.categories variable when categories are in YAML with mixed case + Given I have a _posts directory + And I have a _layouts directory + And I have the following posts: + | title | date | layout | categories | content | + | Star Wars | 2009-03-27 | simple | ['scifi', 'Movies'] | Luke, I am your father. | + | Star Trek | 2013-03-17 | simple | ['SciFi', 'movies'] | Jean Luc, I am your father. | + And I have a simple layout that contains "Post categories: {{ page.categories | array_to_sentence_string }}" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "Post categories: scifi and Movies" in "_site/scifi/movies/2009/03/27/star-wars.html" + And I should see "Post categories: SciFi and movies" in "_site/scifi/movies/2013/03/17/star-trek.html" + +Scenario: Use page.render_with_liquid variable + Given I have a _posts directory + And I have the following posts: + | title | render_with_liquid | date | content | + | Unrendered Post | false | 2017-07-06 | Hello {{ page.title }} | + | Rendered Post | true | 2017-07-06 | Hello {{ page.title }} | + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should not see "Hello Unrendered Post" in "_site/2017/07/06/unrendered-post.html" + But I should see "Hello {{ page.title }}" in "_site/2017/07/06/unrendered-post.html" + And I should see "Hello Rendered Post" in "_site/2017/07/06/rendered-post.html" + + Scenario Outline: Use page.path variable + Given I have a <dir>/_posts directory + And I have the following post in "<dir>": + | title | type | date | content | + | my-post | html | 2013-04-12 | Source path: {{ page.path }} | + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "Source path: <path_prefix>_posts/2013-04-12-my-post.html" in "_site/<dir>/2013/04/12/my-post.html" + + Examples: + | dir | path_prefix | + | . | | + | dir | dir/ | + | dir/nested | dir/nested/ | + + Scenario: Cannot override page.path variable + Given I have a _posts directory + And I have the following post: + | title | date | path | content | + | override | 2013-04-12 | override-path.html | Non-custom path: {{ page.path }} | + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "Non-custom path: _posts/2013-04-12-override.markdown" in "_site/2013/04/12/override.html" + + Scenario: Disable a post from being published + Given I have a _posts directory + And I have an "index.html" file that contains "Published!" + And I have the following post: + | title | date | layout | published | content | + | Star Wars | 2009-03-27 | simple | false | Luke, I am your father. | + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And the "_site/2009/03/27/star-wars.html" file should not exist + And I should see "Published!" in "_site/index.html" + + Scenario: Use a custom variable + Given I have a _posts directory + And I have a _layouts directory + And I have the following post: + | title | date | layout | author | content | + | Star Wars | 2009-03-27 | simple | Darth Vader | Luke, I am your father. | + And I have a simple layout that contains "Post author: {{ page.author }}" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "Post author: Darth Vader" in "_site/2009/03/27/star-wars.html" + + Scenario: Use a variable which is a reserved keyword in Ruby + Given I have a _posts directory + And I have a _layouts directory + And I have the following post: + | title | date | layout | class | content | + | My post | 2016-01-21 | simple | kewl-post | Luke, I am your father. | + And I have a simple layout that contains "{{page.title}} has class {{page.class}}" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "My post has class kewl-post" in "_site/2016/01/21/my-post.html" + + Scenario: Previous and next posts title + Given I have a _posts directory + And I have a _layouts directory + And I have the following posts: + | title | date | layout | author | content | + | Star Wars | 2009-03-27 | ordered | Darth Vader | Luke, I am your father. | + | Some like it hot | 2009-04-27 | ordered | Osgood | Nobody is perfect. | + | Terminator | 2009-05-27 | ordered | Arnold | Sayonara, baby | + And I have a ordered layout that contains "Previous post: {{ page.previous.title }} and next post: {{ page.next.title }}" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "next post: Some like it hot" in "_site/2009/03/27/star-wars.html" + And I should see "Previous post: Some like it hot" in "_site/2009/05/27/terminator.html" + + Scenario: Deprecate calling data keys directly via Ruby + Given I have a _posts directory + And I have a _plugins directory + And I have the following post: + | title | date | content | + | My post | 2016-01-21 | Luke, I am your father. | + And I have a "_plugins/foo.rb" file with content: + """ + Jekyll::Hooks.register :documents, :pre_render do |doc| + doc.title + end + """ + And I have a "_plugins/bar.rb" file with content: + """ + module FooBar + def self.dummy?(doc) + doc.title == "Dummy Document" + end + end + + Jekyll::Hooks.register :documents, :post_render do |doc| + FooBar.dummy?(doc) + end + """ + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "Deprecation: Document#title" in the build output + And I should see "_plugins/foo.rb:2" in the build output + And I should see "_plugins/bar.rb:3" in the build output + But I should not see "lib/jekyll/document.rb" in the build output diff --git a/features/post_excerpts.feature b/features/post_excerpts.feature new file mode 100644 index 0000000..a145d36 --- /dev/null +++ b/features/post_excerpts.feature @@ -0,0 +1,114 @@ +Feature: Post excerpts + As a hacker who likes to blog + I want to be able to make a static site + In order to share my awesome ideas with the interwebs + But some people can only focus for a few moments + So just give them a taste + + Scenario: An excerpt without a layout + Given I have an "index.html" page that contains "{% for post in site.posts %}{{ post.excerpt }}{% endfor %}" + And I have a _posts directory + And I have the following posts: + | title | date | layout | content | + | entry1 | 2007-12-31 | post | content for entry1. | + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see exactly "<p>content for entry1.</p>" in "_site/index.html" + + Scenario: An excerpt from a post with a layout + Given I have an "index.html" page that contains "{% for post in site.posts %}{{ post.excerpt }}{% endfor %}" + And I have a _posts directory + And I have a _layouts directory + And I have a post layout that contains "{{ page.excerpt }}" + And I have the following posts: + | title | date | layout | content | + | entry1 | 2007-12-31 | post | content for entry1. | + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And the _site/2007 directory should exist + And the _site/2007/12 directory should exist + And the _site/2007/12/31 directory should exist + And the "_site/2007/12/31/entry1.html" file should exist + And I should see exactly "<p>content for entry1.</p>" in "_site/2007/12/31/entry1.html" + And I should see exactly "<p>content for entry1.</p>" in "_site/index.html" + + Scenario: An excerpt with Liquid constructs from a post with a layout + Given I have an "index.html" page that contains "{% for post in site.posts %}{{ post.excerpt }}{% endfor %}" + And I have a configuration file with "baseurl" set to "/blog" + And I have a _posts directory + And I have a _layouts directory + And I have a post layout that contains "{{ page.excerpt }}" + And I have the following posts: + | title | date | layout | content | + | entry1 | 2007-12-31 | post | {{ 'assets/style.css' \| relative_url }} | + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And the _site/2007 directory should exist + And the _site/2007/12 directory should exist + And the _site/2007/12/31 directory should exist + And the "_site/2007/12/31/entry1.html" file should exist + And I should see exactly "<p>/blog/assets/style.css</p>" in "_site/2007/12/31/entry1.html" + And I should see exactly "<p>/blog/assets/style.css</p>" in "_site/index.html" + + Scenario: An excerpt from a post with a layout which has context + Given I have an "index.html" page that contains "{% for post in site.posts %}{{ post.excerpt }}{% endfor %}" + And I have a _posts directory + And I have a _layouts directory + And I have a post layout that contains "<html><head></head><body>{{ page.excerpt }}</body></html>" + And I have the following posts: + | title | date | layout | content | + | entry1 | 2007-12-31 | post | content for entry1. | + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And the _site/2007 directory should exist + And the _site/2007/12 directory should exist + And the _site/2007/12/31 directory should exist + And the "_site/2007/12/31/entry1.html" file should exist + And I should see "<p>content for entry1.</p>" in "_site/index.html" + And I should see "<html><head></head><body><p>content for entry1.</p>\n</body></html>" in "_site/2007/12/31/entry1.html" + + Scenario: Excerpts from posts having 'render_with_liquid' in their front matter + Given I have an "index.html" page that contains "{% for post in site.posts %}{{ post.excerpt }}{% endfor %}" + And I have a _posts directory + And I have a _layouts directory + And I have a post layout that contains "{{ page.excerpt }}" + And I have the following posts: + | title | layout | render_with_liquid | date | content | + | Unrendered Post | post | false | 2017-07-06 | Liquid is not rendered at {{ page.url }} | + | Rendered Post | post | true | 2017-07-06 | Liquid is rendered at {{ page.url }} | + When I run jekyll build + Then I should get a zero exit status + And the _site/2017/07/06 directory should exist + And the "_site/2017/07/06/unrendered-post.html" file should exist + And the "_site/2017/07/06/rendered-post.html" file should exist + And I should see "Liquid is not rendered at {{ page.url }}" in "_site/2017/07/06/unrendered-post.html" + But I should see "<p>Liquid is rendered at /2017/07/06/rendered-post.html</p>" in "_site/2017/07/06/rendered-post.html" + And I should see "<p>Liquid is not rendered at {{ page.url }}</p>\n<p>Liquid is rendered at /2017/07/06/rendered-post.html</p>" in "_site/index.html" + + Scenario: Excerpts from posts with reference-style Markdown links + Given I have a configuration file with: + | key | value | + | permalink | "/:title:output_ext" | + | kramdown | { show_warnings: true } | + And I have an "index.html" page that contains "{% for post in site.posts %}{{ post.excerpt }}{% endfor %}" + And I have a _layouts directory + And I have a post layout that contains "{{ page.excerpt }}" + And I have a _posts directory + And I have the following posts: + | title | layout | date | content | + | Just Text Excerpt | post | 2019-03-06 | Install Jekyll\n\nNext Para [^1]\n\n[^1]: Lorem ipsum | + | Text and Footnote | post | 2019-03-07 | Alpha [^1]\n\nNext Para\n\n[^1]: Omega sigma | + | Text and Reference Link | post | 2019-03-08 | Read [docs][link]\n\nNext Para\n\n[link]: docs.jekyll.com | + | Text and Self-referencing Link | post | 2019-03-09 | Check out [jekyll]\n\nNext Para\n\n[jekyll]: jekyllrb.com | + When I run jekyll build + Then I should get a zero exit status + And I should not see "Kramdown warning" in the build output + But I should see exactly "<p>Install Jekyll</p>" in "_site/just-text-excerpt.html" + And I should see "<p>Alpha <sup id=\"fnref:1\" role=\"doc-noteref\"><a href=\"#fn:1\" class=\"footnote\" rel=\"footnote\">1</a></sup></p>" in "_site/text-and-footnote.html" + And I should see "<p>Omega sigma <a href=\"#fnref:1\" class=\"reversefootnote\" role=\"doc-backlink\">↩</a></p>" in "_site/text-and-footnote.html" + And I should see "<p>Read <a href=\"docs.jekyll.com\">docs</a></p>" in "_site/text-and-reference-link.html" + And I should see "<p>Check out <a href=\"jekyllrb.com\">jekyll</a></p>" in "_site/text-and-self-referencing-link.html" diff --git a/features/post_url_tag.feature b/features/post_url_tag.feature new file mode 100644 index 0000000..ee59b71 --- /dev/null +++ b/features/post_url_tag.feature @@ -0,0 +1,159 @@ +Feature: PostUrl Tag + As a blogger who likes to write a variety of content + I want to be able to link to posts easily + And render them without much hassle + + Scenario: A site that is using the defaults for permalink + Given I have a _posts directory + And I have the following post: + | title | date | content | + | Hello World | 2019-02-04 | Lorem ipsum dolor | + And I have an "index.md" page that contains "[Welcome]({% post_url 2019-02-04-hello-world %})" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "<p><a href=\"/2019/02/04/hello-world.html\">Welcome</a></p>" in "_site/index.html" + + Scenario: Site with site-wide custom permalink setting + Given I have a _posts directory + And I have the following posts: + | title | date | content | + | Hello World | 2019-02-04 | Lorem ipsum dolor | + | We Meet Again | 2019-02-05 | Alpha beta gamma | + And I have a configuration file with "permalink" set to "/:title:output_ext" + And I have an "index.md" page that contains "[Welcome]({% post_url 2019-02-04-hello-world %})" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "<p><a href=\"/hello-world.html\">Welcome</a></p>" in "_site/index.html" + + Scenario: Site with custom permalink settings on each post + Given I have a _posts directory + And I have the following posts: + | title | date | permalink | content | + | Hello World | 2019-02-04 | "/2019/hello-world/" | Lorem ipsum dolor | + | We Meet Again | 2019-02-05 | "/2019/second-meeting/" | Alpha beta gamma | + And I have a configuration file with "permalink" set to "/:title:output_ext" + And I have an "index.md" page that contains "[Welcome]({% post_url 2019-02-04-hello-world %})" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "<p><a href=\"/2019/hello-world/\">Welcome</a></p>" in "_site/index.html" + + Scenario: Site with no posts + Given I have an "index.md" page that contains "[Welcome]({% post_url 2019-02-04-hello-world %})" + When I run jekyll build + Then I should get a non-zero exit status + And the _site directory should not exist + But I should see "Could not find post \"2019-02-04-hello-world\" in tag 'post_url'." in the build output + + Scenario: Site with a future-dated post + Given I have a _posts directory + And I have the following posts: + | title | date | content | + | Hello World | 2019-02-04 | Lorem ipsum dolor | + | We Meet Again | 2119-02-04 | Alpha beta gamma | + And I have a configuration file with "permalink" set to "/:title:output_ext" + And I have an "index.md" page that contains "[Welcome Again]({% post_url 2119-02-04-we-meet-again %})" + When I run jekyll build --future + Then I should get a zero exit status + And the _site directory should exist + And I should see "<p><a href=\"/we-meet-again.html\">Welcome Again</a></p>" in "_site/index.html" + + Scenario: Site with configured baseurl + Given I have a _posts directory + And I have the following posts: + | title | date | content | + | Hello World | 2019-02-04 | Lorem ipsum dolor | + | We Meet Again | 2019-02-05 | Alpha beta gamma | + And I have a configuration file with "baseurl" set to "blog" + And I have an "index.md" page that contains "[Welcome]({% post_url 2019-02-04-hello-world %})" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "<p><a href=\"/blog/2019/02/04/hello-world.html\">Welcome</a></p>" in "_site/index.html" + + Scenario: Posts with categories + Given I have a _posts directory + And I have the following post: + | title | date | content | + | Hello World | 2019-02-04 | Lorem ipsum dolor | + And I have a movies/_posts directory + And I have the following post in "movies": + | title | date | content | + | Hello Movies | 2019-02-05 | Lorem ipsum dolor | + And I have the following post in "movies": + | title | date | category | content | + | Star Wars | 2019-02-06 | film | Luke, I am your father | + And I have an "index.md" page with content: + """ + [Welcome]({% post_url 2019-02-04-hello-world %}) + + [Movies]({% post_url movies/2019-02-05-hello-movies %}) + + [Film]({% post_url movies/2019-02-06-star-wars %}) + """ + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "<p><a href=\"/2019/02/04/hello-world.html\">Welcome</a></p>" in "_site/index.html" + And I should see "<p><a href=\"/movies/2019/02/05/hello-movies.html\">Movies</a></p>" in "_site/index.html" + And I should see "<p><a href=\"/movies/film/2019/02/06/star-wars.html\">Film</a></p>" in "_site/index.html" + + Scenario: Duplicate posts with categories + Given I have a _posts directory + And I have the following post: + | title | date | content | + | Hello World | 2019-02-04 | Lorem ipsum dolor | + And I have a movies/_posts directory + And I have the following post in "movies": + | title | date | content | + | Hello World | 2019-02-04 | Lorem ipsum dolor | + And I have an "index.md" page with content: + """ + [Welcome]({% post_url 2019-02-04-hello-world %}) + + [Movies]({% post_url movies/2019-02-04-hello-world %}) + """ + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "<p><a href=\"/2019/02/04/hello-world.html\">Welcome</a></p>" in "_site/index.html" + And I should see "<p><a href=\"/movies/2019/02/04/hello-world.html\">Movies</a></p>" in "_site/index.html" + + Scenario: Deprecated usage to link nested post + Given I have a movies/_posts directory + And I have the following post in "movies": + | title | date | content | + | Hello World | 2019-02-04 | Lorem ipsum dolor | + And I have an "index.md" page that contains "[Movies]({% post_url 2019-02-04-hello-world %})" + When I run jekyll build + Then I should get a zero exit status + And I should see "Deprecation: A call to '{% post_url 2019-02-04-hello-world %}' did not match a post" in the build output + But the _site directory should exist + And I should see "<p><a href=\"/movies/2019/02/04/hello-world.html\">Movies</a></p>" in "_site/index.html" + + Scenario: Nested posts in a directory with name containing spaces + Given I have a Cats and Dogs/_posts directory + And I have the following post in "Cats and Dogs": + | title | date | content | + | Hello World | 2019-02-04 | Lorem ipsum dolor | + And I have a _posts/Salt and Pepper directory + And I have the following post under "Salt and Pepper": + | title | date | content | + | Hello Again | 2019-02-05 | Lorem ipsum dolor | + And I have an "index.md" file with content: + """ + --- + --- + + [Post 1]({% post_url Cats and Dogs/2019-02-04-hello-world %}) + + [Post 2]({% post_url Salt and Pepper/2019-02-05-hello-again %}) + """ + When I run jekyll build + Then I should get a zero exit status + And I should not see "Deprecation: A call to '{% post_url" in the build output + But the _site directory should exist + And I should see "<p><a href=\"/cats%20and%20dogs/2019/02/04/hello-world.html\">Post 1</a></p>" in "_site/index.html" + And I should see "<p><a href=\"/2019/02/05/hello-again.html\">Post 2</a></p>" in "_site/index.html" diff --git a/features/rendering.feature b/features/rendering.feature new file mode 100644 index 0000000..2b504a0 --- /dev/null +++ b/features/rendering.feature @@ -0,0 +1,241 @@ +Feature: Rendering + As a hacker who likes to blog + I want to be able to make a static site + In order to share my awesome ideas with the interwebs + But I want to make it as simply as possible + So render with Liquid and place in Layouts + + Scenario: Rendering a site with parentheses in its path name + Given I have a blank site in "omega(beta)" + And I have an "omega(beta)/test.md" page with layout "simple" that contains "Hello World" + And I have an omega(beta)/_includes directory + And I have an "omega(beta)/_includes/head.html" file that contains "Snippet" + And I have a configuration file with "source" set to "omega(beta)" + And I have an omega(beta)/_layouts directory + And I have an "omega(beta)/_layouts/simple.html" file that contains "{% include head.html %}: {{ content }}" + When I run jekyll build --profile + Then I should get a zero exit status + And I should see "Snippet: <p>Hello World</p>" in "_site/test.html" + And I should see "_layouts/simple.html" in the build output + + Scenario: When receiving bad Liquid + Given I have a "index.html" page with layout "simple" that contains "{% include invalid.html %}" + And I have a simple layout that contains "{{ content }}" + When I run jekyll build + Then I should get a non-zero exit-status + And I should see "Liquid Exception" in the build output + + Scenario: When receiving a liquid syntax error in included file + Given I have a _includes directory + And I have a "_includes/invalid.html" file that contains "{% INVALID %}" + And I have a "index.html" page with layout "simple" that contains "{% include invalid.html %}" + And I have a simple layout that contains "{{ content }}" + When I run jekyll build + Then I should get a non-zero exit-status + And I should see "Liquid Exception: Liquid syntax error \(.+/invalid\.html line 1\): Unknown tag 'INVALID' included in index\.html" in the build output + + Scenario: When receiving a generic liquid error in included file + Given I have a _includes directory + And I have a "_includes/invalid.html" file that contains "{{ site.title | prepend 'Prepended Text' }}" + And I have a "index.html" page with layout "simple" that contains "{% include invalid.html %}" + And I have a simple layout that contains "{{ content }}" + When I run jekyll build + Then I should get a non-zero exit-status + And I should see "Liquid Exception: Liquid error \(.+/_includes/invalid\.html line 1\): wrong number of arguments (\(given 1, expected 2\)|\(1 for 2\)) included in index\.html" in the build output + + Scenario: Rendering a default site containing a file with rogue Liquid constructs + Given I have a "index.html" page with title "Simple Test" that contains "{{ page.title | foobar }}\n\n{{ page.author }}" + When I run jekyll build + Then I should get a zero exit-status + And I should not see "Liquid Exception:" in the build output + + Scenario: Rendering a default site containing a file with a non-existent Liquid variable + Given I have a "index.html" file with content: + """ + --- + title: Simple Test + --- + {{ site.lorem.ipsum }} + {{ site.title }} + """ + And I have a configuration file with "title" set to "Hello World" + When I run jekyll build + Then I should get a zero exit-status + And the _site directory should exist + + Scenario: Rendering a custom site containing a file with a non-existent Liquid variable + Given I have a "index.html" file with content: + """ + --- + title: Simple Test + --- + {{ page.title }} + + {{ page.author }} + """ + And I have a "_config.yml" file with content: + """ + liquid: + strict_variables: true + """ + When I run jekyll build + Then I should get a non-zero exit-status + And I should see "Liquid error \(line 3\): undefined variable author in index.html" in the build output + + Scenario: Rendering a custom site containing a file with a non-existent Liquid filter + Given I have a "index.html" file with content: + """ + --- + author: John Doe + --- + {{ page.title }} + + {{ page.author | foobar }} + """ + And I have a "_config.yml" file with content: + """ + liquid: + strict_filters: true + """ + When I run jekyll build + Then I should get a non-zero exit-status + And I should see "Liquid error \(line 3\): undefined filter foobar in index.html" in the build output + + Scenario: Render Liquid and place in layout + Given I have a "index.html" page with layout "simple" that contains "Hi there, Jekyll {{ jekyll.environment }}!" + And I have a simple layout that contains "{{ content }}Ahoy, indeed!" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "Hi there, Jekyll development!\nAhoy, indeed" in "_site/index.html" + + Scenario: Don't place asset files in layout + Given I have an "index.scss" page with layout "simple" that contains ".foo-bar { color:black; }" + And I have an "index.coffee" page with layout "simple" that contains "whatever()" + And I have a configuration file with "plugins" set to "[jekyll-coffeescript]" + And I have a simple layout that contains "{{ content }}Ahoy, indeed!" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should not see "Ahoy, indeed!" in "_site/index.css" + And I should not see "Ahoy, indeed!" in "_site/index.js" + + Scenario: Ignore defaults and don't place pages and documents with layout set to 'none' + Given I have a "index.md" page with layout "none" that contains "Hi there, {{ site.author }}!" + And I have a _trials directory + And I have a "_trials/no-layout.md" page with layout "none" that contains "Hi there, {{ site.author }}!" + And I have a "_trials/test.md" page with layout "null" that contains "Hi there, {{ site.author }}!" + And I have a none layout that contains "{{ content }}Welcome!" + And I have a page layout that contains "{{ content }}Check this out!" + And I have a configuration file with: + | key | value | + | author | John Doe | + | collections | {trials: {output: true}} | + | defaults | [{scope: {path: ""}, values: {layout: page}}] | + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should not see "Welcome!" in "_site/trials/no-layout.html" + And I should not see "Check this out!" in "_site/trials/no-layout.html" + But I should see "Check this out!" in "_site/trials/test.html" + And I should see "Hi there, John Doe!" in "_site/index.html" + And I should not see "Welcome!" in "_site/index.html" + And I should not see "Build Warning:" in the build output + + Scenario: Don't place pages and documents with layout set to 'none' + Given I have a "index.md" page with layout "none" that contains "Hi there, {{ site.author }}!" + And I have a _trials directory + And I have a "_trials/no-layout.md" page with layout "none" that contains "Hi there, {{ site.author }}!" + And I have a "_trials/test.md" page with layout "page" that contains "Hi there, {{ site.author }}!" + And I have a none layout that contains "{{ content }}Welcome!" + And I have a page layout that contains "{{ content }}Check this out!" + And I have a configuration file with: + | key | value | + | author | John Doe | + | collections | {trials: {output: true}} | + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should not see "Welcome!" in "_site/trials/no-layout.html" + And I should not see "Welcome!" in "_site/index.html" + But I should see "Check this out!" in "_site/trials/test.html" + And I should see "Hi there, John Doe!" in "_site/index.html" + And I should not see "Build Warning:" in the build output + + Scenario: Render liquid in Sass + Given I have an "index.scss" page that contains ".foo-bar { color:{{site.color}}; }" + And I have a configuration file with "color" set to "red" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see ".foo-bar { color: red; }\n\n\/\*# sourceMappingURL=index.css.map \*\/" in "_site/index.css" + + Scenario: Not render liquid in CoffeeScript without explicitly including jekyll-coffeescript + Given I have an "index.coffee" page with animal "cicada" that contains "hey='for {{page.animal}}'" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And the "_site/index.js" file should not exist + + Scenario: Render liquid in CoffeeScript with jekyll-coffeescript enabled + Given I have an "index.coffee" page with animal "cicada" that contains "hey='for {{page.animal}}'" + And I have a configuration file with "plugins" set to "[jekyll-coffeescript]" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "hey = 'for cicada';" in "_site/index.js" + + Scenario: Rendering Liquid expressions that return strings containing Liquid expressions + Given I have an "index.md" file with content: + """ + --- + prequel: "{% link series/first-part.md %}" + sequel: "{% link series/last-part.md %}" + --- + + This is the second-part of the series named {{ site.novel }}. + The first part is at {{ page.prequel }}. + + Lorem ipsum + + {% capture sequel_link %}{{ page.sequel }}{% endcapture %} + The last part of the series can be read at {{ sequel_link }} + """ + And I have a configuration file with "novel" set to "'{{ site.title }}'" + When I run jekyll build + Then I should get a zero exit status + And I should see "series named {{ site.title }}" in "_site/index.html" + And I should see "{% link series/first-part.md %}" in "_site/index.html" + And I should see "{% link series/last-part.md %}" in "_site/index.html" + + Scenario: Render content of another page + Given I have an "index.md" page that contains "__Hello World__" + And I have an "about.md" page that contains "{{ page.name }}" + And I have a "test.json" file with content: + """ + --- + --- + + { + "hpages": [ + {%- for page in site.html_pages %} + { + "url" : {{ page.url | jsonify }}, + "name" : {{ page.name | jsonify }}, + "path" : {{ page.path | jsonify }}, + "title" : {{ page.title | jsonify }}, + "layout" : {{ page.layout | jsonify }}, + "content": {{ page.content | jsonify }}, + "excerpt": {{ page.excerpt | jsonify }} + }{% unless forloop.last %},{% endunless -%} + {% endfor %} + ] + } + """ + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + But I should not see "content\": \"{{ page.name }}" in "_site/test.json" + And I should not see "content\": \"__Hello World__" in "_site/test.json" + But I should see "content\": \"<p>about.md</p>" in "_site/test.json" + And I should see "content\": \"<p><strong>Hello World</strong></p>" in "_site/test.json" diff --git a/features/site_configuration.feature b/features/site_configuration.feature new file mode 100644 index 0000000..53d5b21 --- /dev/null +++ b/features/site_configuration.feature @@ -0,0 +1,362 @@ +Feature: Site configuration + As a hacker who likes to blog + I want to be able to configure jekyll + In order to make setting up a site easier + + Scenario: Change source directory + Given I have a blank site in "_sourcedir" + And I have an "_sourcedir/index.html" file that contains "Changing source directory" + And I have a configuration file with "source" set to "_sourcedir" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "Changing source directory" in "_site/index.html" + + Scenario: Change destination directory + Given I have an "index.html" file that contains "Changing destination directory" + And I have a configuration file with "destination" set to "_mysite" + When I run jekyll build + Then the _mysite directory should exist + And I should see "Changing destination directory" in "_mysite/index.html" + + Scenario Outline: Similarly named source and destination + Given I have a blank site in "<source>" + And I have an "<source>/index.md" page that contains "markdown" + And I have a configuration file with: + | key | value | + | source | <source> | + | destination | <dest> | + When I run jekyll build + Then the <source> directory should exist + And the "<dest>/index.html" file should <file_exist> exist + And I should see "markdown" in "<source>/index.md" + + Examples: + | source | dest | file_exist | + | mysite_source | mysite | | + | mysite | mysite_dest | | + | mysite/ | mysite | not | + | mysite | ./mysite | not | + | mysite/source | mysite | not | + | mysite | mysite/dest | | + + Scenario: Exclude files inline + Given I have an "Rakefile" file that contains "I want to be excluded" + And I have an "README" file that contains "I want to be excluded" + And I have an "index.html" file that contains "I want to be included" + And I have a "Gemfile" file that contains "gem 'include-me'" + And I have a configuration file with "exclude" set to "['Rakefile', 'README']" + When I run jekyll build + Then I should see "I want to be included" in "_site/index.html" + And the "_site/Gemfile" file should not exist + And the "_site/Rakefile" file should not exist + And the "_site/README" file should not exist + + Scenario: Exclude files with YAML array + Given I have an "Rakefile" file that contains "I want to be excluded" + And I have an "README" file that contains "I want to be excluded" + And I have an "index.html" file that contains "I want to be included" + And I have a "Gemfile" file that contains "gem 'include-me'" + And I have a configuration file with "exclude" set to: + | value | + | README | + | Rakefile | + When I run jekyll build + Then I should see "I want to be included" in "_site/index.html" + And the "_site/Rakefile" file should not exist + And the "_site/README" file should not exist + And the "_site/Gemfile" file should not exist + + Scenario: Copy over excluded files when their directory is explicitly included + Given I have a ".gitignore" file that contains ".DS_Store" + And I have an ".htaccess" file that contains "SomeDirective" + And I have a "Gemfile" file that contains "gem 'include-me'" + And I have a node_modules directory + And I have a "node_modules/bazinga.js" file that contains "var c = 'Bazinga!';" + And I have a "node_modules/warning.js" file that contains "var w = 'Winter is coming!';" + And I have a configuration file with "include" set to: + | value | + | .gitignore | + | .foo | + | Gemfile | + | node_modules | + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And the "_site/.htaccess" file should not exist + But I should see ".DS_Store" in "_site/.gitignore" + And I should see "gem 'include-me'" in "_site/Gemfile" + And I should see "var c = 'Bazinga!';" in "_site/node_modules/bazinga.js" + And I should see "var w = 'Winter is coming!';" in "_site/node_modules/warning.js" + + Scenario: Copy over excluded files only when they are explicitly included + Given I have a ".gitignore" file that contains ".DS_Store" + And I have an ".htaccess" file that contains "SomeDirective" + And I have a node_modules directory + And I have a "node_modules/bazinga.js" file that contains "var c = 'Bazinga!';" + And I have a "node_modules/warning.js" file that contains "var w = 'Winter is coming!';" + And I have a configuration file with "include" set to: + | value | + | .gitignore | + | .foo | + | node_modules/bazinga.js | + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And the "_site/.htaccess" file should not exist + But I should see ".DS_Store" in "_site/.gitignore" + And I should see "var c = 'Bazinga!';" in "_site/node_modules/bazinga.js" + But the "_site/node_modules/warning.js" file should not exist + + Scenario: Copy over excluded wild-card files only when they are explicitly included + Given I have a ".gitignore" file that contains ".DS_Store" + And I have an ".htaccess" file that contains "SomeDirective" + And I have an "foo.txt" file that contains "Lorem Ipsum" + And I have an "index.md" page that contains "{{ site.title }}" + And I have an "about.md" page that contains "{{ site.author }}" + And I have a configuration file with: + | key | value | + | title | Barren Site | + | author | John Doe | + | exclude | ["**"] | + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And the "_site/.gitignore" file should not exist + And the "_site/foo.txt" file should not exist + And the "_site/index.html" file should not exist + And the "_site/about.html" file should not exist + But the "_site/.htaccess" file should exist + Given I have a configuration file with: + | key | value | + | title | Barren Site | + | author | John Doe | + | exclude | ["**"] | + | include | ["about.md"] | + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And the "_site/.gitignore" file should not exist + And the "_site/foo.txt" file should not exist + And the "_site/index.html" file should not exist + And the "_site/.htaccess" file should not exist + But the "_site/about.html" file should exist + And I should see "John Doe" in "_site/about.html" + + Scenario: Process included files only once + Given I have a ".foobar" page that contains "dotfile with front matter" + And I have an ".htaccess" file that contains "SomeDirective" + And I have a "_redirects" file that contains "/foo/* /bar/* 301!" + And I have an "index.md" file with content: + """ + --- + --- + + Dotpages: {{ site.pages | where: 'path', '.foobar' | size }} + Dotstatics: {{ site.static_files | where: 'path', '/_redirects' | size }} + """ + And I have a configuration file with "title" set to "Hello World" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + But the "_site/.foobar" file should not exist + And the "_site/_redirects" file should not exist + And I should see "Dotpages: 0" in "_site/index.html" + And I should see "Dotstatics: 0" in "_site/index.html" + + When I have a configuration file with: + | key | value | + | title | Hello World | + | include | [.foobar, _redirects] | + When I run jekyll build + Then I should get a zero exit status + And I should not see "Conflict:" in the build output + And the _site directory should exist + And the "_site/.foobar" file should exist + And the "_site/_redirects" file should exist + And I should see "Dotpages: 1" in "_site/index.html" + And I should see "Dotstatics: 1" in "_site/index.html" + + Scenario: Use Kramdown for markup + Given I have an "index.markdown" page that contains "[Google](https://www.google.com)" + And I have a configuration file with "markdown" set to "kramdown" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "<a href=\"https://www.google.com\">Google</a>" in "_site/index.html" + + Scenario: Highlight code with rouge + Given I have an "index.html" page that contains "{% highlight ruby %} puts 'Hello world!' {% endhighlight %}" + And I have a configuration file with "highlighter" set to "rouge" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "Hello world!" in "_site/index.html" + And I should see "class=\"highlight\"" in "_site/index.html" + + Scenario: Rouge renders code block once + Given I have a configuration file with "highlighter" set to "rouge" + And I have a _posts directory + And I have the following post: + | title | date | layout | content | + | foo | 2014-04-27 11:34 | default | {% highlight text %} test {% endhighlight %} | + When I run jekyll build + Then I should not see "highlight(.*)highlight" in "_site/2014/04/27/foo.html" + + Scenario: Set time and no future dated posts + Given I have a _layouts directory + And I have a page layout that contains "Page Layout: {{ site.posts.size }} on {{ site.time | date: "%Y-%m-%d" }}" + And I have a post layout that contains "Post Layout: {{ content }}" + And I have an "index.html" page with layout "page" that contains "site index page" + And I have a configuration file with: + | key | value | + | time | 2010-01-01 | + | future | false | + And I have a _posts directory + And I have the following posts: + | title | date | layout | content | + | entry1 | 2007-12-31 | post | content for entry1. | + | entry2 | 2020-01-31 | post | content for entry2. | + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "Page Layout: 1 on 2010-01-01" in "_site/index.html" + And I should see "Post Layout: <p>content for entry1.</p>" in "_site/2007/12/31/entry1.html" + And the "_site/2020/01/31/entry2.html" file should not exist + + Scenario: Set time and future dated posts allowed + Given I have a _layouts directory + And I have a page layout that contains "Page Layout: {{ site.posts.size }} on {{ site.time | date: "%Y-%m-%d" }}" + And I have a post layout that contains "Post Layout: {{ content }}" + And I have an "index.html" page with layout "page" that contains "site index page" + And I have a configuration file with: + | key | value | + | time | 2010-01-01 | + | future | true | + And I have a _posts directory + And I have the following posts: + | title | date | layout | content | + | entry1 | 2007-12-31 | post | content for entry1. | + | entry2 | 2020-01-31 | post | content for entry2. | + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "Page Layout: 2 on 2010-01-01" in "_site/index.html" + And I should see "Post Layout: <p>content for entry1.</p>" in "_site/2007/12/31/entry1.html" + And I should see "Post Layout: <p>content for entry2.</p>" in "_site/2020/01/31/entry2.html" + + Scenario: Generate proper dates with explicitly set timezone (same as posts' time) + Given I have a _layouts directory + And I have a page layout that contains "Page Layout: {{ site.posts.size }}" + And I have a post layout that contains "Post Layout: {{ content }} built at {{ page.date | date_to_xmlschema }}" + And I have an "index.html" page with layout "page" that contains "site index page" + And I have a configuration file with: + | key | value | + | timezone | America/New_York | + And I have a _posts directory + And I have the following posts: + | title | date | layout | content | + | entry1 | 2013-04-09 23:22 -0400 | post | content for entry1. | + | entry2 | 2013-04-10 03:14 -0400 | post | content for entry2. | + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "Page Layout: 2" in "_site/index.html" + And I should see "Post Layout: <p>content for entry1.</p>\n built at" in "_site/2013/04/09/entry1.html" + And I should see "Post Layout: <p>content for entry2.</p>\n built at" in "_site/2013/04/10/entry2.html" + And I should see date "2013-04-09T23:22:00-04:00" in "_site/2013/04/09/entry1.html" unless Windows + And I should see date "2013-04-09T22:22:00-05:00" in "_site/2013/04/09/entry1.html" if on Windows + And I should see date "2013-04-10T03:14:00-04:00" in "_site/2013/04/10/entry2.html" unless Windows + And I should see date "2013-04-10T02:14:00-05:00" in "_site/2013/04/10/entry2.html" if on Windows + + Scenario: Generate proper dates with explicitly set timezone (different than posts' time) + Given I have a _layouts directory + And I have a page layout that contains "Page Layout: {{ site.posts.size }}" + And I have a post layout that contains "Post Layout: {{ content }} built at {{ page.date | date_to_xmlschema }}" + And I have an "index.html" page with layout "page" that contains "site index page" + And I have a configuration file with: + | key | value | + | timezone | Pacific/Honolulu | + And I have a _posts directory + And I have the following posts: + | title | date | layout | content | + | entry1 | 2013-04-09 23:22 +0400 | post | content for entry1. | + | entry2 | 2013-04-10 03:14 +0400 | post | content for entry2. | + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "Page Layout: 2" in "_site/index.html" + And the "_site/2013/04/09/entry1.html" file should exist + And the "_site/2013/04/09/entry2.html" file should exist + And I should see "Post Layout: <p>content for entry1.</p>\n built at 2013-04-09T09:22:00-10:00" in "_site/2013/04/09/entry1.html" + And I should see "Post Layout: <p>content for entry2.</p>\n built at 2013-04-09T13:14:00-10:00" in "_site/2013/04/09/entry2.html" + + Scenario: Generate proper dates with explicitly set timezone (using non-half hour offset ) + Given I have a _layouts directory + And I have a page layout that contains "Page Layout: {{ site.posts.size }}" + And I have a post layout that contains "Post Layout: {{ content }} built at {{ page.date | date_to_xmlschema }}" + And I have an "index.html" page with layout "page" that contains "site index page" + And I have a configuration file with: + | key | value | + | timezone | Australia/Eucla | + And I have a _posts directory + And I have the following posts: + | title | date | layout | content | + | entry1 | 2013-04-09 23:22 +0400 | post | content for entry1. | + | entry2 | 2013-04-10 03:14 +0400 | post | content for entry2. | + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "Page Layout: 2" in "_site/index.html" + And the "_site/2013/04/10/entry1.html" file should exist + And the "_site/2013/04/10/entry2.html" file should exist + And I should see "Post Layout: <p>content for entry1.</p>\n built at 2013-04-10T04:07:00\+08:45" in "_site/2013/04/10/entry1.html" + And I should see "Post Layout: <p>content for entry2.</p>\n built at 2013-04-10T07:59:00\+08:45" in "_site/2013/04/10/entry2.html" + + Scenario: Limit the number of posts generated by most recent date + Given I have a _posts directory + And I have a configuration file with: + | key | value | + | limit_posts | 2 | + And I have the following posts: + | title | date | content | + | Apples | 2009-03-27 | An article about apples | + | Oranges | 2009-04-01 | An article about oranges | + | Bananas | 2009-04-05 | An article about bananas | + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And the "_site/2009/04/05/bananas.html" file should exist + And the "_site/2009/04/01/oranges.html" file should exist + And the "_site/2009/03/27/apples.html" file should not exist + + Scenario: Using a different layouts directory + Given I have a _theme directory + And I have a page theme that contains "Page Layout: {{ site.posts.size }} on {{ site.time | date: "%Y-%m-%d" }}" + And I have a post theme that contains "Post Layout: {{ content }}" + And I have an "index.html" page with layout "page" that contains "site index page" + And I have a configuration file with: + | key | value | + | time | 2010-01-01 | + | future | true | + | layouts_dir | _theme | + And I have a _posts directory + And I have the following posts: + | title | date | layout | content | + | entry1 | 2007-12-31 | post | content for entry1. | + | entry2 | 2020-01-31 | post | content for entry2. | + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "Page Layout: 2 on 2010-01-01" in "_site/index.html" + And I should see "Post Layout: <p>content for entry1.</p>" in "_site/2007/12/31/entry1.html" + And I should see "Post Layout: <p>content for entry2.</p>" in "_site/2020/01/31/entry2.html" + + Scenario: arbitrary file reads via layouts + Given I have an "index.html" page with layout "page" that contains "FOO" + And I have a "_config.yml" file that contains "layouts: '../../../../../../../../../../../../../../usr/include'" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "FOO" in "_site/index.html" + And I should not see " " in "_site/index.html" diff --git a/features/site_data.feature b/features/site_data.feature new file mode 100644 index 0000000..e3c99a6 --- /dev/null +++ b/features/site_data.feature @@ -0,0 +1,118 @@ +Feature: Site data + As a hacker who likes to blog + I want to be able to embed data into my site + In order to make the site slightly dynamic + + Scenario: Use page variable in a page + Given I have an "contact.html" page with title "Contact" that contains "{{ page.title }}: email@example.com" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "Contact: email@example.com" in "_site/contact.html" + + Scenario Outline: Use page.path variable in a page + Given I have a <dir> directory + And I have a "<path>" page that contains "Source path: {{ page.path }}" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "Source path: <path>" in "_site/<path>" + + Examples: + | dir | path | + | . | index.html | + | dir | dir/about.html | + | dir/nested | dir/nested/page.html | + + Scenario: Override page.path + Given I have an "override.html" page with path "custom-override.html" that contains "Custom path: {{ page.path }}" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "Custom path: custom-override.html" in "_site/override.html" + + Scenario: Use site.time variable + Given I have an "index.html" page that contains "{{ site.time }}" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see today's time in "_site/index.html" + + Scenario: Use site.posts variable for latest post + Given I have a _posts directory + And I have an "index.html" page that contains "{{ site.posts.first.title }}: {{ site.posts.first.url }}" + And I have the following posts: + | title | date | content | + | First Post | 2009-03-25 | My First Post | + | Second Post | 2009-03-26 | My Second Post | + | Third Post | 2009-03-27 | My Third Post | + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "Third Post: /2009/03/27/third-post.html" in "_site/index.html" + + Scenario: Use site.posts variable in a loop + Given I have a _posts directory + And I have an "index.html" page that contains "{% for post in site.posts %} {{ post.title }} {% endfor %}" + And I have the following posts: + | title | date | content | + | First Post | 2009-03-25 | My First Post | + | Second Post | 2009-03-26 | My Second Post | + | Third Post | 2009-03-27 | My Third Post | + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "Third Post Second Post First Post" in "_site/index.html" + + Scenario: Use site.categories.code variable + Given I have a _posts directory + And I have an "index.html" page that contains "{% for post in site.categories.code %} {{ post.title }} {% endfor %}" + And I have the following posts: + | title | date | category | content | + | Awesome Hack | 2009-03-26 | code | puts 'Hello World' | + | Delicious Beer | 2009-03-26 | food | 1) Yuengling | + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "Awesome Hack" in "_site/index.html" + + Scenario: Use site.tags variable + Given I have a _posts directory + And I have an "index.html" page that contains "{% for post in site.tags.beer %} {{ post.content }} {% endfor %}" + And I have the following posts: + | title | date | tag | content | + | Delicious Beer | 2009-03-26 | beer | 1) Yuengling | + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "Yuengling" in "_site/index.html" + + Scenario: Order Posts by name when on the same date + Given I have a _posts directory + And I have an "index.html" page that contains "{% for post in site.posts %}{{ post.title }}:{{ post.previous.title}},{{ post.next.title}} {% endfor %}" + And I have the following posts: + | title | date | content | + | first | 2009-02-26 | first | + | A | 2009-03-26 | A | + | B | 2009-03-26 | B | + | C | 2009-03-26 | C | + | last | 2009-04-26 | last | + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "last:C, C:B,last B:A,C A:first,B first:,A" in "_site/index.html" + + Scenario: Use configuration date in site payload + Given I have an "index.html" page that contains "{{ site.url }}" + And I have a configuration file with "url" set to "http://example.com" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "http://example.com" in "_site/index.html" + + Scenario: Access Jekyll version via jekyll.version + Given I have an "index.html" page that contains "{{ jekyll.version }}" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "\d+\.\d+\.\d+" in "_site/index.html" diff --git a/features/step_definitions.rb b/features/step_definitions.rb new file mode 100644 index 0000000..616ab0b --- /dev/null +++ b/features/step_definitions.rb @@ -0,0 +1,417 @@ +# frozen_string_literal: true + +Before do + FileUtils.rm_rf(Paths.test_dir) if Paths.test_dir.exist? + FileUtils.mkdir_p(Paths.test_dir) unless Paths.test_dir.directory? + Dir.chdir(Paths.test_dir) + @timezone_before_scenario = ENV["TZ"] +end + +# + +After do + FileUtils.rm_rf(Paths.test_dir) if Paths.test_dir.exist? + Paths.output_file.delete if Paths.output_file.exist? + Paths.status_file.delete if Paths.status_file.exist? + Dir.chdir(Paths.test_dir.parent) + ENV["TZ"] = @timezone_before_scenario +end + +# + +Given(%r!^I have a blank site in "(.*)"$!) do |path| + unless File.exist?(path) + then FileUtils.mkdir_p(path) + end +end + +# + +Given(%r!^I do not have a "(.*)" directory$!) do |path| + Paths.test_dir.join(path).directory? +end + +# + +Given(%r!^I have an? "(.*)" page(?: with (.*) "(.*)")? that contains "(.*)"$!) do |file, key, value, text| + File.write(file, <<~DATA) + --- + #{key || "layout"}: #{value || "none"} + --- + + #{text} + DATA +end + +# + +Given(%r!^I have an? "(.*)" file that contains "(.*)"$!) do |file, text| + File.write(file, text) +end + +# + +Given(%r!^I have an? (.*) (layout|theme) that contains "(.*)"$!) do |name, type, text| + folder = type == "layout" ? "_layouts" : "_theme" + + destination_file = Pathname.new(File.join(folder, "#{name}.html")) + FileUtils.mkdir_p(destination_file.parent) unless destination_file.parent.directory? + File.write(destination_file, text) +end + +# + +Given(%r!^I have an? "(.*)" file with content:$!) do |file, text| + File.write(file, text) +end + +# + +Given(%r!^I have an? "(.*)" page with content:$!) do |file, text| + File.write(file, <<~DATA) + --- + --- + + #{text} + DATA +end + +# + +Given(%r!^I have an? (.*) directory$!) do |dir| + unless File.directory?(dir) + then FileUtils.mkdir_p(dir) + end +end + +# + +Given(%r!^I have the following (draft|page|post)s?(?: (in|under) "([^"]+)")?:$!) do |status, direction, folder, table| + table.hashes.each do |input_hash| + title = slug(input_hash["title"]) + ext = input_hash["type"] || "markdown" + filename = "#{title}.#{ext}" if %w(draft page).include?(status) + before, after = location(folder, direction) + dest_folder = "_drafts" if status == "draft" + dest_folder = "_posts" if status == "post" + dest_folder = "" if status == "page" + + if status == "post" + parsed_date = Time.xmlschema(input_hash["date"]) rescue Time.parse(input_hash["date"]) + input_hash["date"] = parsed_date + filename = "#{parsed_date.strftime("%Y-%m-%d")}-#{title}.#{ext}" + end + + path = File.join(before, dest_folder, after, filename) + File.write(path, file_content_from_hash(input_hash)) + end +end + +# + +Given(%r!^I have the following (draft|post)s? within the "(.*)" directory:$!) do |type, folder, table| + table.hashes.each do |input_hash| + title = slug(input_hash["title"]) + parsed_date = Time.xmlschema(input_hash["date"]) rescue Time.parse(input_hash["date"]) + + filename = type == "draft" ? "#{title}.markdown" : "#{parsed_date.strftime("%Y-%m-%d")}-#{title}.markdown" + + path = File.join(folder, "_#{type}s", filename) + File.write(path, file_content_from_hash(input_hash)) + end +end + +# + +Given(%r!^I have the following documents? under the (.*) collection:$!) do |folder, table| + table.hashes.each do |input_hash| + title = slug(input_hash["title"]) + filename = "#{title}.md" + dest_folder = "_#{folder}" + + path = File.join(dest_folder, filename) + File.write(path, file_content_from_hash(input_hash)) + end +end + +# + +Given(%r!^I have the following documents? under the "(.*)" collection within the "(.*)" directory:$!) do |label, dir, table| + table.hashes.each do |input_hash| + title = slug(input_hash["title"]) + path = File.join(dir, "_#{label}", "#{title}.md") + File.write(path, file_content_from_hash(input_hash)) + end +end + +# + +Given(%r!^I have the following documents? nested inside "(.*)" directory under the "(.*)" collection within the "(.*)" directory:$!) do |subdir, label, dir, table| + table.hashes.each do |input_hash| + title = slug(input_hash["title"]) + path = File.join(dir, "_#{label}", subdir, "#{title}.md") + File.write(path, file_content_from_hash(input_hash)) + end +end + +# + +Given(%r!^I have a configuration file with "(.*)" set to "(.*)"$!) do |key, value| + config = \ + if source_dir.join("_config.yml").exist? + SafeYAML.load_file(source_dir.join("_config.yml")) + else + {} + end + config[key] = SafeYAML.load(value) + Jekyll.set_timezone(value) if key == "timezone" + File.write("_config.yml", YAML.dump(config)) +end + +# + +Given(%r!^I have a configuration file with:$!) do |table| + table.hashes.each do |row| + step %(I have a configuration file with "#{row["key"]}" set to "#{row["value"]}") + end +end + +# + +Given(%r!^I have a configuration file with "([^\"]*)" set to:$!) do |key, table| + File.open("_config.yml", "w") do |f| + f.write("#{key}:\n") + table.hashes.each do |row| + f.write("- #{row["value"]}\n") + end + end +end + +# + +Given(%r!^I have fixture collections(?: in "(.*)" directory)?$!) do |directory| + collections_dir = File.join(source_dir, directory.to_s) + FileUtils.cp_r Paths.source_dir.join("test", "source", "_methods"), collections_dir + FileUtils.cp_r Paths.source_dir.join("test", "source", "_thanksgiving"), collections_dir + FileUtils.cp_r Paths.source_dir.join("test", "source", "_tutorials"), collections_dir +end + +# + +Given(%r!^I wait (\d+) second(s?)$!) do |time, _| + sleep(time.to_f) +end + +# + +When(%r!^I run jekyll(.*)$!) do |args| + run_jekyll(args) + if args.include?("--verbose") || ENV["DEBUG"] + warn "\n#{jekyll_run_output}\n" + end +end + +# + +When(%r!^I run bundle(.*)$!) do |args| + run_bundle(args) + if args.include?("--verbose") || ENV["DEBUG"] + warn "\n#{jekyll_run_output}\n" + end +end + +# + +When(%r!^I run gem(.*)$!) do |args| + run_rubygem(args) + if args.include?("--verbose") || ENV["DEBUG"] + warn "\n#{jekyll_run_output}\n" + end +end + +# + +When(%r!^I run git add .$!) do + run_in_shell("git", "add", ".", "--verbose") +end + +# + +When(%r!^I decide to build the theme gem$!) do + Dir.chdir(Paths.theme_gem_dir) + [ + "_includes/blank.html", + "_sass/blank.scss", + "assets/blank.scss", + "_config.yml" + ].each do |filename| + File.new(filename, "w") + end +end + +# + +When(%r!^I change "(.*)" to contain "(.*)"$!) do |file, text| + File.open(file, "a") do |f| + f.write(text) + end +end + +# + +When(%r!^I delete the file "(.*)"$!) do |file| + File.delete(file) +end + +# + +Then(%r!^the (.*) directory should +(not )?exist$!) do |dir, negative| + if negative.nil? + expect(Pathname.new(dir)).to exist + else + expect(Pathname.new(dir)).to_not exist + end +end + +# + +Then(%r!^I should (not )?see "(.*)" in "(.*)"$!) do |negative, text, file| + step %(the "#{file}" file should exist) + regexp = Regexp.new(text, Regexp::MULTILINE) + if negative.nil? || negative.empty? + expect(file_contents(file)).to match regexp + else + expect(file_contents(file)).not_to match regexp + end +end + +# + +Then(%r!^I should (not )?see "(.*)" in "(.*)" if on Windows$!) do |negative, text, file| + step %(the "#{file}" file should exist) + regexp = Regexp.new(text, Regexp::MULTILINE) + if negative.nil? || negative.empty? + if Jekyll::Utils::Platforms.really_windows? + expect(file_contents(file)).to match regexp + else + expect(file_contents(file)).not_to match regexp + end + end +end + +# + +Then(%r!^I should (not )?see "(.*)" in "(.*)" unless Windows$!) do |negative, text, file| + step %(the "#{file}" file should exist) + regexp = Regexp.new(text, Regexp::MULTILINE) + if negative.nil? || negative.empty? + if Jekyll::Utils::Platforms.really_windows? + expect(file_contents(file)).not_to match regexp + else + expect(file_contents(file)).to match regexp + end + end +end + +# + +Then(%r!^I should see date "(.*)" in "(.*)" unless Windows$!) do |text, file| + step %(the "#{file}" file should exist) + regexp = Regexp.new(text) + if Jekyll::Utils::Platforms.really_windows? && !dst_active? + expect(file_contents(file)).not_to match regexp + else + expect(file_contents(file)).to match regexp + end +end + +# + +Then(%r!^I should see date "(.*)" in "(.*)" if on Windows$!) do |text, file| + step %(the "#{file}" file should exist) + regexp = Regexp.new(text) + if Jekyll::Utils::Platforms.really_windows? && !dst_active? + expect(file_contents(file)).to match regexp + else + expect(file_contents(file)).not_to match regexp + end +end + +# + +Then(%r!^I should see exactly "(.*)" in "(.*)"$!) do |text, file| + step %(the "#{file}" file should exist) + expect(file_contents(file).strip).to eq text +end + +# + +Then(%r!^I should see escaped "(.*)" in "(.*)"$!) do |text, file| + step %(I should see "#{Regexp.escape(text)}" in "#{file}") +end + +# + +Then(%r!^the "(.*)" file should +(not )?exist$!) do |file, negative| + if negative.nil? + expect(Pathname.new(file)).to exist + else + expect(Pathname.new(file)).to_not exist + end +end + +# + +Then(%r!^I should see today's time in "(.*)"$!) do |file| + step %(I should see "#{seconds_agnostic_time(Time.now)}" in "#{file}") +end + +# + +Then(%r!^I should see today's date in "(.*)"$!) do |file| + step %(I should see "#{Date.today}" in "#{file}") +end + +# + +Then(%r!^I should (not )?see "(.*)" in the build output$!) do |negative, text| + if negative.nil? || negative.empty? + expect(jekyll_run_output).to match Regexp.new(text) + else + expect(jekyll_run_output).not_to match Regexp.new(text) + end +end + +# + +Then(%r!^I should get an updated git index$!) do + index = %w( + .gitignore + Gemfile + LICENSE.txt + README.md + _config.yml + _includes/blank.html + _layouts/default.html + _layouts/page.html + _layouts/post.html + _sass/blank.scss + assets/blank.scss + my-cool-theme.gemspec + ) + index.each do |file| + expect(jekyll_run_output).to match file + end +end + +# + +Then(%r!^I should get a zero exit(?:\-| )status$!) do + step %(I should see "EXIT STATUS: 0" in the build output) +end + +# + +Then(%r!^I should get a non-zero exit(?:\-| )status$!) do + step %(I should not see "EXIT STATUS: 0" in the build output) +end diff --git a/features/support/formatter.rb b/features/support/formatter.rb new file mode 100644 index 0000000..091353a --- /dev/null +++ b/features/support/formatter.rb @@ -0,0 +1,225 @@ +# frozen_string_literal: true + +require "fileutils" +require "colorator" +require "cucumber/formatter/console" +require "cucumber/formatter/io" + +module Jekyll + module Cucumber + class Formatter + attr_accessor :indent, :runtime + include ::Cucumber::Formatter::Console + include ::Cucumber::Formatter::Io + include FileUtils + + CHARS = { + :failed => "\u2718".red, + :pending => "\u203D".yellow, + :undefined => "\u2718".red, + :passed => "\u2714".green, + :skipped => "\u203D".blue, + }.freeze + + # + + def initialize(runtime, path_or_io, options) + @runtime = runtime + @snippets_input = [] + @io = ensure_io(path_or_io) + @prefixes = options[:prefixes] || {} + @delayed_messages = [] + @options = options + @exceptions = [] + @indent = 0 + @timings = {} + end + + # + + def before_features(_features) + print_profile_information + end + + # + + def after_features(features) + @io.puts + print_worst_offenders + print_summary(features) + end + + # + + def before_feature(_feature) + @exceptions = [] + @indent = 0 + end + + # + + def feature_element_timing_key(feature_element) + "\"#{feature_element.name}\" (#{feature_element.location})" + end + + # + + def before_feature_element(feature_element) + @indent = 2 + @scenario_indent = 2 + @timings[feature_element_timing_key(feature_element)] = Time.now + end + + # + + def after_feature_element(feature_element) + @timings[feature_element_timing_key(feature_element)] = Time.now - @timings[feature_element_timing_key(feature_element)] + @io.print " (#{@timings[feature_element_timing_key(feature_element)]}s)" + end + + # + + def tag_name(tag_name); end + + def comment_line(comment_line); end + + def after_tags(tags); end + + # + + def before_background(_background) + @scenario_indent = 2 + @in_background = true + @indent = 2 + end + + # + + def after_background(_background) + @in_background = nil + end + + # + + def background_name(keyword, name, source_line, indent) + print_feature_element_name( + keyword, name, source_line, indent + ) + end + + # + + def scenario_name(keyword, name, source_line, indent) + print_feature_element_name( + keyword, name, source_line, indent + ) + end + + # + + def before_step(step) + @current_step = step + end + + # + + # rubocop:disable Metrics/ParameterLists + def before_step_result(_keyword, _step_match, _multiline_arg, status, exception, \ + _source_indent, background, _file_colon_line) + + @hide_this_step = false + if exception + if @exceptions.include?(exception) + @hide_this_step = true + return + end + + @exceptions << exception + end + + if status != :failed && @in_background ^ background + @hide_this_step = true + return + end + + @status = status + end + + # + + def step_name(_keyword, _step_match, status, _source_indent, _background, _file_colon_line) + @io.print CHARS[status] + @io.print " " + end + # rubocop:enable Metrics/ParameterLists + + # + + def exception(exception, status) + return if @hide_this_step + + @io.puts + print_exception(exception, status, @indent) + @io.flush + end + + # + + def after_test_step(test_step, result) + collect_snippet_data( + test_step, result + ) + end + + # + + def print_feature_element_name(feature_element) + @io.print "\n#{feature_element.location} Scenario: #{feature_element.name} " + @io.flush + end + + # + + def cell_prefix(status) + @prefixes[status] + end + + # + + def print_worst_offenders + @io.puts + @io.puts "Worst offenders:" + @timings.sort_by { |_f, t| -t }.take(10).each do |(f, t)| + @io.puts " #{t}s for #{f}" + end + @io.puts + end + + # + + def print_summary(features) + @io.puts + print_stats(features, @options) + print_snippets(@options) + print_passing_wip(@options) + end + end + end +end + +AfterConfiguration do |config| + f = Jekyll::Cucumber::Formatter.new(nil, $stdout, {}) + + config.on_event :test_case_started do |event| + f.print_feature_element_name(event.test_case) + f.before_feature_element(event.test_case) + end + + config.on_event :test_case_finished do |event| + f.after_feature_element(event.test_case) + end + + config.on_event :test_run_finished do + f.print_worst_offenders + end +end diff --git a/features/support/helpers.rb b/features/support/helpers.rb new file mode 100644 index 0000000..b6e77ee --- /dev/null +++ b/features/support/helpers.rb @@ -0,0 +1,169 @@ +# frozen_string_literal: true + +require "jekyll" + +class Paths + SOURCE_DIR = Pathname.new(File.expand_path("../..", __dir__)) + + def self.test_dir; source_dir.join("tmp", "jekyll"); end + + def self.theme_gem_dir; source_dir.join("tmp", "jekyll", "my-cool-theme"); end + + def self.output_file; test_dir.join("jekyll_output.txt"); end + + def self.status_file; test_dir.join("jekyll_status.txt"); end + + def self.jekyll_bin; source_dir.join("exe", "jekyll"); end + + def self.source_dir; SOURCE_DIR; end +end + +# + +def file_content_from_hash(input_hash) + matter_hash = input_hash.reject { |k, _v| k == "content" } + matter = matter_hash.map { |k, v| "#{k}: #{v}\n" }.join + matter.chomp! + content = if input_hash["input"] && input_hash["filter"] + "{{ #{input_hash["input"]} | #{input_hash["filter"]} }}" + else + input_hash["content"] + end + + <<~EOF + --- + #{matter} + --- + + #{content} + EOF +end + +# + +def source_dir(*files) + Paths.test_dir(*files) +end + +# + +def all_steps_to_path(path) + source = source_dir + dest = Pathname.new(path).expand_path + paths = [] + + dest.ascend do |f| + break if f == source + paths.unshift f.to_s + end + + paths +end + +# + +def jekyll_run_output + Paths.output_file.read if Paths.output_file.file? +end + +# + +def jekyll_run_status + Paths.status_file.read if Paths.status_file.file? +end + +# + +def run_bundle(args) + run_in_shell("bundle", *args.strip.split(" ")) +end + +# + +def run_rubygem(args) + run_in_shell("gem", *args.strip.split(" ")) +end + +# + +def run_jekyll(args) + args = args.strip.split(" ") # Shellwords? + process = run_in_shell("ruby", Paths.jekyll_bin.to_s, *args, "--trace") + process.exitstatus.zero? +end + +# + +def run_in_shell(*args) + p, output = Jekyll::Utils::Exec.run(*args) + + File.write(Paths.status_file, p.exitstatus) + File.open(Paths.output_file, "wb") do |f| + f.print "$ " + f.puts args.join(" ") + f.puts output + f.puts "EXIT STATUS: #{p.exitstatus}" + end + + p +end + +# + +def slug(title = nil) + if title + title.downcase.gsub(%r![^\w]!, " ").strip.gsub(%r!\s+!, "-") + else + Time.now.strftime("%s%9N") # nanoseconds since the Epoch + end +end + +# + +def location(folder, direction) + if folder + before = folder if direction == "in" + after = folder if direction == "under" + end + + [before || ".", + after || "",] +end + +# + +def file_contents(path) + Pathname.new(path).read +end + +# + +def seconds_agnostic_datetime(datetime = Time.now) + date, time, zone = datetime.to_s.split(" ") + time = seconds_agnostic_time(time) + + [ + Regexp.escape(date), + "#{time}:\\d{2}", + Regexp.escape(zone), + ].join("\\ ") +end + +# + +def seconds_agnostic_time(time) + time = time.strftime("%H:%M:%S") if time.is_a?(Time) + hour, minutes, = time.split(":") + "#{hour}:#{minutes}" +end + +# Helper method for Windows +def dst_active? + config = Jekyll.configuration("quiet" => true) + ENV["TZ"] = config["timezone"] + dst = Time.now.isdst + + # reset variable to default state on Windows + ENV["TZ"] = nil + dst +end diff --git a/features/theme.feature b/features/theme.feature new file mode 100644 index 0000000..75ff5c6 --- /dev/null +++ b/features/theme.feature @@ -0,0 +1,151 @@ +Feature: Writing themes + As a hacker who likes to share my expertise + I want to be able to make a gemified theme + In order to share my awesome style skillz with other Jekyllites + + Scenario: Generating a new theme scaffold + When I run jekyll new-theme my-cool-theme + Then I should get a zero exit status + And the my-cool-theme directory should exist + + Scenario: Generating a new theme scaffold with a code of conduct + When I run jekyll new-theme my-cool-theme --code-of-conduct + Then I should get a zero exit status + And the my-cool-theme directory should exist + And the "my-cool-theme/CODE_OF_CONDUCT.md" file should exist + + Scenario: A theme with SCSS + Given I have a configuration file with "theme" set to "test-theme" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see ".sample { color: red; }\n\n\/\*# sourceMappingURL=style.css.map \*\/" in "_site/assets/style.css" + + Scenario: Overriding a theme with SCSS + Given I have a configuration file with "theme" set to "test-theme" + And I have an assets directory + And I have an "assets/style.scss" page that contains "@import 'test-theme-black';" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see ".sample { color: black; }\n\n\/\*# sourceMappingURL=style.css.map \*\/" in "_site/assets/style.css" + + Scenario: A theme with an include + Given I have a configuration file with "theme" set to "test-theme" + And I have an _includes directory + And I have an "_includes/in_project.html" file that contains "I'm in the project." + And I have an "index.html" page that contains "{% include in_project.html %} {% include include.html %}" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "I'm in the project." in "_site/index.html" + And I should see "<span class=\"sample\">include.html from test-theme</span>" in "_site/index.html" + + Scenario: A theme without data + Given I have a configuration file with "theme" set to "test-theme-skinny" + And I have a _data directory + And I have a "_data/greetings.yml" file with content: + """ + foo: "Hello! I’m foo. And who are you?" + """ + And I have an "index.html" page that contains "{{ site.data.greetings.foo }}" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "Hello! I’m foo. And who are you?" in "_site/index.html" + + Scenario: A theme with data overridden by data in source directory + Given I have a configuration file with "theme" set to "test-theme" + And I have a _data directory + And I have a "_data/greetings.yml" file with content: + """ + foo: "Hello! I’m foo. And who are you?" + """ + And I have an "index.html" page that contains "{{ site.data.greetings.foo }}" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "Hello! I’m foo. And who are you?" in "_site/index.html" + And I should not see "Hello! I’m bar. What’s up so far?" in "_site/index.html" + + Scenario: A theme with a layout + Given I have a configuration file with "theme" set to "test-theme" + And I have an _layouts directory + And I have an "_layouts/post.html" file that contains "post.html from the project: {{ content }}" + And I have an "index.html" page with layout "default" that contains "I'm content." + And I have a "post.html" page with layout "post" that contains "I'm more content." + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "default.html from test-theme: I'm content." in "_site/index.html" + And I should see "post.html from the project: I'm more content." in "_site/post.html" + + Scenario: A theme with assets + Given I have a configuration file with "theme" set to "test-theme" + And I have an assets directory + And I have an "assets/application.coffee" file that contains "From your site." + And I have an "assets/base.js" file that contains "From your site." + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "From your site." in "_site/assets/application.coffee" + And I should see "From your site." in "_site/assets/base.js" + + Scenario: A theme with *just* layouts + Given I have a configuration file with "theme" set to "test-theme-skinny" + And I have an "index.html" page with layout "home" that contains "The quick brown fox." + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "Message: The quick brown fox." in "_site/index.html" + But I should not see "_includes" in the build output + And I should not see "_sass" in the build output + And I should not see "assets" in the build output + + Scenario: Requiring dependencies of a theme + Given I have a configuration file with "theme" set to "test-dependency-theme" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And the "_site/test.txt" file should exist + + Scenario: Complicated site that puts it all together + Given I have a configuration file with "theme" set to "test-theme" + And I have a _posts directory + And I have the following posts: + | title | date | layout | content | + | entry1 | 2016-04-21 | post | I am using a local layout. {% include include.html %} | + | entry2 | 2016-04-21 | default | I am using a themed layout. {% include include.html %} {% include in_project.html %} | + And I have a _layouts directory + And I have a "_layouts/post.html" page with layout "default" that contains "I am a post layout! {{ content }}" + And I have an _includes directory + And I have an "_includes/in_project.html" file that contains "I am in the project, not the theme." + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "I am in the project, not the theme." in "_site/2016/04/21/entry2.html" + And I should see "<span class=\"sample\">include.html from test-theme</span>" in "_site/2016/04/21/entry2.html" + And I should see "default.html from test-theme:" in "_site/2016/04/21/entry2.html" + And I should see "I am using a themed layout." in "_site/2016/04/21/entry2.html" + And I should not see "I am a post layout!" in "_site/2016/04/21/entry2.html" + And I should not see "I am in the project, not the theme." in "_site/2016/04/21/entry1.html" + And I should see "<span class=\"sample\">include.html from test-theme</span>" in "_site/2016/04/21/entry1.html" + And I should see "default.html from test-theme:" in "_site/2016/04/21/entry1.html" + And I should see "I am using a local layout." in "_site/2016/04/21/entry1.html" + And I should see "I am a post layout!" in "_site/2016/04/21/entry1.html" + + Scenario: Complicated site that puts it all together in respect to data folders + Given I have a configuration file with "theme" set to "test-theme" + And I have a _data directory + And I have a "_data/i18n.yml" file with content: + """ + testimonials: + header: Kundenstimmen + """ + And I have an "index.html" page that contains "{% include testimonials.html %}" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should not see "Testimonials" in "_site/index.html" + And I should see "Kundenstimmen" in "_site/index.html" + And I should see "Design by FTC" in "_site/index.html" diff --git a/features/theme_configuration.feature b/features/theme_configuration.feature new file mode 100644 index 0000000..00ed81a --- /dev/null +++ b/features/theme_configuration.feature @@ -0,0 +1,36 @@ +Feature: Bundling Config file with Theme gems + As a web developer who likes to share my expertise + I want to be able to pre-configure my gemified theme + In order to make it easier for other Jekyllites to use my theme + + Scenario: Easy onboarding with a pre-configured theme + Given I have a configuration file with "theme" set to "test-theme" + And I have an "index.md" page that contains "{{ site.test_theme.skin }}" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "aero" in "_site/index.html" + + Scenario: Disabling import of theme configuration entirely + Given I have a configuration file with: + | key | value | + | theme | test-theme | + | ignore_theme_config | true | + And I have an "index.md" page that contains "{{ site.test_theme.skin }}" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should not see "aero" in "_site/index.html" + + Scenario: A pre-configured theme with valid config file overriding Jekyll defaults + Given I have a configuration file with "theme" set to "test-theme" + And I have an "index.md" page that contains "{{ site.baseurl }}" + And I have a node_modules directory + And I have a "node_modules/alert.js" file that contains "alert('foo');" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And the "_site/index.html" file should exist + But the "_site/node_modules/alert.js" file should not exist + And the "_site/extras/banner.html" file should not exist + And I should not see "/test-theme" in "_site/index.html" diff --git a/features/theme_gem.feature b/features/theme_gem.feature new file mode 100644 index 0000000..ef84974 --- /dev/null +++ b/features/theme_gem.feature @@ -0,0 +1,32 @@ +Feature: Building Theme Gems + As a hacker who likes to share my expertise + I want to be able to make a bonafide rubygem off my theme + In order to share my awesome style skillz with other Jekyllites + + Scenario: Generating a new Jekyll Theme + When I run jekyll new-theme my-cool-theme + Then I should get a zero exit status + And the my-cool-theme directory should exist + + Scenario: Checking if a bonafide Theme gem will be built from generated scaffolding + When I run jekyll new-theme my-cool-theme + Then the my-cool-theme directory should exist + When I decide to build the theme gem + Then the "_includes/blank.html" file should exist + Then the "_sass/blank.scss" file should exist + Then the "assets/blank.scss" file should exist + When I run git add . + Then I should get an updated git index + When I run gem build --force my-cool-theme.gemspec + Then the "./my-cool-theme-0.1.0.gem" file should exist + When I run gem unpack my-cool-theme-0.1.0.gem + Then the my-cool-theme-0.1.0 directory should exist + And the "my-cool-theme-0.1.0/_layouts/default.html" file should exist + And the "my-cool-theme-0.1.0/_includes/blank.html" file should exist + And the "my-cool-theme-0.1.0/_sass/blank.scss" file should exist + And the "my-cool-theme-0.1.0/assets/blank.scss" file should exist + And the "my-cool-theme-0.1.0/_config.yml" file should exist + And the my-cool-theme-0.1.0/.git directory should not exist + And the "my-cool-theme-0.1.0/.gitignore" file should not exist + And the "my-cool-theme-0.1.0/Gemfile" file should not exist + And the "my-cool-theme-0.1.0/my-cool-theme.gemspec" file should not exist diff --git a/jekyll.gemspec b/jekyll.gemspec new file mode 100644 index 0000000..67333b2 --- /dev/null +++ b/jekyll.gemspec @@ -0,0 +1,50 @@ +# frozen_string_literal: true + +require "English" +require_relative "lib/jekyll/version" + +Gem::Specification.new do |s| + s.name = "jekyll" + s.version = Jekyll::VERSION + s.license = "MIT" + s.authors = ["Tom Preston-Werner", "Parker Moore", "Matt Rogers"] + s.email = ["maintainers@jekyllrb.com"] + s.homepage = "https://jekyllrb.com" + s.summary = "A simple, blog aware, static site generator." + s.description = "Jekyll is a simple, blog aware, static site generator." + + all_files = `git ls-files`.split($INPUT_RECORD_SEPARATOR) + s.files = all_files.grep(%r!^(exe|lib|rubocop)/|^.rubocop.yml$!) + s.executables = all_files.grep(%r!^exe/!) { |f| File.basename(f) } + s.bindir = "exe" + s.require_paths = ["lib"] + + s.metadata = { + "source_code_uri" => "https://github.com/jekyll/jekyll", + "bug_tracker_uri" => "https://github.com/jekyll/jekyll/issues", + "changelog_uri" => "https://github.com/jekyll/jekyll/releases", + "homepage_uri" => s.homepage, + } + + s.rdoc_options = ["--charset=UTF-8"] + s.extra_rdoc_files = %w(README.markdown LICENSE) + + s.required_ruby_version = ">= 2.5.0" + s.required_rubygems_version = ">= 2.7.0" + + 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", "~> 1.0") + s.add_runtime_dependency("jekyll-sass-converter", ">= 2.0", "< 4.0") + s.add_runtime_dependency("jekyll-watch", "~> 2.0") + s.add_runtime_dependency("kramdown", "~> 2.3", ">= 2.3.1") + s.add_runtime_dependency("kramdown-parser-gfm", "~> 1.0") + s.add_runtime_dependency("liquid", "~> 4.0") + s.add_runtime_dependency("mercenary", ">= 0.3.6", "< 0.5") + s.add_runtime_dependency("pathutil", "~> 0.9") + s.add_runtime_dependency("rouge", ">= 3.0", "< 5.0") + s.add_runtime_dependency("safe_yaml", "~> 1.0") + s.add_runtime_dependency("terminal-table", ">= 1.8", "< 4.0") + s.add_runtime_dependency("webrick", "~> 1.7") +end diff --git a/lib/blank_template/_config.yml b/lib/blank_template/_config.yml new file mode 100644 index 0000000..a0cd66a --- /dev/null +++ b/lib/blank_template/_config.yml @@ -0,0 +1,3 @@ +url: "" # the base hostname & protocol for your site, e.g. http://example.com +baseurl: "" # the subpath of your site, e.g. /blog +title: "" # the name of your site, e.g. ACME Corp. diff --git a/lib/blank_template/_layouts/default.html b/lib/blank_template/_layouts/default.html new file mode 100644 index 0000000..fa0b00a --- /dev/null +++ b/lib/blank_template/_layouts/default.html @@ -0,0 +1,12 @@ +<!DOCTYPE html> +<html lang="{{ site.lang | default: "en-US" }}"> + <head> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <meta charset="utf-8"> + <title>{{ page.title }} - {{ site.title }}</title> + <link rel="stylesheet" href="{{ "/assets/css/main.css" | relative_url }}"> + </head> + <body> + {{ content }} + </body> +</html> diff --git a/lib/blank_template/_sass/main.scss b/lib/blank_template/_sass/main.scss new file mode 100644 index 0000000..126160d --- /dev/null +++ b/lib/blank_template/_sass/main.scss @@ -0,0 +1,9 @@ +$backgroundColor: #ffffff; +$bodyColor: #000000; +$bodyFont: -apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol"; + +body { + background: $backgroundColor; + color: $bodyColor; + font-family: $bodyFont; +} diff --git a/lib/blank_template/assets/css/main.scss b/lib/blank_template/assets/css/main.scss new file mode 100644 index 0000000..9f41894 --- /dev/null +++ b/lib/blank_template/assets/css/main.scss @@ -0,0 +1,4 @@ +--- +--- + +@import "main"; diff --git a/lib/blank_template/index.md b/lib/blank_template/index.md new file mode 100644 index 0000000..82eaafb --- /dev/null +++ b/lib/blank_template/index.md @@ -0,0 +1,8 @@ +--- +layout: default +title: "Happy Jekylling!" +--- + +## You're ready to go! + +Start developing your Jekyll website. diff --git a/lib/jekyll.rb b/lib/jekyll.rb new file mode 100644 index 0000000..231f8fd --- /dev/null +++ b/lib/jekyll.rb @@ -0,0 +1,195 @@ +# frozen_string_literal: true + +$LOAD_PATH.unshift __dir__ # For use/testing when no gem is installed + +# Require all of the Ruby files in the given directory. +# +# path - The String relative path from here to the directory. +# +# Returns nothing. +def require_all(path) + glob = File.join(__dir__, path, "*.rb") + Dir[glob].sort.each do |f| + require f + end +end + +# rubygems +require "rubygems" + +# stdlib +require "forwardable" +require "fileutils" +require "time" +require "English" +require "pathname" +require "logger" +require "set" +require "csv" +require "json" + +# 3rd party +require "pathutil" +require "addressable/uri" +require "safe_yaml/load" +require "liquid" +require "kramdown" +require "colorator" +require "i18n" + +SafeYAML::OPTIONS[:suppress_warnings] = true + +module Jekyll + # internal requires + autoload :Cleaner, "jekyll/cleaner" + autoload :Collection, "jekyll/collection" + autoload :Configuration, "jekyll/configuration" + autoload :Convertible, "jekyll/convertible" + autoload :Deprecator, "jekyll/deprecator" + autoload :Document, "jekyll/document" + autoload :EntryFilter, "jekyll/entry_filter" + autoload :Errors, "jekyll/errors" + autoload :Excerpt, "jekyll/excerpt" + autoload :PageExcerpt, "jekyll/page_excerpt" + autoload :External, "jekyll/external" + autoload :FrontmatterDefaults, "jekyll/frontmatter_defaults" + autoload :Hooks, "jekyll/hooks" + autoload :Layout, "jekyll/layout" + autoload :Inclusion, "jekyll/inclusion" + autoload :Cache, "jekyll/cache" + autoload :CollectionReader, "jekyll/readers/collection_reader" + autoload :DataReader, "jekyll/readers/data_reader" + autoload :LayoutReader, "jekyll/readers/layout_reader" + autoload :PostReader, "jekyll/readers/post_reader" + autoload :PageReader, "jekyll/readers/page_reader" + autoload :StaticFileReader, "jekyll/readers/static_file_reader" + autoload :ThemeAssetsReader, "jekyll/readers/theme_assets_reader" + autoload :LogAdapter, "jekyll/log_adapter" + autoload :Page, "jekyll/page" + autoload :PageWithoutAFile, "jekyll/page_without_a_file" + autoload :PathManager, "jekyll/path_manager" + autoload :PluginManager, "jekyll/plugin_manager" + autoload :Publisher, "jekyll/publisher" + autoload :Profiler, "jekyll/profiler" + autoload :Reader, "jekyll/reader" + autoload :Regenerator, "jekyll/regenerator" + autoload :RelatedPosts, "jekyll/related_posts" + autoload :Renderer, "jekyll/renderer" + autoload :LiquidRenderer, "jekyll/liquid_renderer" + autoload :Site, "jekyll/site" + autoload :StaticFile, "jekyll/static_file" + autoload :Stevenson, "jekyll/stevenson" + autoload :Theme, "jekyll/theme" + autoload :ThemeBuilder, "jekyll/theme_builder" + autoload :URL, "jekyll/url" + autoload :Utils, "jekyll/utils" + autoload :VERSION, "jekyll/version" + + # extensions + require "jekyll/plugin" + require "jekyll/converter" + require "jekyll/generator" + require "jekyll/command" + require "jekyll/liquid_extensions" + require "jekyll/filters" + + class << self + # Public: Tells you which Jekyll environment you are building in so you can skip tasks + # if you need to. This is useful when doing expensive compression tasks on css and + # images and allows you to skip that when working in development. + + def env + ENV["JEKYLL_ENV"] || "development" + end + + # Public: Generate a Jekyll configuration Hash by merging the default + # options with anything in _config.yml, and adding the given options on top. + # + # override - A Hash of config directives that override any options in both + # the defaults and the config file. + # See Jekyll::Configuration::DEFAULTS for a + # list of option names and their defaults. + # + # Returns the final configuration Hash. + def configuration(override = {}) + config = Configuration.new + override = Configuration[override].stringify_keys + unless override.delete("skip_config_files") + config = config.read_config_files(config.config_files(override)) + end + + # Merge DEFAULTS < _config.yml < override + Configuration.from(Utils.deep_merge_hashes(config, override)).tap do |obj| + set_timezone(obj["timezone"]) if obj["timezone"] + end + end + + # Public: Set the TZ environment variable to use the timezone specified + # + # timezone - the IANA Time Zone + # + # Returns nothing + # rubocop:disable Naming/AccessorMethodName + def set_timezone(timezone) + ENV["TZ"] = if Utils::Platforms.really_windows? + Utils::WinTZ.calculate(timezone) + else + timezone + end + end + # rubocop:enable Naming/AccessorMethodName + + # Public: Fetch the logger instance for this Jekyll process. + # + # Returns the LogAdapter instance. + def logger + @logger ||= LogAdapter.new(Stevenson.new, (ENV["JEKYLL_LOG_LEVEL"] || :info).to_sym) + end + + # Public: Set the log writer. + # New log writer must respond to the same methods + # as Ruby's internal Logger. + # + # writer - the new Logger-compatible log transport + # + # Returns the new logger. + def logger=(writer) + @logger = LogAdapter.new(writer, (ENV["JEKYLL_LOG_LEVEL"] || :info).to_sym) + end + + # Public: An array of sites + # + # Returns the Jekyll sites created. + def sites + @sites ||= [] + end + + # Public: Ensures the questionable path is prefixed with the base directory + # and prepends the questionable path with the base directory if false. + # + # base_directory - the directory with which to prefix the questionable path + # questionable_path - the path we're unsure about, and want prefixed + # + # Returns the sanitized path. + def sanitized_path(base_directory, questionable_path) + return base_directory if base_directory.eql?(questionable_path) + return base_directory if questionable_path.nil? + + +Jekyll::PathManager.sanitized_path(base_directory, questionable_path) + end + + # Conditional optimizations + Jekyll::External.require_if_present("liquid/c") + end +end + +require "jekyll/drops/drop" +require "jekyll/drops/document_drop" +require_all "jekyll/commands" +require_all "jekyll/converters" +require_all "jekyll/converters/markdown" +require_all "jekyll/drops" +require_all "jekyll/generators" +require_all "jekyll/tags" + +require "jekyll-sass-converter" diff --git a/lib/jekyll/cache.rb b/lib/jekyll/cache.rb new file mode 100644 index 0000000..8bffd0d --- /dev/null +++ b/lib/jekyll/cache.rb @@ -0,0 +1,186 @@ +# frozen_string_literal: true + +require "digest" + +module Jekyll + class Cache + # class-wide base cache + @base_cache = {} + + # class-wide directive to write cache to disk is enabled by default + @disk_cache_enabled = true + + class << self + attr_accessor :cache_dir # class-wide cache location + + attr_reader :base_cache, # class-wide base cache reader + :disk_cache_enabled # class-wide directive to write cache to disk + + # Disable Marshaling cached items to disk + def disable_disk_cache! + @disk_cache_enabled = false + end + + # Clear all caches + def clear + delete_cache_files + base_cache.each_value(&:clear) + end + + # Compare the current config to the cached config + # If they are different, clear all caches + # + # Returns nothing. + def clear_if_config_changed(config) + config = config.inspect + cache = Jekyll::Cache.new "Jekyll::Cache" + return if cache.key?("config") && cache["config"] == config + + clear + cache = Jekyll::Cache.new "Jekyll::Cache" + cache["config"] = config + nil + end + + private + + # Delete all cached items from all caches + # + # Returns nothing. + def delete_cache_files + FileUtils.rm_rf(@cache_dir) if disk_cache_enabled + end + end + + # + + # Get an existing named cache, or create a new one if none exists + # + # name - name of the cache + # + # Returns nothing. + def initialize(name) + @cache = Jekyll::Cache.base_cache[name] ||= {} + @name = name.gsub(%r![^\w\s-]!, "-") + end + + # Clear this particular cache + def clear + delete_cache_files + @cache.clear + end + + # Retrieve a cached item + # Raises if key does not exist in cache + # + # Returns cached value + def [](key) + return @cache[key] if @cache.key?(key) + + path = path_to(hash(key)) + if disk_cache_enabled? && File.file?(path) && File.readable?(path) + @cache[key] = load(path) + else + raise + end + end + + # Add an item to cache + # + # Returns nothing. + def []=(key, value) + @cache[key] = value + return unless disk_cache_enabled? + + path = path_to(hash(key)) + value = new Hash(value) if value.is_a?(Hash) && !value.default.nil? + dump(path, value) + rescue TypeError + Jekyll.logger.debug "Cache:", "Cannot dump object #{key}" + end + + # If an item already exists in the cache, retrieve it. + # Else execute code block, and add the result to the cache, and return that result. + def getset(key) + self[key] + rescue StandardError + value = yield + self[key] = value + value + end + + # Remove one particular item from the cache + # + # Returns nothing. + def delete(key) + @cache.delete(key) + File.delete(path_to(hash(key))) if disk_cache_enabled? + end + + # Check if `key` already exists in this cache + # + # Returns true if key exists in the cache, false otherwise + def key?(key) + # First, check if item is already cached in memory + return true if @cache.key?(key) + # Otherwise, it might be cached on disk + # but we should not consider the disk cache if it is disabled + return false unless disk_cache_enabled? + + path = path_to(hash(key)) + File.file?(path) && File.readable?(path) + end + + def disk_cache_enabled? + !!Jekyll::Cache.disk_cache_enabled + end + + private + + # Given a hashed key, return the path to where this item would be saved on disk. + def path_to(hash = nil) + @base_dir ||= File.join(Jekyll::Cache.cache_dir, @name) + return @base_dir if hash.nil? + + File.join(@base_dir, hash[0..1], hash[2..-1]).freeze + end + + # Given a key, return a SHA2 hash that can be used for caching this item to disk. + def hash(key) + Digest::SHA2.hexdigest(key).freeze + end + + # Remove all this caches items from disk + # + # Returns nothing. + def delete_cache_files + FileUtils.rm_rf(path_to) if disk_cache_enabled? + end + + # Load `path` from disk and return the result. + # This MUST NEVER be called in Safe Mode + # rubocop:disable Security/MarshalLoad + def load(path) + raise unless disk_cache_enabled? + + cached_file = File.open(path, "rb") + value = Marshal.load(cached_file) + cached_file.close + value + end + # rubocop:enable Security/MarshalLoad + + # Given a path and a value, save value to disk at path. + # This should NEVER be called in Safe Mode + # + # Returns nothing. + def dump(path, value) + return unless disk_cache_enabled? + + FileUtils.mkdir_p(File.dirname(path)) + File.open(path, "wb") do |cached_file| + Marshal.dump(value, cached_file) + end + end + end +end diff --git a/lib/jekyll/cleaner.rb b/lib/jekyll/cleaner.rb new file mode 100644 index 0000000..59e7c22 --- /dev/null +++ b/lib/jekyll/cleaner.rb @@ -0,0 +1,111 @@ +# frozen_string_literal: true + +module Jekyll + # Handles the cleanup of a site's destination before it is built. + class Cleaner + HIDDEN_FILE_REGEX = %r!/\.{1,2}$!.freeze + attr_reader :site + + def initialize(site) + @site = site + end + + # Cleans up the site's destination directory + def cleanup! + FileUtils.rm_rf(obsolete_files) + FileUtils.rm_rf(metadata_file) unless @site.incremental? + end + + private + + # Private: The list of files and directories to be deleted during cleanup process + # + # Returns an Array of the file and directory paths + def obsolete_files + out = (existing_files - new_files - new_dirs + replaced_files).to_a + Jekyll::Hooks.trigger :clean, :on_obsolete, out + out + end + + # Private: The metadata file storing dependency tree and build history + # + # Returns an Array with the metadata file as the only item + def metadata_file + [site.regenerator.metadata_file] + end + + # Private: The list of existing files, apart from those included in + # keep_files and hidden files. + # + # Returns a Set with the file paths + def existing_files + files = Set.new + regex = keep_file_regex + dirs = keep_dirs + + Utils.safe_glob(site.in_dest_dir, ["**", "*"], File::FNM_DOTMATCH).each do |file| + next if HIDDEN_FILE_REGEX.match?(file) || regex.match?(file) || dirs.include?(file) + + files << file + end + + files + end + + # Private: The list of files to be created when site is built. + # + # Returns a Set with the file paths + def new_files + @new_files ||= Set.new.tap do |files| + site.each_site_file { |item| files << item.destination(site.dest) } + end + end + + # Private: The list of directories to be created when site is built. + # These are the parent directories of the files in #new_files. + # + # Returns a Set with the directory paths + def new_dirs + @new_dirs ||= new_files.flat_map { |file| parent_dirs(file) }.to_set + end + + # Private: The list of parent directories of a given file + # + # Returns an Array with the directory paths + def parent_dirs(file) + parent_dir = File.dirname(file) + if parent_dir == site.dest + [] + else + parent_dirs(parent_dir).unshift(parent_dir) + end + end + + # Private: The list of existing files that will be replaced by a directory + # during build + # + # Returns a Set with the file paths + def replaced_files + new_dirs.select { |dir| File.file?(dir) }.to_set + end + + # Private: The list of directories that need to be kept because they are + # parent directories of files specified in keep_files + # + # Returns a Set with the directory paths + def keep_dirs + site.keep_files.flat_map { |file| parent_dirs(site.in_dest_dir(file)) }.to_set + end + + # Private: Creates a regular expression from the config's keep_files array + # + # Examples + # ['.git','.svn'] with site.dest "/myblog/_site" creates + # the following regex: /\A\/myblog\/_site\/(\.git|\/.svn)/ + # + # Returns the regular expression + def keep_file_regex + %r!\A#{Regexp.quote(site.dest)}/(#{Regexp.union(site.keep_files).source})! + end + end +end diff --git a/lib/jekyll/collection.rb b/lib/jekyll/collection.rb new file mode 100644 index 0000000..1ce33bf --- /dev/null +++ b/lib/jekyll/collection.rb @@ -0,0 +1,310 @@ +# frozen_string_literal: true + +module Jekyll + class Collection + attr_reader :site, :label, :metadata + attr_writer :docs + + # Create a new Collection. + # + # site - the site to which this collection belongs. + # label - the name of the collection + # + # Returns nothing. + def initialize(site, label) + @site = site + @label = sanitize_label(label) + @metadata = extract_metadata + end + + # Fetch the Documents in this collection. + # Defaults to an empty array if no documents have been read in. + # + # Returns an array of Jekyll::Document objects. + def docs + @docs ||= [] + end + + # Override of normal respond_to? to match method_missing's logic for + # looking in @data. + def respond_to_missing?(method, include_private = false) + docs.respond_to?(method.to_sym, include_private) || super + end + + # Override of method_missing to check in @data for the key. + def method_missing(method, *args, &blck) + if docs.respond_to?(method.to_sym) + Jekyll.logger.warn "Deprecation:", + "#{label}.#{method} should be changed to #{label}.docs.#{method}." + Jekyll.logger.warn "", "Called by #{caller(0..0)}." + docs.public_send(method.to_sym, *args, &blck) + else + super + end + end + + # Fetch the static files in this collection. + # Defaults to an empty array if no static files have been read in. + # + # Returns an array of Jekyll::StaticFile objects. + def files + @files ||= [] + end + + # Read the allowed documents into the collection's array of docs. + # + # Returns the sorted array of docs. + def read + filtered_entries.each do |file_path| + full_path = collection_dir(file_path) + next if File.directory?(full_path) + + if Utils.has_yaml_header? full_path + read_document(full_path) + else + read_static_file(file_path, full_path) + end + end + site.static_files.concat(files) unless files.empty? + sort_docs! + end + + # All the entries in this collection. + # + # Returns an Array of file paths to the documents in this collection + # relative to the collection's directory + def entries + return [] unless exists? + + @entries ||= begin + collection_dir_slash = "#{collection_dir}/" + Utils.safe_glob(collection_dir, ["**", "*"], File::FNM_DOTMATCH).map do |entry| + entry[collection_dir_slash] = "" + entry + end + end + end + + # Filtered version of the entries in this collection. + # See `Jekyll::EntryFilter#filter` for more information. + # + # Returns a list of filtered entry paths. + def filtered_entries + return [] unless exists? + + @filtered_entries ||= + Dir.chdir(directory) do + entry_filter.filter(entries).reject do |f| + path = collection_dir(f) + File.directory?(path) || entry_filter.symlink?(f) + end + end + end + + # The directory for this Collection, relative to the site source or the directory + # containing the collection. + # + # Returns a String containing the directory name where the collection + # is stored on the filesystem. + def relative_directory + @relative_directory ||= "_#{label}" + end + + # The full path to the directory containing the collection. + # + # Returns a String containing th directory name where the collection + # is stored on the filesystem. + def directory + @directory ||= site.in_source_dir( + File.join(container, relative_directory) + ) + end + + # The full path to the directory containing the collection, with + # optional subpaths. + # + # *files - (optional) any other path pieces relative to the + # directory to append to the path + # + # Returns a String containing th directory name where the collection + # is stored on the filesystem. + def collection_dir(*files) + return directory if files.empty? + + site.in_source_dir(container, relative_directory, *files) + end + + # Checks whether the directory "exists" for this collection. + # The directory must exist on the filesystem and must not be a symlink + # if in safe mode. + # + # Returns false if the directory doesn't exist or if it's a symlink + # and we're in safe mode. + def exists? + File.directory?(directory) && !entry_filter.symlink?(directory) + end + + # The entry filter for this collection. + # Creates an instance of Jekyll::EntryFilter. + # + # Returns the instance of Jekyll::EntryFilter for this collection. + def entry_filter + @entry_filter ||= Jekyll::EntryFilter.new(site, relative_directory) + end + + # An inspect string. + # + # Returns the inspect string + def inspect + "#<#{self.class} @label=#{label} docs=#{docs}>" + end + + # Produce a sanitized label name + # Label names may not contain anything but alphanumeric characters, + # underscores, and hyphens. + # + # label - the possibly-unsafe label + # + # Returns a sanitized version of the label. + def sanitize_label(label) + label.gsub(%r![^a-z0-9_\-.]!i, "") + end + + # Produce a representation of this Collection for use in Liquid. + # Exposes two attributes: + # - label + # - docs + # + # Returns a representation of this collection for use in Liquid. + def to_liquid + Drops::CollectionDrop.new self + end + + # Whether the collection's documents ought to be written as individual + # files in the output. + # + # Returns true if the 'write' metadata is true, false otherwise. + def write? + !!metadata.fetch("output", false) + end + + # The URL template to render collection's documents at. + # + # Returns the URL template to render collection's documents at. + def url_template + @url_template ||= metadata.fetch("permalink") do + Utils.add_permalink_suffix("/:collection/:path", site.permalink_style) + end + end + + # Extract options for this collection from the site configuration. + # + # Returns the metadata for this collection + def extract_metadata + if site.config["collections"].is_a?(Hash) + site.config["collections"][label] || {} + else + {} + end + end + + private + + def container + @container ||= site.config["collections_dir"] + end + + def read_document(full_path) + doc = Document.new(full_path, :site => site, :collection => self) + doc.read + docs << doc if site.unpublished || doc.published? + end + + def sort_docs! + if metadata["order"].is_a?(Array) + rearrange_docs! + elsif metadata["sort_by"].is_a?(String) + sort_docs_by_key! + else + docs.sort! + end + end + + # A custom sort function based on Schwartzian transform + # Refer https://byparker.com/blog/2017/schwartzian-transform-faster-sorting/ for details + def sort_docs_by_key! + meta_key = metadata["sort_by"] + # Modify `docs` array to cache document's property along with the Document instance + docs.map! { |doc| [doc.data[meta_key], doc] }.sort! do |apples, olives| + order = determine_sort_order(meta_key, apples, olives) + + # Fall back to `Document#<=>` if the properties were equal or were non-sortable + # Otherwise continue with current sort-order + if order.nil? || order.zero? + apples[-1] <=> olives[-1] + else + order + end + + # Finally restore the `docs` array with just the Document objects themselves + end.map!(&:last) + end + + def determine_sort_order(sort_key, apples, olives) + apple_property, apple_document = apples + olive_property, olive_document = olives + + if apple_property.nil? && !olive_property.nil? + order_with_warning(sort_key, apple_document, 1) + elsif !apple_property.nil? && olive_property.nil? + order_with_warning(sort_key, olive_document, -1) + else + apple_property <=> olive_property + end + end + + def order_with_warning(sort_key, document, order) + Jekyll.logger.warn "Sort warning:", "'#{sort_key}' not defined in #{document.relative_path}" + order + end + + # Rearrange documents within the `docs` array as listed in the `metadata["order"]` array. + # + # Involves converting the two arrays into hashes based on relative_paths as keys first, then + # merging them to remove duplicates and finally retrieving the Document instances from the + # merged array. + def rearrange_docs! + docs_table = {} + custom_order = {} + + # pre-sort to normalize default array across platforms and then proceed to create a Hash + # from that sorted array. + docs.sort.each do |doc| + docs_table[doc.relative_path] = doc + end + + metadata["order"].each do |entry| + custom_order[File.join(relative_directory, entry)] = nil + end + + result = Jekyll::Utils.deep_merge_hashes(custom_order, docs_table).values + result.compact! + self.docs = result + end + + def read_static_file(file_path, full_path) + relative_dir = Jekyll.sanitized_path( + relative_directory, + File.dirname(file_path) + ).chomp("/.") + + files << StaticFile.new( + site, + site.source, + relative_dir, + File.basename(full_path), + self + ) + end + end +end diff --git a/lib/jekyll/command.rb b/lib/jekyll/command.rb new file mode 100644 index 0000000..be8890e --- /dev/null +++ b/lib/jekyll/command.rb @@ -0,0 +1,105 @@ +# frozen_string_literal: true + +module Jekyll + class Command + class << self + # A list of subclasses of Jekyll::Command + def subclasses + @subclasses ||= [] + end + + # Keep a list of subclasses of Jekyll::Command every time it's inherited + # Called automatically. + # + # base - the subclass + # + # Returns nothing + def inherited(base) + subclasses << base + super(base) + end + + # Run Site#process and catch errors + # + # site - the Jekyll::Site object + # + # Returns nothing + def process_site(site) + site.process + rescue Jekyll::Errors::FatalException => e + Jekyll.logger.error "ERROR:", "YOUR SITE COULD NOT BE BUILT:" + Jekyll.logger.error "", "------------------------------------" + Jekyll.logger.error "", e.message + exit(1) + end + + # Create a full Jekyll configuration with the options passed in as overrides + # + # options - the configuration overrides + # + # Returns a full Jekyll configuration + def configuration_from_options(options) + return options if options.is_a?(Jekyll::Configuration) + + Jekyll.configuration(options) + end + + # Add common options to a command for building configuration + # + # cmd - the Jekyll::Command to add these options to + # + # Returns nothing + # rubocop:disable Metrics/MethodLength + def add_build_options(cmd) + cmd.option "config", "--config CONFIG_FILE[,CONFIG_FILE2,...]", + Array, "Custom configuration file" + cmd.option "destination", "-d", "--destination DESTINATION", + "The current folder will be generated into DESTINATION" + cmd.option "source", "-s", "--source SOURCE", "Custom source directory" + cmd.option "future", "--future", "Publishes posts with a future date" + cmd.option "limit_posts", "--limit_posts MAX_POSTS", Integer, + "Limits the number of posts to parse and publish" + cmd.option "watch", "-w", "--[no-]watch", "Watch for changes and rebuild" + cmd.option "baseurl", "-b", "--baseurl URL", + "Serve the website from the given base URL" + cmd.option "force_polling", "--force_polling", "Force watch to use polling" + cmd.option "lsi", "--lsi", "Use LSI for improved related posts" + cmd.option "show_drafts", "-D", "--drafts", "Render posts in the _drafts folder" + cmd.option "unpublished", "--unpublished", + "Render posts that were marked as unpublished" + cmd.option "disable_disk_cache", "--disable-disk-cache", + "Disable caching to disk in non-safe mode" + cmd.option "quiet", "-q", "--quiet", "Silence output." + cmd.option "verbose", "-V", "--verbose", "Print verbose output." + cmd.option "incremental", "-I", "--incremental", "Enable incremental rebuild." + cmd.option "strict_front_matter", "--strict_front_matter", + "Fail if errors are present in front matter" + end + # rubocop:enable Metrics/MethodLength + + # Run ::process method in a given set of Jekyll::Command subclasses and suggest + # re-running the associated command with --trace switch to obtain any additional + # information or backtrace regarding the encountered Exception. + # + # cmd - the Jekyll::Command to be handled + # options - configuration overrides + # klass - an array of Jekyll::Command subclasses associated with the command + # + # Note that all exceptions are rescued.. + # rubocop: disable Lint/RescueException + def process_with_graceful_fail(cmd, options, *klass) + klass.each { |k| k.process(options) if k.respond_to?(:process) } + rescue Exception => e + raise e if cmd.trace + + msg = " Please append `--trace` to the `#{cmd.name}` command " + dashes = "-" * msg.length + Jekyll.logger.error "", dashes + Jekyll.logger.error "Jekyll #{Jekyll::VERSION} ", msg + Jekyll.logger.error "", " for any additional information or backtrace. " + Jekyll.logger.abort_with "", dashes + end + # rubocop: enable Lint/RescueException + end + end +end diff --git a/lib/jekyll/commands/build.rb b/lib/jekyll/commands/build.rb new file mode 100644 index 0000000..204b2a1 --- /dev/null +++ b/lib/jekyll/commands/build.rb @@ -0,0 +1,82 @@ +# frozen_string_literal: true + +module Jekyll + module Commands + class Build < Command + class << self + # Create the Mercenary command for the Jekyll CLI for this Command + def init_with_program(prog) + prog.command(:build) do |c| + c.syntax "build [options]" + c.description "Build your site" + c.alias :b + + add_build_options(c) + + c.action do |_, options| + options["serving"] = false + process_with_graceful_fail(c, options, self) + end + end + end + + # Build your jekyll site + # Continuously watch if `watch` is set to true in the config. + def process(options) + # Adjust verbosity quickly + Jekyll.logger.adjust_verbosity(options) + + options = configuration_from_options(options) + site = Jekyll::Site.new(options) + + if options.fetch("skip_initial_build", false) + Jekyll.logger.warn "Build Warning:", + "Skipping the initial build. This may result in an out-of-date site." + else + build(site, options) + end + + if options.fetch("detach", false) + Jekyll.logger.info "Auto-regeneration:", + "disabled when running server detached." + elsif options.fetch("watch", false) + watch(site, options) + else + Jekyll.logger.info "Auto-regeneration:", "disabled. Use --watch to enable." + end + end + + # Build your Jekyll site. + # + # site - the Jekyll::Site instance to build + # options - A Hash of options passed to the command + # + # Returns nothing. + def build(site, options) + t = Time.now + source = File.expand_path(options["source"]) + destination = File.expand_path(options["destination"]) + incremental = options["incremental"] + Jekyll.logger.info "Source:", source + Jekyll.logger.info "Destination:", destination + Jekyll.logger.info "Incremental build:", + (incremental ? "enabled" : "disabled. Enable with --incremental") + Jekyll.logger.info "Generating..." + process_site(site) + Jekyll.logger.info "", "done in #{(Time.now - t).round(3)} seconds." + end + + # Private: Watch for file changes and rebuild the site. + # + # site - A Jekyll::Site instance + # options - A Hash of options passed to the command + # + # Returns nothing. + def watch(site, options) + External.require_with_graceful_fail "jekyll-watch" + Jekyll::Watcher.watch(options, site) + end + end + end + end +end diff --git a/lib/jekyll/commands/clean.rb b/lib/jekyll/commands/clean.rb new file mode 100644 index 0000000..9b657a4 --- /dev/null +++ b/lib/jekyll/commands/clean.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +module Jekyll + module Commands + class Clean < Command + class << self + def init_with_program(prog) + prog.command(:clean) do |c| + c.syntax "clean [subcommand]" + c.description "Clean the site (removes site output and metadata file) without building." + + add_build_options(c) + + c.action do |_, options| + Jekyll::Commands::Clean.process(options) + end + end + end + + def process(options) + options = configuration_from_options(options) + destination = options["destination"] + metadata_file = File.join(options["source"], ".jekyll-metadata") + cache_dir = File.join(options["source"], options["cache_dir"]) + sass_cache = ".sass-cache" + + remove(destination, :checker_func => :directory?) + remove(metadata_file, :checker_func => :file?) + remove(cache_dir, :checker_func => :directory?) + remove(sass_cache, :checker_func => :directory?) + end + + def remove(filename, checker_func: :file?) + if File.public_send(checker_func, filename) + Jekyll.logger.info "Cleaner:", "Removing #{filename}..." + FileUtils.rm_rf(filename) + else + Jekyll.logger.info "Cleaner:", "Nothing to do for #{filename}." + end + end + end + end + end +end diff --git a/lib/jekyll/commands/doctor.rb b/lib/jekyll/commands/doctor.rb new file mode 100644 index 0000000..7e01293 --- /dev/null +++ b/lib/jekyll/commands/doctor.rb @@ -0,0 +1,177 @@ +# frozen_string_literal: true + +module Jekyll + module Commands + class Doctor < Command + class << self + def init_with_program(prog) + prog.command(:doctor) do |c| + c.syntax "doctor" + c.description "Search site and print specific deprecation warnings" + c.alias(:hyde) + + c.option "config", "--config CONFIG_FILE[,CONFIG_FILE2,...]", Array, + "Custom configuration file" + + c.action do |_, options| + Jekyll::Commands::Doctor.process(options) + end + end + end + + def process(options) + site = Jekyll::Site.new(configuration_from_options(options)) + site.reset + site.read + site.generate + + if healthy?(site) + Jekyll.logger.info "Your test results", "are in. Everything looks fine." + else + abort + end + end + + def healthy?(site) + [ + fsnotify_buggy?(site), + !deprecated_relative_permalinks(site), + !conflicting_urls(site), + !urls_only_differ_by_case(site), + proper_site_url?(site), + properly_gathered_posts?(site), + ].all? + end + + def properly_gathered_posts?(site) + return true if site.config["collections_dir"].empty? + + posts_at_root = site.in_source_dir("_posts") + return true unless File.directory?(posts_at_root) + + Jekyll.logger.warn "Warning:", + "Detected '_posts' directory outside custom `collections_dir`!" + Jekyll.logger.warn "", + "Please move '#{posts_at_root}' into the custom directory at " \ + "'#{site.in_source_dir(site.config["collections_dir"])}'" + false + end + + def deprecated_relative_permalinks(site) + if site.config["relative_permalinks"] + Jekyll::Deprecator.deprecation_message "Your site still uses relative permalinks, " \ + "which was removed in Jekyll v3.0.0." + true + end + end + + def conflicting_urls(site) + conflicting_urls = false + destination_map(site).each do |dest, paths| + next unless paths.size > 1 + + conflicting_urls = true + Jekyll.logger.warn "Conflict:", + "The following destination is shared by multiple files." + Jekyll.logger.warn "", "The written file may end up with unexpected contents." + Jekyll.logger.warn "", dest.to_s.cyan + paths.each { |path| Jekyll.logger.warn "", " - #{path}" } + Jekyll.logger.warn "" + end + conflicting_urls + end + + def fsnotify_buggy?(_site) + return true unless Utils::Platforms.osx? + + if Dir.pwd != `pwd`.strip + Jekyll.logger.error <<~STR + We have detected that there might be trouble using fsevent on your + operating system, you can read https://github.com/thibaudgg/rb-fsevent/wiki/no-fsevents-fired-(OSX-bug) + for possible work arounds or you can work around it immediately + with `--force-polling`. + STR + + false + end + + true + end + + def urls_only_differ_by_case(site) + urls_only_differ_by_case = false + urls = case_insensitive_urls(site.pages + site.docs_to_write, site.dest) + urls.each_value do |real_urls| + next unless real_urls.uniq.size > 1 + + urls_only_differ_by_case = true + Jekyll.logger.warn "Warning:", "The following URLs only differ by case. On a " \ + "case-insensitive file system one of the URLs will be " \ + "overwritten by the other: #{real_urls.join(", ")}" + end + urls_only_differ_by_case + end + + def proper_site_url?(site) + url = site.config["url"] + [ + url_exists?(url), + url_valid?(url), + url_absolute(url), + ].all? + end + + private + + def destination_map(site) + {}.tap do |result| + site.each_site_file do |thing| + next if allow_used_permalink?(thing) + + dest_path = thing.destination(site.dest) + (result[dest_path] ||= []) << thing.path + end + end + end + + def allow_used_permalink?(item) + defined?(JekyllRedirectFrom) && item.is_a?(JekyllRedirectFrom::RedirectPage) + end + + def case_insensitive_urls(things, destination) + things.each_with_object({}) do |thing, memo| + dest = thing.destination(destination) + (memo[dest.downcase] ||= []) << dest + end + end + + def url_exists?(url) + return true unless url.nil? || url.empty? + + Jekyll.logger.warn "Warning:", "You didn't set an URL in the config file, you may " \ + "encounter problems with some plugins." + false + end + + def url_valid?(url) + Addressable::URI.parse(url) + true + # Addressable::URI#parse only raises a TypeError + # https://github.com/sporkmonger/addressable/blob/0a0e96acb17225f9b1c9cab0bad332b448934c9a/lib/addressable/uri.rb#L103 + rescue TypeError + Jekyll.logger.warn "Warning:", "The site URL does not seem to be valid, " \ + "check the value of `url` in your config file." + false + end + + def url_absolute(url) + return true if url.is_a?(String) && Addressable::URI.parse(url).absolute? + + Jekyll.logger.warn "Warning:", "Your site URL does not seem to be absolute, " \ + "check the value of `url` in your config file." + false + end + end + end + end +end diff --git a/lib/jekyll/commands/help.rb b/lib/jekyll/commands/help.rb new file mode 100644 index 0000000..90403df --- /dev/null +++ b/lib/jekyll/commands/help.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +module Jekyll + module Commands + class Help < Command + class << self + def init_with_program(prog) + prog.command(:help) do |c| + c.syntax "help [subcommand]" + c.description "Show the help message, optionally for a given subcommand." + + c.action do |args, _| + cmd = (args.first || "").to_sym + if args.empty? + Jekyll.logger.info prog.to_s + elsif prog.has_command? cmd + Jekyll.logger.info prog.commands[cmd].to_s + else + invalid_command(prog, cmd) + abort + end + end + end + end + + def invalid_command(prog, cmd) + Jekyll.logger.error "Error:", + "Hmm... we don't know what the '#{cmd}' command is." + Jekyll.logger.info "Valid commands:", prog.commands.keys.join(", ") + end + end + end + end +end diff --git a/lib/jekyll/commands/new.rb b/lib/jekyll/commands/new.rb new file mode 100644 index 0000000..0a00f26 --- /dev/null +++ b/lib/jekyll/commands/new.rb @@ -0,0 +1,168 @@ +# frozen_string_literal: true + +require "erb" + +module Jekyll + module Commands + class New < Command + class << self + def init_with_program(prog) + prog.command(:new) do |c| + c.syntax "new PATH" + c.description "Creates a new Jekyll site scaffold in PATH" + + c.option "force", "--force", "Force creation even if PATH already exists" + c.option "blank", "--blank", "Creates scaffolding but with empty files" + c.option "skip-bundle", "--skip-bundle", "Skip 'bundle install'" + + c.action do |args, options| + Jekyll::Commands::New.process(args, options) + end + end + end + + def process(args, options = {}) + raise ArgumentError, "You must specify a path." if args.empty? + + new_blog_path = File.expand_path(args.join(" "), Dir.pwd) + FileUtils.mkdir_p new_blog_path + if preserve_source_location?(new_blog_path, options) + Jekyll.logger.error "Conflict:", "#{new_blog_path} exists and is not empty." + Jekyll.logger.abort_with "", "Ensure #{new_blog_path} is empty or else try again " \ + "with `--force` to proceed and overwrite any files." + end + + if options["blank"] + create_blank_site new_blog_path + else + create_site new_blog_path + end + + after_install(new_blog_path, options) + end + + def blank_template + File.expand_path("../../blank_template", __dir__) + end + + def create_blank_site(path) + FileUtils.cp_r blank_template + "/.", path + FileUtils.chmod_R "u+w", path + + Dir.chdir(path) do + FileUtils.mkdir(%w(_data _drafts _includes _posts)) + end + end + + def scaffold_post_content + ERB.new(File.read(File.expand_path(scaffold_path, site_template))).result + end + + # Internal: Gets the filename of the sample post to be created + # + # Returns the filename of the sample post, as a String + def initialized_post_name + "_posts/#{Time.now.strftime("%Y-%m-%d")}-welcome-to-jekyll.markdown" + end + + private + + def gemfile_contents + <<~RUBY + source "https://rubygems.org" + # Hello! This is where you manage which Jekyll version is used to run. + # When you want to use a different version, change it below, save the + # file and run `bundle install`. Run Jekyll with `bundle exec`, like so: + # + # bundle exec jekyll serve + # + # This will help ensure the proper Jekyll version is running. + # Happy Jekylling! + gem "jekyll", "~> #{Jekyll::VERSION}" + # This is the default theme for new Jekyll sites. You may change this to anything you like. + gem "minima", "~> 2.5" + # If you want to use GitHub Pages, remove the "gem "jekyll"" above and + # uncomment the line below. To upgrade, run `bundle update github-pages`. + # gem "github-pages", group: :jekyll_plugins + # If you have any plugins, put them here! + group :jekyll_plugins do + gem "jekyll-feed", "~> 0.12" + end + + # Windows and JRuby does not include zoneinfo files, so bundle the tzinfo-data gem + # and associated library. + platforms :mingw, :x64_mingw, :mswin, :jruby do + gem "tzinfo", ">= 1", "< 3" + gem "tzinfo-data" + end + + # Performance-booster for watching directories on Windows + gem "wdm", "~> 0.1.1", :platforms => [:mingw, :x64_mingw, :mswin] + + # Lock `http_parser.rb` gem to `v0.6.x` on JRuby builds since newer versions of the gem + # do not have a Java counterpart. + gem "http_parser.rb", "~> 0.6.0", :platforms => [:jruby] + RUBY + end + + def create_site(new_blog_path) + create_sample_files new_blog_path + + File.write(File.expand_path(initialized_post_name, new_blog_path), scaffold_post_content) + + File.write(File.expand_path("Gemfile", new_blog_path), gemfile_contents) + end + + def preserve_source_location?(path, options) + !options["force"] && !Dir["#{path}/**/*"].empty? + end + + def create_sample_files(path) + FileUtils.cp_r site_template + "/.", path + FileUtils.chmod_R "u+w", path + FileUtils.rm File.expand_path(scaffold_path, path) + end + + def site_template + File.expand_path("../../site_template", __dir__) + end + + def scaffold_path + "_posts/0000-00-00-welcome-to-jekyll.markdown.erb" + end + + # After a new blog has been created, print a success notification and + # then automatically execute bundle install from within the new blog dir + # unless the user opts to generate a blank blog or skip 'bundle install'. + + def after_install(path, options = {}) + unless options["blank"] || options["skip-bundle"] + begin + require "bundler" + bundle_install path + rescue LoadError + Jekyll.logger.info "Could not load Bundler. Bundle install skipped." + end + end + + Jekyll.logger.info "New jekyll site installed in #{path.cyan}." + Jekyll.logger.info "Bundle install skipped." if options["skip-bundle"] + end + + def bundle_install(path) + Jekyll.logger.info "Running bundle install in #{path.cyan}..." + Dir.chdir(path) do + exe = Gem.bin_path("bundler", "bundle") + process, output = Jekyll::Utils::Exec.run("ruby", exe, "install") + + output.to_s.each_line do |line| + Jekyll.logger.info("Bundler:".green, line.strip) unless line.to_s.empty? + end + + raise SystemExit unless process.success? + end + end + end + end + end +end diff --git a/lib/jekyll/commands/new_theme.rb b/lib/jekyll/commands/new_theme.rb new file mode 100644 index 0000000..3419d17 --- /dev/null +++ b/lib/jekyll/commands/new_theme.rb @@ -0,0 +1,39 @@ +# frozen_string_literal: true + +require "erb" + +module Jekyll + module Commands + class NewTheme < Jekyll::Command + class << self + def init_with_program(prog) + prog.command(:"new-theme") do |c| + c.syntax "new-theme NAME" + c.description "Creates a new Jekyll theme scaffold" + c.option "code_of_conduct", "-c", "--code-of-conduct", + "Include a Code of Conduct. (defaults to false)" + + c.action do |args, opts| + Jekyll::Commands::NewTheme.process(args, opts) + end + end + end + + def process(args, opts) + if !args || args.empty? + raise Jekyll::Errors::InvalidThemeName, "You must specify a theme name." + end + + new_theme_name = args.join("_") + theme = Jekyll::ThemeBuilder.new(new_theme_name, opts) + Jekyll.logger.abort_with "Conflict:", "#{theme.path} already exists." if theme.path.exist? + + theme.create! + Jekyll.logger.info "Your new Jekyll theme, #{theme.name.cyan}, " \ + "is ready for you in #{theme.path.to_s.cyan}!" + Jekyll.logger.info "For help getting started, read #{theme.path}/README.md." + end + end + end + end +end diff --git a/lib/jekyll/commands/serve.rb b/lib/jekyll/commands/serve.rb new file mode 100644 index 0000000..4600130 --- /dev/null +++ b/lib/jekyll/commands/serve.rb @@ -0,0 +1,367 @@ +# frozen_string_literal: true + +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", + "Show a directory listing instead of loading " \ + "your index file.",], + "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( + index.htm + index.html + index.rhtml + index.xht + index.xhtml + index.cgi + index.xml + 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| + cmd.description "Serve your site locally" + cmd.syntax "serve [options]" + cmd.alias :server + cmd.alias :s + + add_build_options(cmd) + COMMAND_OPTIONS.each do |key, val| + cmd.option key, *val + end + + cmd.action do |_, opts| + opts["livereload_port"] ||= LIVERELOAD_PORT + opts["serving"] = true + opts["watch"] = true unless opts.key?("watch") + + # Set the reactor to nil so any old reactor will be GCed. + # We can't unregister a hook so while running tests we don't want to + # inadvertently keep using a reactor created by a previous test. + @reload_reactor = nil + + config = configuration_from_options(opts) + config["url"] = default_url(config) if Jekyll.env == "development" + + process_with_graceful_fail(cmd, config, Build, Serve) + end + end + end + + # + + def process(opts) + opts = configuration_from_options(opts) + destination = opts["destination"] + if opts["livereload"] + validate_options(opts) + register_reload_hooks(opts) + end + setup(destination) + + 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 + + # 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| + @changed_pages = [] + site.each_site_file do |item| + @changed_pages << item if site.regenerator.regenerate?(item) + 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 + # rubocop:enable Metrics/AbcSize + + # 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. + # + # rubocop:disable Security/IoMethods + def setup(destination) + require_relative "serve/servlet" + + FileUtils.mkdir_p(destination) + if File.exist?(File.join(destination, "404.html")) + WEBrick::HTTPResponse.class_eval do + def create_error_page + @header["Content-Type"] = "text/html; charset=UTF-8" + @body = IO.read(File.join(@config[:DocumentRoot], "404.html")) + end + end + end + end + # rubocop:enable Security/IoMethods + + def webrick_opts(opts) + opts = { + :JekyllOptions => opts, + :DoNotReverseLookup => true, + :MimeTypes => mime_types, + :MimeTypesCharset => mime_types_charset, + :DocumentRoot => opts["destination"], + :StartCallback => start_callback(opts["detach"]), + :StopCallback => stop_callback(opts["detach"]), + :BindAddress => opts["host"], + :Port => opts["port"], + :DirectoryIndex => DIRECTORY_INDEX, + } + + opts[:DirectoryIndex] = [] if opts[:JekyllOptions]["show_dir_listing"] + + enable_ssl(opts) + enable_logging(opts) + opts + end + + def start_up_webrick(opts, destination) + @reload_reactor.start(opts) if opts["livereload"] + + @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 + def file_handler_opts + WEBrick::Config::FileHandler.merge( + :FancyIndexing => true, + :NondisclosureName => [ + ".ht*", "~*", + ] + ) + end + + def server_address(server, options = {}) + format_url( + server.config[:SSLEnable], + server.config[:BindAddress], + server.config[:Port], + options["baseurl"] + ) + end + + def format_url(ssl_enabled, address, port, baseurl = nil) + format("%<prefix>s://%<address>s:%<port>i%<baseurl>s", + :prefix => ssl_enabled ? "https" : "http", + :address => address, + :port => port, + :baseurl => baseurl ? "#{baseurl}/" : "") + end + + def default_url(opts) + config = configuration_from_options(opts) + format_url( + config["ssl_cert"] && config["ssl_key"], + config["host"] == "127.0.0.1" ? "localhost" : config["host"], + config["port"] + ) + end + + def launch_browser(server, opts) + address = server_address(server, opts) + return system "start", address if Utils::Platforms.windows? + return system "xdg-open", address if Utils::Platforms.linux? + return system "open", address if Utils::Platforms.osx? + + Jekyll.logger.error "Refusing to launch browser. Platform launcher unknown." + end + + # Keep in our area with a thread or detach the server as requested + # by the user. This method determines what we do based on what you + # ask us to do. + def boot_or_detach(server, opts) + if opts["detach"] + pid = Process.fork do + server.start + end + + Process.detach(pid) + Jekyll.logger.info "Server detached with pid '#{pid}'.", + "Run `pkill -f jekyll' or `kill -9 #{pid}' to stop the server." + else + t = Thread.new { server.start } + trap("INT") { server.shutdown } + t.join + end + end + + # Make the stack verbose if the user requests it. + def enable_logging(opts) + opts[:AccessLog] = [] + level = WEBrick::Log.const_get(opts[:JekyllOptions]["verbose"] ? :DEBUG : :WARN) + opts[:Logger] = WEBrick::Log.new($stdout, level) + end + + # Add SSL to the stack if the user triggers --enable-ssl and they + # provide both types of certificates commonly needed. Raise if they + # forget to add one of the certificates. + def enable_ssl(opts) + cert, key, src = + opts[:JekyllOptions].values_at("ssl_cert", "ssl_key", "source") + + return if cert.nil? && key.nil? + raise "Missing --ssl_cert or --ssl_key. Both are required." unless cert && key + + require "openssl" + require "webrick/https" + + opts[:SSLCertificate] = OpenSSL::X509::Certificate.new(read_file(src, cert)) + begin + opts[:SSLPrivateKey] = OpenSSL::PKey::RSA.new(read_file(src, key)) + rescue StandardError + if defined?(OpenSSL::PKey::EC) + opts[:SSLPrivateKey] = OpenSSL::PKey::EC.new(read_file(src, key)) + else + raise + end + end + opts[:SSLEnable] = true + end + + def start_callback(detached) + unless detached + proc do + mutex.synchronize do + # Block until EventMachine reactor starts + @reload_reactor&.started_event&.wait + @running = true + Jekyll.logger.info("Server running...", "press ctrl-c to stop.") + @run_cond.broadcast + end + end + end + end + + 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 + + def mime_types + file = File.expand_path("../mime.types", __dir__) + WEBrick::HTTPUtils.load_mime_types(file) + end + + def mime_types_charset + SafeYAML.load_file(File.expand_path("serve/mime_types_charset.json", __dir__)) + end + + def read_file(source_dir, file_path) + File.read(Jekyll.sanitized_path(source_dir, file_path)) + end + 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 0000000..78f9408 --- /dev/null +++ b/lib/jekyll/commands/serve/live_reload_reactor.rb @@ -0,0 +1,119 @@ +# frozen_string_literal: true + +require "em-websocket" + +require_relative "websockets" + +module Jekyll + module Commands + class Serve + class LiveReloadReactor + attr_reader :started_event, :stopped_event, :thread + + def initialize + @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(websocket) + websocket.onopen { |handshake| connect(websocket, handshake) } + websocket.onclose { disconnect(websocket) } + websocket.onmessage { |msg| print_message(msg) } + websocket.onerror { |error| log_error(error) } + end + + def start(opts) + @thread = Thread.new do + # Use epoll if the kernel supports it + EM.epoll + EM.run do + EM.error_handler { |e| log_error(e) } + + 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 { @started_event.set } + EM.add_shutdown_hook { @stopped_event.set } + + Jekyll.logger.info "LiveReload address:", + "http://#{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| + json_message = JSON.dump( + :command => "reload", + :path => p.url, + :liveCSS => true + ) + + Jekyll.logger.debug "LiveReload:", "Reloading URL #{p.url.inspect}" + @websockets.each { |ws| ws.send(json_message) } + end + end + + private + + def connect(websocket, handshake) + @connections_count += 1 + if @connections_count == 1 + message = "Browser connected" + message += " over SSL/TLS" if handshake.secure? + Jekyll.logger.info "LiveReload:", message + end + websocket.send( + JSON.dump( + :command => "hello", + :protocols => ["http://livereload.com/protocols/official-7"], + :serverName => "jekyll" + ) + ) + + @websockets << websocket + end + + def disconnect(websocket) + @websockets.delete(websocket) + end + + 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. + Jekyll.logger.info "LiveReload:", "Browser URL: #{msg["url"]}" if msg["command"] == "url" + end + + def log_error(error) + Jekyll.logger.error "LiveReload experienced an error. " \ + "Run with --trace for more information." + raise error + 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 0000000..eee60ec --- /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<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ +(function() { + var Connector, PROTOCOL_6, PROTOCOL_7, Parser, Version, _ref; + + _ref = require('./protocol'), Parser = _ref.Parser, PROTOCOL_6 = _ref.PROTOCOL_6, PROTOCOL_7 = _ref.PROTOCOL_7; + + Version = '2.2.2'; + + exports.Connector = Connector = (function() { + function Connector(options, WebSocket, Timer, handlers) { + this.options = options; + this.WebSocket = WebSocket; + this.Timer = Timer; + this.handlers = handlers; + this._uri = "ws" + (this.options.https ? "s" : "") + "://" + this.options.host + ":" + this.options.port + "/livereload"; + this._nextDelay = this.options.mindelay; + this._connectionDesired = false; + this.protocol = 0; + this.protocolParser = new Parser({ + connected: (function(_this) { + return function(protocol) { + _this.protocol = protocol; + _this._handshakeTimeout.stop(); + _this._nextDelay = _this.options.mindelay; + _this._disconnectionReason = 'broken'; + return _this.handlers.connected(protocol); + }; + })(this), + error: (function(_this) { + return function(e) { + _this.handlers.error(e); + return _this._closeOnError(); + }; + })(this), + message: (function(_this) { + return function(message) { + return _this.handlers.message(message); + }; + })(this) + }); + this._handshakeTimeout = new Timer((function(_this) { + return function() { + if (!_this._isSocketConnected()) { + return; + } + _this._disconnectionReason = 'handshake-timeout'; + return _this.socket.close(); + }; + })(this)); + this._reconnectTimer = new Timer((function(_this) { + return function() { + if (!_this._connectionDesired) { + return; + } + return _this.connect(); + }; + })(this)); + this.connect(); + } + + Connector.prototype._isSocketConnected = function() { + return this.socket && this.socket.readyState === this.WebSocket.OPEN; + }; + + Connector.prototype.connect = function() { + this._connectionDesired = true; + if (this._isSocketConnected()) { + return; + } + this._reconnectTimer.stop(); + this._disconnectionReason = 'cannot-connect'; + this.protocolParser.reset(); + this.handlers.connecting(); + this.socket = new this.WebSocket(this._uri); + this.socket.onopen = (function(_this) { + return function(e) { + return _this._onopen(e); + }; + })(this); + this.socket.onclose = (function(_this) { + return function(e) { + return _this._onclose(e); + }; + })(this); + this.socket.onmessage = (function(_this) { + return function(e) { + return _this._onmessage(e); + }; + })(this); + return this.socket.onerror = (function(_this) { + return function(e) { + return _this._onerror(e); + }; + })(this); + }; + + Connector.prototype.disconnect = function() { + this._connectionDesired = false; + this._reconnectTimer.stop(); + if (!this._isSocketConnected()) { + return; + } + this._disconnectionReason = 'manual'; + return this.socket.close(); + }; + + Connector.prototype._scheduleReconnection = function() { + if (!this._connectionDesired) { + return; + } + if (!this._reconnectTimer.running) { + this._reconnectTimer.start(this._nextDelay); + return this._nextDelay = Math.min(this.options.maxdelay, this._nextDelay * 2); + } + }; + + Connector.prototype.sendCommand = function(command) { + if (this.protocol == null) { + return; + } + return this._sendCommand(command); + }; + + Connector.prototype._sendCommand = function(command) { + return this.socket.send(JSON.stringify(command)); + }; + + Connector.prototype._closeOnError = function() { + this._handshakeTimeout.stop(); + this._disconnectionReason = 'error'; + return this.socket.close(); + }; + + Connector.prototype._onopen = function(e) { + var hello; + this.handlers.socketConnected(); + this._disconnectionReason = 'handshake-failed'; + hello = { + command: 'hello', + protocols: [PROTOCOL_6, PROTOCOL_7] + }; + hello.ver = Version; + if (this.options.ext) { + hello.ext = this.options.ext; + } + if (this.options.extver) { + hello.extver = this.options.extver; + } + if (this.options.snipver) { + hello.snipver = this.options.snipver; + } + this._sendCommand(hello); + return this._handshakeTimeout.start(this.options.handshake_timeout); + }; + + Connector.prototype._onclose = function(e) { + this.protocol = 0; + this.handlers.disconnected(this._disconnectionReason, this._nextDelay); + return this._scheduleReconnection(); + }; + + Connector.prototype._onerror = function(e) {}; + + Connector.prototype._onmessage = function(e) { + return this.protocolParser.process(e.data); + }; + + return Connector; + + })(); + +}).call(this); + +},{"./protocol":6}],2:[function(require,module,exports){ +(function() { + var CustomEvents; + + CustomEvents = { + bind: function(element, eventName, handler) { + if (element.addEventListener) { + return element.addEventListener(eventName, handler, false); + } else if (element.attachEvent) { + element[eventName] = 1; + return element.attachEvent('onpropertychange', function(event) { + if (event.propertyName === eventName) { + return handler(); + } + }); + } else { + throw new Error("Attempt to attach custom event " + eventName + " to something which isn't a DOMElement"); + } + }, + fire: function(element, eventName) { + var event; + if (element.addEventListener) { + event = document.createEvent('HTMLEvents'); + event.initEvent(eventName, true, true); + return document.dispatchEvent(event); + } else if (element.attachEvent) { + if (element[eventName]) { + return element[eventName]++; + } + } else { + throw new Error("Attempt to fire custom event " + eventName + " on something which isn't a DOMElement"); + } + } + }; + + exports.bind = CustomEvents.bind; + + exports.fire = CustomEvents.fire; + +}).call(this); + +},{}],3:[function(require,module,exports){ +(function() { + var LessPlugin; + + module.exports = LessPlugin = (function() { + LessPlugin.identifier = 'less'; + + LessPlugin.version = '1.0'; + + function LessPlugin(window, host) { + this.window = window; + this.host = host; + } + + LessPlugin.prototype.reload = function(path, options) { + if (this.window.less && this.window.less.refresh) { + if (path.match(/\.less$/i)) { + return this.reloadLess(path); + } + if (options.originalPath.match(/\.less$/i)) { + return this.reloadLess(options.originalPath); + } + } + return false; + }; + + LessPlugin.prototype.reloadLess = function(path) { + var link, links, _i, _len; + links = (function() { + var _i, _len, _ref, _results; + _ref = document.getElementsByTagName('link'); + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + link = _ref[_i]; + if (link.href && link.rel.match(/^stylesheet\/less$/i) || (link.rel.match(/stylesheet/i) && link.type.match(/^text\/(x-)?less$/i))) { + _results.push(link); + } + } + return _results; + })(); + if (links.length === 0) { + return false; + } + for (_i = 0, _len = links.length; _i < _len; _i++) { + link = links[_i]; + link.href = this.host.generateCacheBustUrl(link.href); + } + this.host.console.log("LiveReload is asking LESS to recompile all stylesheets"); + this.window.less.refresh(true); + return true; + }; + + LessPlugin.prototype.analyze = function() { + return { + disable: !!(this.window.less && this.window.less.refresh) + }; + }; + + return LessPlugin; + + })(); + +}).call(this); + +},{}],4:[function(require,module,exports){ +(function() { + var Connector, LiveReload, Options, Reloader, Timer, + __hasProp = {}.hasOwnProperty; + + Connector = require('./connector').Connector; + + Timer = require('./timer').Timer; + + Options = require('./options').Options; + + Reloader = require('./reloader').Reloader; + + exports.LiveReload = LiveReload = (function() { + function LiveReload(window) { + var k, v, _ref; + this.window = window; + this.listeners = {}; + this.plugins = []; + this.pluginIdentifiers = {}; + this.console = this.window.console && this.window.console.log && this.window.console.error ? this.window.location.href.match(/LR-verbose/) ? this.window.console : { + log: function() {}, + error: this.window.console.error.bind(this.window.console) + } : { + log: function() {}, + error: function() {} + }; + if (!(this.WebSocket = this.window.WebSocket || this.window.MozWebSocket)) { + this.console.error("LiveReload disabled because the browser does not seem to support web sockets"); + return; + } + if ('LiveReloadOptions' in window) { + this.options = new Options(); + _ref = window['LiveReloadOptions']; + for (k in _ref) { + if (!__hasProp.call(_ref, k)) continue; + v = _ref[k]; + this.options.set(k, v); + } + } else { + this.options = Options.extract(this.window.document); + if (!this.options) { + this.console.error("LiveReload disabled because it could not find its own <SCRIPT> 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/mime_types_charset.json b/lib/jekyll/commands/serve/mime_types_charset.json new file mode 100644 index 0000000..017469b --- /dev/null +++ b/lib/jekyll/commands/serve/mime_types_charset.json @@ -0,0 +1,71 @@ +{ + "application/javascript": "UTF-8", + "application/json": "UTF-8", + "application/manifest+json": "UTF-8", + "application/vnd.syncml+xml": "UTF-8", + "application/vnd.syncml.dm+wbxml": "UTF-8", + "application/vnd.syncml.dm+xml": "UTF-8", + "application/vnd.syncml.dmddf+xml": "UTF-8", + "application/vnd.wap.wbxml": "UTF-8", + "text/cache-manifest": "UTF-8", + "text/calendar": "UTF-8", + "text/coffeescript": "UTF-8", + "text/css": "UTF-8", + "text/csv": "UTF-8", + "text/html": "UTF-8", + "text/jade": "UTF-8", + "text/jsx": "UTF-8", + "text/less": "UTF-8", + "text/markdown": "UTF-8", + "text/mathml": "UTF-8", + "text/mdx": "UTF-8", + "text/n3": "UTF-8", + "text/plain": "UTF-8", + "text/prs.lines.tag": "UTF-8", + "text/richtext": "UTF-8", + "text/sgml": "UTF-8", + "text/shex": "UTF-8", + "text/slim": "UTF-8", + "text/spdx": "UTF-8", + "text/stylus": "UTF-8", + "text/tab-separated-values": "UTF-8", + "text/troff": "UTF-8", + "text/turtle": "UTF-8", + "text/uri-list": "UTF-8", + "text/vcard": "UTF-8", + "text/vnd.curl": "UTF-8", + "text/vnd.curl.dcurl": "UTF-8", + "text/vnd.curl.mcurl": "UTF-8", + "text/vnd.curl.scurl": "UTF-8", + "text/vnd.familysearch.gedcom": "UTF-8", + "text/vnd.fly": "UTF-8", + "text/vnd.fmi.flexstor": "UTF-8", + "text/vnd.graphviz": "UTF-8", + "text/vnd.in3d.3dml": "UTF-8", + "text/vnd.in3d.spot": "UTF-8", + "text/vnd.sun.j2me.app-descriptor": "UTF-8", + "text/vnd.wap.wml": "UTF-8", + "text/vnd.wap.wmlscript": "UTF-8", + "text/vtt": "UTF-8", + "text/x-asm": "UTF-8", + "text/x-c": "UTF-8", + "text/x-component": "UTF-8", + "text/x-fortran": "UTF-8", + "text/x-handlebars-template": "UTF-8", + "text/x-java-source": "UTF-8", + "text/x-lua": "UTF-8", + "text/x-markdown": "UTF-8", + "text/x-nfo": "UTF-8", + "text/x-opml": "UTF-8", + "text/x-pascal": "UTF-8", + "text/x-processing": "UTF-8", + "text/x-sass": "UTF-8", + "text/x-scss": "UTF-8", + "text/x-setext": "UTF-8", + "text/x-sfv": "UTF-8", + "text/x-suse-ymp": "UTF-8", + "text/x-uuencode": "UTF-8", + "text/x-vcalendar": "UTF-8", + "text/x-vcard": "UTF-8", + "text/yaml": "UTF-8" +} diff --git a/lib/jekyll/commands/serve/servlet.rb b/lib/jekyll/commands/serve/servlet.rb new file mode 100644 index 0000000..c8895e5 --- /dev/null +++ b/lib/jekyll/commands/serve/servlet.rb @@ -0,0 +1,206 @@ +# frozen_string_literal: true + +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"].to_s.start_with?("inline") + end + + def bad_browser? + BAD_USER_AGENTS.any? { |pattern| pattern.match?(@request["User-Agent"]) } + end + + def html? + @response["Content-Type"].to_s.include?("text/html") + end + end + + # This class inserts the LiveReload script tags into HTML as it is served + class BodyProcessor + HEAD_TAG_REGEX = %r!<head>|<head[^(er)][^<]*>!.freeze + + 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["<head"] + line.gsub!(HEAD_TAG_REGEX) do |match| + %(#{match}#{template.result(binding)}) + end + + @livereload_added = true + end + + @content_length += line.bytesize + @processed = true + end + @new_body = @new_body.join + end + # rubocop:enable Metrics/MethodLength + + def template + # Unclear what "snipver" does. Doc at + # https://github.com/livereload/livereload-js states that the recommended + # setting is 1. + + # Complicated JavaScript to ensure that livereload.js is loaded from the + # same origin as the page. Mostly useful for dealing with the browser's + # distinction between 'localhost' and 127.0.0.1 + @template ||= ERB.new(<<~TEMPLATE) + <script> + document.write( + '<script src="' + location.protocol + '//' + + (location.host || 'localhost').split(':')[0] + + ':<%=@options["livereload_port"] %>/livereload.js?snipver=1<%= livereload_args %>"' + + '></' + + 'script>'); + </script> + 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 + src += "&port=#{@options["livereload_port"]}" if @options["livereload_port"] + src + end + end + + class Servlet < WEBrick::HTTPServlet::FileHandler + DEFAULTS = { + "Cache-Control" => "private, max-age=0, proxy-revalidate, " \ + "no-store, no-cache, must-revalidate", + }.freeze + + def initialize(server, root, callbacks) + # So we can access them easily. + @jekyll_opts = server.config[:JekyllOptions] + @mime_types_charset = server.config[:MimeTypesCharset] + set_defaults + super + end + + def search_index_file(req, res) + super || + search_file(req, res, ".html") || + search_file(req, res, ".xhtml") + 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, "#{basename}.html") || + super(req, res, "#{basename}.xhtml") + end + + # 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 + + conditionally_inject_charset(res) + res.header.merge!(@headers) + rtn + end + # rubocop:enable Naming/MethodName + + private + + # Inject charset based on Jekyll config only if our mime-types database contains + # the charset metadata. + # + # Refer `script/vendor-mimes` in the repository for further details. + def conditionally_inject_charset(res) + typ = res.header["content-type"] + return unless @mime_types_charset.key?(typ) + return if %r!;\s*charset=!.match?(typ) + + res.header["content-type"] = "#{typ}; charset=#{@jekyll_opts["encoding"]}" + end + + def set_defaults + hash_ = @jekyll_opts.fetch("webrick", {}).fetch("headers", {}) + DEFAULTS.each_with_object(@headers = hash_) do |(key, val), hash| + hash[key] = val unless hash.key?(key) + end + end + end + end + end +end diff --git a/lib/jekyll/commands/serve/websockets.rb b/lib/jekyll/commands/serve/websockets.rb new file mode 100644 index 0000000..6aa522c --- /dev/null +++ b/lib/jekyll/commands/serve/websockets.rb @@ -0,0 +1,81 @@ +# 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.start_with?("/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 + # rubocop:enable Metrics/MethodLength + end + end + end +end diff --git a/lib/jekyll/configuration.rb b/lib/jekyll/configuration.rb new file mode 100644 index 0000000..9b2fbff --- /dev/null +++ b/lib/jekyll/configuration.rb @@ -0,0 +1,313 @@ +# frozen_string_literal: true + +module Jekyll + class Configuration < Hash + # Default options. Overridden by values in _config.yml. + # Strings rather than symbols are used for compatibility with YAML. + DEFAULTS = { + # Where things are + "source" => Dir.pwd, + "destination" => File.join(Dir.pwd, "_site"), + "collections_dir" => "", + "cache_dir" => ".jekyll-cache", + "plugins_dir" => "_plugins", + "layouts_dir" => "_layouts", + "data_dir" => "_data", + "includes_dir" => "_includes", + "collections" => {}, + + # Handling Reading + "safe" => false, + "include" => [".htaccess"], + "exclude" => [], + "keep_files" => [".git", ".svn"], + "encoding" => "utf-8", + "markdown_ext" => "markdown,mkdown,mkdn,mkd,md", + "strict_front_matter" => false, + + # Filtering Content + "show_drafts" => nil, + "limit_posts" => 0, + "future" => false, + "unpublished" => false, + + # Plugins + "whitelist" => [], + "plugins" => [], + + # Conversion + "markdown" => "kramdown", + "highlighter" => "rouge", + "lsi" => false, + "excerpt_separator" => "\n\n", + "incremental" => false, + + # Serving + "detach" => false, # default to not detaching the server + "port" => "4000", + "host" => "127.0.0.1", + "baseurl" => nil, # this mounts at /, i.e. no subdirectory + "show_dir_listing" => false, + + # Output Configuration + "permalink" => "date", + "paginate_path" => "/page:num", + "timezone" => nil, # use the local timezone + + "quiet" => false, + "verbose" => false, + "defaults" => [], + + "liquid" => { + "error_mode" => "warn", + "strict_filters" => false, + "strict_variables" => false, + }, + + "kramdown" => { + "auto_ids" => true, + "toc_levels" => (1..6).to_a, + "entity_output" => "as_char", + "smart_quotes" => "lsquo,rsquo,ldquo,rdquo", + "input" => "GFM", + "hard_wrap" => false, + "guess_lang" => true, + "footnote_nr" => 1, + "show_warnings" => false, + }, + }.each_with_object(Configuration.new) { |(k, v), hsh| hsh[k] = v.freeze }.freeze + + class << self + # Static: Produce a Configuration ready for use in a Site. + # It takes the input, fills in the defaults where values do not exist. + # + # user_config - a Hash or Configuration of overrides. + # + # Returns a Configuration filled with defaults. + def from(user_config) + Utils.deep_merge_hashes(DEFAULTS, Configuration[user_config].stringify_keys) + .add_default_collections.add_default_excludes + end + end + + # Public: Turn all keys into string + # + # Return a copy of the hash where all its keys are strings + def stringify_keys + each_with_object({}) { |(k, v), hsh| hsh[k.to_s] = v } + end + + def get_config_value_with_override(config_key, override) + override[config_key] || self[config_key] || DEFAULTS[config_key] + end + + # Public: Directory of the Jekyll source folder + # + # override - the command-line options hash + # + # Returns the path to the Jekyll source directory + def source(override) + get_config_value_with_override("source", override) + end + + def quiet(override = {}) + get_config_value_with_override("quiet", override) + end + alias_method :quiet?, :quiet + + def verbose(override = {}) + get_config_value_with_override("verbose", override) + end + alias_method :verbose?, :verbose + + def safe_load_file(filename) + case File.extname(filename) + when %r!\.toml!i + Jekyll::External.require_with_graceful_fail("tomlrb") unless defined?(Tomlrb) + Tomlrb.load_file(filename) + when %r!\.ya?ml!i + SafeYAML.load_file(filename) || {} + else + raise ArgumentError, + "No parser for '#{filename}' is available. Use a .y(a)ml or .toml file instead." + end + end + + # Public: Generate list of configuration files from the override + # + # override - the command-line options hash + # + # Returns an Array of config files + def config_files(override) + # Adjust verbosity quickly + Jekyll.logger.adjust_verbosity( + :quiet => quiet?(override), + :verbose => verbose?(override) + ) + + # Get configuration from <source>/_config.yml or <source>/<config_file> + config_files = override["config"] + if config_files.to_s.empty? + default = %w(yml yaml toml).find(-> { "yml" }) do |ext| + File.exist?(Jekyll.sanitized_path(source(override), "_config.#{ext}")) + end + config_files = Jekyll.sanitized_path(source(override), "_config.#{default}") + @default_config_file = true + end + Array(config_files) + end + + # Public: Read configuration and return merged Hash + # + # file - the path to the YAML file to be read in + # + # Returns this configuration, overridden by the values in the file + def read_config_file(file) + file = File.expand_path(file) + next_config = safe_load_file(file) + + unless next_config.is_a?(Hash) + raise ArgumentError, "Configuration file: (INVALID) #{file}".yellow + end + + Jekyll.logger.info "Configuration file:", file + next_config + rescue SystemCallError + if @default_config_file ||= nil + Jekyll.logger.warn "Configuration file:", "none" + {} + else + Jekyll.logger.error "Fatal:", "The configuration file '#{file}' could not be found." + raise LoadError, "The Configuration file '#{file}' could not be found." + end + end + + # Public: Read in a list of configuration files and merge with this hash + # + # files - the list of configuration file paths + # + # Returns the full configuration, with the defaults overridden by the values in the + # configuration files + def read_config_files(files) + configuration = clone + + begin + files.each do |config_file| + next if config_file.nil? || config_file.empty? + + new_config = read_config_file(config_file) + configuration = Utils.deep_merge_hashes(configuration, new_config) + end + rescue ArgumentError => e + Jekyll.logger.warn "WARNING:", "Error reading configuration. Using defaults (and options)." + warn e + end + + configuration.validate.add_default_collections + end + + # Public: Split a CSV string into an array containing its values + # + # csv - the string of comma-separated values + # + # Returns an array of the values contained in the CSV + def csv_to_array(csv) + csv.split(",").map(&:strip) + end + + # Public: Ensure the proper options are set in the configuration + # + # Returns the configuration Hash + def validate + config = clone + + check_plugins(config) + check_include_exclude(config) + + config + end + + def add_default_collections + config = clone + + # It defaults to `{}`, so this is only if someone sets it to null manually. + return config if config["collections"].nil? + + # Ensure we have a hash. + if config["collections"].is_a?(Array) + config["collections"] = config["collections"].each_with_object({}) do |collection, hash| + hash[collection] = {} + end + end + + config["collections"] = Utils.deep_merge_hashes( + { "posts" => {} }, config["collections"] + ).tap do |collections| + collections["posts"]["output"] = true + if config["permalink"] + collections["posts"]["permalink"] ||= style_to_permalink(config["permalink"]) + end + end + + config + end + + DEFAULT_EXCLUDES = %w( + .sass-cache .jekyll-cache + gemfiles Gemfile Gemfile.lock + node_modules + vendor/bundle/ vendor/cache/ vendor/gems/ vendor/ruby/ + ).freeze + + def add_default_excludes + config = clone + return config if config["exclude"].nil? + + config["exclude"].concat(DEFAULT_EXCLUDES).uniq! + config + end + + private + + STYLE_TO_PERMALINK = { + :none => "/:categories/:title:output_ext", + :date => "/:categories/:year/:month/:day/:title:output_ext", + :ordinal => "/:categories/:year/:y_day/:title:output_ext", + :pretty => "/:categories/:year/:month/:day/:title/", + :weekdate => "/:categories/:year/W:week/:short_day/:title:output_ext", + }.freeze + + private_constant :STYLE_TO_PERMALINK + + def style_to_permalink(permalink_style) + STYLE_TO_PERMALINK[permalink_style.to_sym] || permalink_style.to_s + end + + def check_include_exclude(config) + %w(include exclude).each do |option| + next unless config.key?(option) + next if config[option].is_a?(Array) + + raise Jekyll::Errors::InvalidConfigurationError, + "'#{option}' should be set as an array, but was: #{config[option].inspect}." + end + end + + # Private: Checks if the `plugins` config is a String + # + # config - the config hash + # + # Raises a Jekyll::Errors::InvalidConfigurationError if the config `plugins` + # is not an Array. + def check_plugins(config) + return unless config.key?("plugins") + return if config["plugins"].is_a?(Array) + + Jekyll.logger.error "'plugins' should be set as an array of gem-names, but was: " \ + "#{config["plugins"].inspect}. Use 'plugins_dir' instead to set " \ + "the directory for your non-gemified Ruby plugins." + raise Jekyll::Errors::InvalidConfigurationError, + "'plugins' should be set as an array, but was: #{config["plugins"].inspect}." + end + end +end diff --git a/lib/jekyll/converter.rb b/lib/jekyll/converter.rb new file mode 100644 index 0000000..3cb74de --- /dev/null +++ b/lib/jekyll/converter.rb @@ -0,0 +1,54 @@ +# frozen_string_literal: true + +module Jekyll + class Converter < Plugin + # Public: Get or set the highlighter prefix. When an argument is specified, + # the prefix will be set. If no argument is specified, the current prefix + # will be returned. + # + # highlighter_prefix - The String prefix (default: nil). + # + # Returns the String prefix. + def self.highlighter_prefix(highlighter_prefix = nil) + unless defined?(@highlighter_prefix) && highlighter_prefix.nil? + @highlighter_prefix = highlighter_prefix + end + @highlighter_prefix + end + + # Public: Get or set the highlighter suffix. When an argument is specified, + # the suffix will be set. If no argument is specified, the current suffix + # will be returned. + # + # highlighter_suffix - The String suffix (default: nil). + # + # Returns the String suffix. + def self.highlighter_suffix(highlighter_suffix = nil) + unless defined?(@highlighter_suffix) && highlighter_suffix.nil? + @highlighter_suffix = highlighter_suffix + end + @highlighter_suffix + end + + # Initialize the converter. + # + # Returns an initialized Converter. + def initialize(config = {}) + @config = config + end + + # Get the highlighter prefix. + # + # Returns the String prefix. + def highlighter_prefix + self.class.highlighter_prefix + end + + # Get the highlighter suffix. + # + # Returns the String suffix. + def highlighter_suffix + self.class.highlighter_suffix + end + end +end diff --git a/lib/jekyll/converters/identity.rb b/lib/jekyll/converters/identity.rb new file mode 100644 index 0000000..7579b33 --- /dev/null +++ b/lib/jekyll/converters/identity.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +module Jekyll + module Converters + # Identity converter. Returns same content as given. + # For more info on converters see https://jekyllrb.com/docs/plugins/converters/ + class Identity < Converter + safe true + + priority :lowest + + # Public: Does the given extension match this converter's list of acceptable extensions? + # Takes one argument: the file's extension (including the dot). + # + # _ext - The String extension to check (not relevant here) + # + # Returns true since it always matches. + def matches(_ext) + true + end + + # Public: The extension to be given to the output file (including the dot). + # + # ext - The String extension or original file. + # + # Returns The String output file extension. + def output_ext(ext) + ext + end + + # Logic to do the content conversion. + # + # content - String content of file (without front matter). + # + # Returns a String of the converted content. + def convert(content) + content + end + end + end +end diff --git a/lib/jekyll/converters/markdown.rb b/lib/jekyll/converters/markdown.rb new file mode 100644 index 0000000..da1f1ce --- /dev/null +++ b/lib/jekyll/converters/markdown.rb @@ -0,0 +1,113 @@ +# frozen_string_literal: true + +module Jekyll + module Converters + # Markdown converter. + # For more info on converters see https://jekyllrb.com/docs/plugins/converters/ + class Markdown < Converter + highlighter_prefix "\n" + highlighter_suffix "\n" + safe true + + def setup + return if @setup ||= false + + unless (@parser = get_processor) + if @config["safe"] + Jekyll.logger.warn "Build Warning:", "Custom processors are not loaded in safe mode" + end + + Jekyll.logger.error "Markdown processor:", + "#{@config["markdown"].inspect} is not a valid Markdown processor." + Jekyll.logger.error "", "Available processors are: #{valid_processors.join(", ")}" + Jekyll.logger.error "" + raise Errors::FatalException, "Invalid Markdown processor given: #{@config["markdown"]}" + end + + @cache = Jekyll::Cache.new("Jekyll::Converters::Markdown") + @setup = true + end + + # RuboCop does not allow reader methods to have names starting with `get_` + # To ensure compatibility, this check has been disabled on this method + # + # rubocop:disable Naming/AccessorMethodName + def get_processor + case @config["markdown"].downcase + when "kramdown" then KramdownParser.new(@config) + else + custom_processor + end + end + # rubocop:enable Naming/AccessorMethodName + + # Public: Provides you with a list of processors comprised of the ones we support internally + # and the ones that you have provided to us (if they're whitelisted for use in safe mode). + # + # Returns an array of symbols. + def valid_processors + [:kramdown] + third_party_processors + end + + # Public: A list of processors that you provide via plugins. + # + # Returns an array of symbols + def third_party_processors + self.class.constants - [:KramdownParser, :PRIORITIES] + end + + # Does the given extension match this converter's list of acceptable extensions? + # Takes one argument: the file's extension (including the dot). + # + # ext - The String extension to check. + # + # Returns true if it matches, false otherwise. + def matches(ext) + extname_list.include?(ext.downcase) + end + + # Public: The extension to be given to the output file (including the dot). + # + # ext - The String extension or original file. + # + # Returns The String output file extension. + def output_ext(_ext) + ".html" + end + + # Logic to do the content conversion. + # + # content - String content of file (without front matter). + # + # Returns a String of the converted content. + def convert(content) + setup + @cache.getset(content) do + @parser.convert(content) + end + end + + def extname_list + @extname_list ||= @config["markdown_ext"].split(",").map! { |e| ".#{e.downcase}" } + end + + private + + def custom_processor + converter_name = @config["markdown"] + self.class.const_get(converter_name).new(@config) if custom_class_allowed?(converter_name) + end + + # Private: Determine whether a class name is an allowed custom + # markdown class name. + # + # parser_name - the name of the parser class + # + # Returns true if the parser name contains only alphanumeric characters and is defined + # within Jekyll::Converters::Markdown + def custom_class_allowed?(parser_name) + parser_name !~ %r![^A-Za-z0-9_]! && self.class.constants.include?(parser_name.to_sym) + end + end + end +end diff --git a/lib/jekyll/converters/markdown/kramdown_parser.rb b/lib/jekyll/converters/markdown/kramdown_parser.rb new file mode 100644 index 0000000..1e6a1de --- /dev/null +++ b/lib/jekyll/converters/markdown/kramdown_parser.rb @@ -0,0 +1,197 @@ +# frozen_string_literal: true + +module Kramdown + # A Kramdown::Document subclass meant to optimize memory usage from initializing + # a kramdown document for parsing. + # + # The optimization is by using the same options Hash (and its derivatives) for + # converting all Markdown documents in a Jekyll site. + class JekyllDocument < Document + class << self + attr_reader :options, :parser + + # The implementation is basically the core logic in +Kramdown::Document#initialize+ + # + # rubocop:disable Naming/MemoizedInstanceVariableName + def setup(options) + @cache ||= {} + + # reset variables on a subsequent set up with a different options Hash + unless @cache[:id] == options.hash + @options = @parser = nil + @cache[:id] = options.hash + end + + @options ||= Options.merge(options).freeze + @parser ||= begin + parser_name = (@options[:input] || "kramdown").to_s + parser_name = parser_name[0..0].upcase + parser_name[1..-1] + try_require("parser", parser_name) + + if Parser.const_defined?(parser_name) + Parser.const_get(parser_name) + else + raise Kramdown::Error, "kramdown has no parser to handle the specified " \ + "input format: #{@options[:input]}" + end + end + end + # rubocop:enable Naming/MemoizedInstanceVariableName + + private + + def try_require(type, name) + require "kramdown/#{type}/#{Utils.snake_case(name)}" + rescue LoadError + false + end + end + + def initialize(source, options = {}) + JekyllDocument.setup(options) + + @options = JekyllDocument.options + @root, @warnings = JekyllDocument.parser.parse(source, @options) + end + + # Use Kramdown::Converter::Html class to convert this document into HTML. + # + # The implementation is basically an optimized version of core logic in + # +Kramdown::Document#method_missing+ from kramdown-2.1.0. + def to_html + output, warnings = Kramdown::Converter::Html.convert(@root, @options) + @warnings.concat(warnings) + output + end + end +end + +# + +module Jekyll + module Converters + class Markdown + class KramdownParser + CODERAY_DEFAULTS = { + "css" => "style", + "bold_every" => 10, + "line_numbers" => "inline", + "line_number_start" => 1, + "tab_width" => 4, + "wrap" => "div", + }.freeze + + def initialize(config) + @main_fallback_highlighter = config["highlighter"] || "rouge" + @config = config["kramdown"] || {} + @highlighter = nil + setup + load_dependencies + end + + # Setup and normalize the configuration: + # * Create Kramdown if it doesn't exist. + # * Set syntax_highlighter, detecting enable_coderay and merging + # highlighter if none. + # * Merge kramdown[coderay] into syntax_highlighter_opts stripping coderay_. + # * Make sure `syntax_highlighter_opts` exists. + + def setup + @config["syntax_highlighter"] ||= highlighter + @config["syntax_highlighter_opts"] ||= {} + @config["syntax_highlighter_opts"]["default_lang"] ||= "plaintext" + @config["syntax_highlighter_opts"]["guess_lang"] = @config["guess_lang"] + @config["coderay"] ||= {} # XXX: Legacy. + modernize_coderay_config + end + + def convert(content) + document = Kramdown::JekyllDocument.new(content, @config) + html_output = document.to_html + if @config["show_warnings"] + document.warnings.each do |warning| + Jekyll.logger.warn "Kramdown warning:", warning + end + end + html_output + end + + private + + def load_dependencies + require "kramdown-parser-gfm" if @config["input"] == "GFM" + + if highlighter == "coderay" + Jekyll::External.require_with_graceful_fail("kramdown-syntax-coderay") + end + + # `mathjax` engine is bundled within kramdown-2.x and will be handled by + # kramdown itself. + if (math_engine = @config["math_engine"]) && math_engine != "mathjax" + Jekyll::External.require_with_graceful_fail("kramdown-math-#{math_engine}") + end + end + + # config[kramdown][syntax_highlighter] > + # config[kramdown][enable_coderay] > + # config[highlighter] + # Where `enable_coderay` is now deprecated because Kramdown + # supports Rouge now too. + def highlighter + return @highlighter if @highlighter + + if @config["syntax_highlighter"] + return @highlighter = @config[ + "syntax_highlighter" + ] + end + + @highlighter = if @config.key?("enable_coderay") && @config["enable_coderay"] + Jekyll::Deprecator.deprecation_message( + "You are using 'enable_coderay', " \ + "use syntax_highlighter: coderay in your configuration file." + ) + + "coderay" + else + @main_fallback_highlighter + end + end + + def strip_coderay_prefix(hash) + hash.each_with_object({}) do |(key, val), hsh| + cleaned_key = key.to_s.delete_prefix("coderay_") + + if key != cleaned_key + Jekyll::Deprecator.deprecation_message( + "You are using '#{key}'. Normalizing to #{cleaned_key}." + ) + end + + hsh[cleaned_key] = val + end + end + + # If our highlighter is CodeRay we go in to merge the CodeRay defaults + # with your "coderay" key if it's there, deprecating it in the + # process of you using it. + def modernize_coderay_config + unless @config["coderay"].empty? + Jekyll::Deprecator.deprecation_message( + "You are using 'kramdown.coderay' in your configuration, " \ + "please use 'syntax_highlighter_opts' instead." + ) + + @config["syntax_highlighter_opts"] = begin + strip_coderay_prefix( + @config["syntax_highlighter_opts"] \ + .merge(CODERAY_DEFAULTS) \ + .merge(@config["coderay"]) + ) + end + end + end + end + end + end +end diff --git a/lib/jekyll/converters/smartypants.rb b/lib/jekyll/converters/smartypants.rb new file mode 100644 index 0000000..606afdc --- /dev/null +++ b/lib/jekyll/converters/smartypants.rb @@ -0,0 +1,70 @@ +# frozen_string_literal: true + +module Kramdown + module Parser + class SmartyPants < Kramdown::Parser::Kramdown + def initialize(source, options) + super + @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 + end +end + +module Jekyll + module Converters + # SmartyPants converter. + # For more info on converters see https://jekyllrb.com/docs/plugins/converters/ + class SmartyPants < Converter + safe true + priority :low + + def initialize(config) + Jekyll::External.require_with_graceful_fail "kramdown" unless defined?(Kramdown) + @config = config["kramdown"].dup || {} + @config[:input] = :SmartyPants + end + + # Does the given extension match this converter's list of acceptable extensions? + # Takes one argument: the file's extension (including the dot). + # + # ext - The String extension to check. + # + # Returns true if it matches, false otherwise. + def matches(_ext) + false + end + + # Public: The extension to be given to the output file (including the dot). + # + # ext - The String extension or original file. + # + # Returns The String output file extension. + def output_ext(_ext) + nil + end + + # Logic to do the content conversion. + # + # content - String content of file (without front matter). + # + # Returns a String of the converted content. + def convert(content) + document = Kramdown::Document.new(content, @config) + 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 + html_output + end + end + end +end diff --git a/lib/jekyll/convertible.rb b/lib/jekyll/convertible.rb new file mode 100644 index 0000000..b9f7a93 --- /dev/null +++ b/lib/jekyll/convertible.rb @@ -0,0 +1,257 @@ +# frozen_string_literal: true + +# Convertible provides methods for converting a pagelike item +# from a certain type of markup into actual content +# +# Requires +# self.site -> Jekyll::Site +# self.content +# self.content= +# self.data= +# self.ext= +# self.output= +# self.name +# self.path +# self.type -> :page, :post or :draft + +module Jekyll + module Convertible + # Returns the contents as a String. + def to_s + content || "" + end + + # Whether the file is published or not, as indicated in YAML front-matter + def published? + !(data.key?("published") && data["published"] == false) + end + + # Read the YAML frontmatter. + # + # base - The String path to the dir containing the file. + # name - The String filename of the file. + # opts - optional parameter to File.read, default at site configs + # + # Returns nothing. + # rubocop:disable Metrics/AbcSize + def read_yaml(base, name, opts = {}) + filename = @path || site.in_source_dir(base, name) + Jekyll.logger.debug "Reading:", relative_path + + begin + self.content = File.read(filename, **Utils.merged_file_read_opts(site, opts)) + if content =~ Document::YAML_FRONT_MATTER_REGEXP + self.content = Regexp.last_match.post_match + self.data = SafeYAML.load(Regexp.last_match(1)) + end + rescue Psych::SyntaxError => e + Jekyll.logger.warn "YAML Exception reading #{filename}: #{e.message}" + raise e if site.config["strict_front_matter"] + rescue StandardError => e + Jekyll.logger.warn "Error reading file #{filename}: #{e.message}" + raise e if site.config["strict_front_matter"] + end + + self.data ||= {} + + validate_data! filename + validate_permalink! filename + + self.data + end + # rubocop:enable Metrics/AbcSize + + def validate_data!(filename) + unless self.data.is_a?(Hash) + raise Errors::InvalidYAMLFrontMatterError, + "Invalid YAML front matter in #{filename}" + end + end + + def validate_permalink!(filename) + if self.data["permalink"] == "" + raise Errors::InvalidPermalinkError, "Invalid permalink in #{filename}" + end + end + + # Transform the contents based on the content type. + # + # Returns the transformed contents. + def transform + renderer.convert(content) + end + + # Determine the extension depending on content_type. + # + # Returns the String extension for the output file. + # e.g. ".html" for an HTML output file. + def output_ext + renderer.output_ext + end + + # Determine which converter to use based on this convertible's + # extension. + # + # Returns the Converter instance. + def converters + renderer.converters + end + + # Render Liquid in the content + # + # content - the raw Liquid content to render + # payload - the payload for Liquid + # info - the info for Liquid + # + # Returns the converted content + def render_liquid(content, payload, info, path) + renderer.render_liquid(content, payload, info, path) + end + + # Convert this Convertible's data to a Hash suitable for use by Liquid. + # + # Returns the Hash representation of this Convertible. + def to_liquid(attrs = nil) + further_data = \ + (attrs || self.class::ATTRIBUTES_FOR_LIQUID).each_with_object({}) do |attribute, hsh| + hsh[attribute] = send(attribute) + end + + Utils.deep_merge_hashes defaults, Utils.deep_merge_hashes(data, further_data) + end + + # The type of a document, + # i.e., its classname downcase'd and to_sym'd. + # + # Returns the type of self. + def type + :pages if is_a?(Page) + end + + # returns the owner symbol for hook triggering + def hook_owner + :pages if is_a?(Page) + end + + # Determine whether the document is an asset file. + # Asset files include CoffeeScript files and Sass/SCSS files. + # + # Returns true if the extname belongs to the set of extensions + # that asset files use. + def asset_file? + sass_file? || coffeescript_file? + end + + # Determine whether the document is a Sass file. + # + # Returns true if extname == .sass or .scss, false otherwise. + def sass_file? + Jekyll::Document::SASS_FILE_EXTS.include?(ext) + end + + # Determine whether the document is a CoffeeScript file. + # + # Returns true if extname == .coffee, false otherwise. + def coffeescript_file? + ext == ".coffee" + end + + # Determine whether the file should be rendered with Liquid. + # + # Returns true if the file has Liquid Tags or Variables, false otherwise. + def render_with_liquid? + return false if data["render_with_liquid"] == false + + Jekyll::Utils.has_liquid_construct?(content) + end + + # Determine whether the file should be placed into layouts. + # + # Returns false if the document is an asset file or if the front matter + # specifies `layout: none` + def place_in_layout? + !(asset_file? || no_layout?) + end + + # Checks if the layout specified in the document actually exists + # + # layout - the layout to check + # + # Returns true if the layout is invalid, false if otherwise + def invalid_layout?(layout) + !data["layout"].nil? && layout.nil? && !(is_a? Jekyll::Excerpt) + end + + # Recursively render layouts + # + # layouts - a list of the layouts + # payload - the payload for Liquid + # info - the info for Liquid + # + # Returns nothing + def render_all_layouts(layouts, payload, info) + renderer.layouts = layouts + self.output = renderer.place_in_layouts(output, payload, info) + ensure + @renderer = nil # this will allow the modifications above to disappear + end + + # Add any necessary layouts to this convertible document. + # + # payload - The site payload Drop or Hash. + # layouts - A Hash of {"name" => "layout"}. + # + # Returns nothing. + def do_layout(payload, layouts) + self.output = renderer.tap do |doc_renderer| + doc_renderer.layouts = layouts + doc_renderer.payload = payload + end.run + + Jekyll.logger.debug "Post-Render Hooks:", relative_path + Jekyll::Hooks.trigger hook_owner, :post_render, self + ensure + @renderer = nil # this will allow the modifications above to disappear + end + + # Write the generated page file to the destination directory. + # + # dest - The String path to the destination dir. + # + # Returns nothing. + def write(dest) + path = destination(dest) + FileUtils.mkdir_p(File.dirname(path)) + Jekyll.logger.debug "Writing:", path + File.write(path, output, :mode => "wb") + Jekyll::Hooks.trigger hook_owner, :post_write, self + end + + # Accessor for data properties by Liquid. + # + # property - The String name of the property to retrieve. + # + # Returns the String value or nil if the property isn't included. + def [](property) + if self.class::ATTRIBUTES_FOR_LIQUID.include?(property) + send(property) + else + data[property] + end + end + + def renderer + @renderer ||= Jekyll::Renderer.new(site, self) + end + + private + + def defaults + @defaults ||= site.frontmatter_defaults.all(relative_path, type) + end + + def no_layout? + data["layout"] == "none" + end + end +end diff --git a/lib/jekyll/deprecator.rb b/lib/jekyll/deprecator.rb new file mode 100644 index 0000000..4ecb656 --- /dev/null +++ b/lib/jekyll/deprecator.rb @@ -0,0 +1,50 @@ +# frozen_string_literal: true + +module Jekyll + module Deprecator + extend self + + def process(args) + arg_is_present? args, "--server", "The --server command has been replaced by the \ + 'serve' subcommand." + arg_is_present? args, "--serve", "The --serve command has been replaced by the \ + 'serve' subcommand." + arg_is_present? args, "--no-server", "To build Jekyll without launching a server, \ + use the 'build' subcommand." + arg_is_present? args, "--auto", "The switch '--auto' has been replaced with \ + '--watch'." + arg_is_present? args, "--no-auto", "To disable auto-replication, simply leave off \ + the '--watch' switch." + arg_is_present? args, "--pygments", "The 'pygments' settings has been removed in \ + favour of 'highlighter'." + arg_is_present? args, "--paginate", "The 'paginate' setting can only be set in \ + your config files." + arg_is_present? args, "--url", "The 'url' setting can only be set in your \ + config files." + no_subcommand(args) + end + + def no_subcommand(args) + unless args.empty? || + args.first !~ %r(!/^--/!) || %w(--help --version).include?(args.first) + deprecation_message "Jekyll now uses subcommands instead of just switches. \ + Run `jekyll help` to find out more." + abort + end + end + + def arg_is_present?(args, deprecated_argument, message) + deprecation_message(message) if args.include?(deprecated_argument) + end + + def deprecation_message(message) + Jekyll.logger.warn "Deprecation:", message + end + + def defaults_deprecate_type(old, current) + Jekyll.logger.warn "Defaults:", "The '#{old}' type has become '#{current}'." + Jekyll.logger.warn "Defaults:", "Please update your front-matter defaults to use \ + 'type: #{current}'." + end + end +end diff --git a/lib/jekyll/document.rb b/lib/jekyll/document.rb new file mode 100644 index 0000000..2e39024 --- /dev/null +++ b/lib/jekyll/document.rb @@ -0,0 +1,543 @@ +# frozen_string_literal: true + +module Jekyll + class Document + include Comparable + extend Forwardable + + attr_reader :path, :site, :extname, :collection, :type + attr_accessor :content, :output + + def_delegator :self, :read_post_data, :post_read + + YAML_FRONT_MATTER_REGEXP = %r!\A(---\s*\n.*?\n?)^((---|\.\.\.)\s*$\n?)!m.freeze + DATELESS_FILENAME_MATCHER = %r!^(?:.+/)*(.*)(\.[^.]+)$!.freeze + DATE_FILENAME_MATCHER = %r!^(?>.+/)*?(\d{2,4}-\d{1,2}-\d{1,2})-([^/]*)(\.[^.]+)$!.freeze + + SASS_FILE_EXTS = %w(.sass .scss).freeze + YAML_FILE_EXTS = %w(.yaml .yml).freeze + + # + + # Class-wide cache to stash and retrieve regexp to detect "super-directories" + # of a particular Jekyll::Document object. + # + # dirname - The *special directory* for the Document. + # e.g. "_posts" or "_drafts" for Documents from the `site.posts` collection. + def self.superdirs_regex(dirname) + @superdirs_regex ||= {} + @superdirs_regex[dirname] ||= %r!#{dirname}.*! + end + + # + + # Create a new Document. + # + # path - the path to the file + # relations - a hash with keys :site and :collection, the values of which + # are the Jekyll::Site and Jekyll::Collection to which this + # Document belong. + # + # Returns nothing. + def initialize(path, relations = {}) + @site = relations[:site] + @path = path + @extname = File.extname(path) + @collection = relations[:collection] + @type = @collection.label.to_sym + + @has_yaml_header = nil + + if draft? + categories_from_path("_drafts") + else + categories_from_path(collection.relative_directory) + end + + data.default_proc = proc do |_, key| + site.frontmatter_defaults.find(relative_path, type, key) + end + + trigger_hooks(:post_init) + end + + # Fetch the Document's data. + # + # Returns a Hash containing the data. An empty hash is returned if + # no data was read. + def data + @data ||= {} + end + + # Merge some data in with this document's data. + # + # Returns the merged data. + def merge_data!(other, source: "YAML front matter") + merge_categories!(other) + Utils.deep_merge_hashes!(data, other) + merge_date!(source) + data + end + + # Returns the document date. If metadata is not present then calculates it + # based on Jekyll::Site#time or the document file modification time. + # + # Return document date string. + def date + data["date"] ||= (draft? ? source_file_mtime : site.time) + end + + # Return document file modification time in the form of a Time object. + # + # Return document file modification Time object. + def source_file_mtime + File.mtime(path) + end + + # Returns whether the document is a draft. This is only the case if + # the document is in the 'posts' collection but in a different + # directory than '_posts'. + # + # Returns whether the document is a draft. + def draft? + data["draft"] ||= relative_path.index(collection.relative_directory).nil? && + collection.label == "posts" + end + + # The path to the document, relative to the collections_dir. + # + # Returns a String path which represents the relative path from the collections_dir + # to this document. + def relative_path + @relative_path ||= path.sub("#{site.collections_path}/", "") + end + + # The output extension of the document. + # + # Returns the output extension + def output_ext + renderer.output_ext + end + + # The base filename of the document, without the file extname. + # + # Returns the basename without the file extname. + def basename_without_ext + @basename_without_ext ||= File.basename(path, ".*") + end + + # The base filename of the document. + # + # Returns the base filename of the document. + def basename + @basename ||= File.basename(path) + end + + def renderer + @renderer ||= Jekyll::Renderer.new(site, self) + end + + # Produces a "cleaned" relative path. + # The "cleaned" relative path is the relative path without the extname + # and with the collection's directory removed as well. + # This method is useful when building the URL of the document. + # + # NOTE: `String#gsub` removes all trailing periods (in comparison to `String#chomp`) + # + # Examples: + # When relative_path is "_methods/site/generate...md": + # cleaned_relative_path + # # => "/site/generate" + # + # Returns the cleaned relative path of the document. + def cleaned_relative_path + @cleaned_relative_path ||= + relative_path[0..-extname.length - 1] + .sub(collection.relative_directory, "") + .gsub(%r!\.*\z!, "") + end + + # Determine whether the document is a YAML file. + # + # Returns true if the extname is either .yml or .yaml, false otherwise. + def yaml_file? + YAML_FILE_EXTS.include?(extname) + end + + # Determine whether the document is an asset file. + # Asset files include CoffeeScript files and Sass/SCSS files. + # + # Returns true if the extname belongs to the set of extensions + # that asset files use. + def asset_file? + sass_file? || coffeescript_file? + end + + # Determine whether the document is a Sass file. + # + # Returns true if extname == .sass or .scss, false otherwise. + def sass_file? + SASS_FILE_EXTS.include?(extname) + end + + # Determine whether the document is a CoffeeScript file. + # + # Returns true if extname == .coffee, false otherwise. + def coffeescript_file? + extname == ".coffee" + end + + # Determine whether the file should be rendered with Liquid. + # + # Returns false if the document is either an asset file or a yaml file, + # or if the document doesn't contain any Liquid Tags or Variables, + # true otherwise. + def render_with_liquid? + return false if data["render_with_liquid"] == false + + !(coffeescript_file? || yaml_file? || !Utils.has_liquid_construct?(content)) + end + + # Determine whether the file should be rendered with a layout. + # + # Returns true if the Front Matter specifies that `layout` is set to `none`. + def no_layout? + data["layout"] == "none" + end + + # Determine whether the file should be placed into layouts. + # + # Returns false if the document is set to `layouts: none`, or is either an + # asset file or a yaml file. Returns true otherwise. + def place_in_layout? + !(asset_file? || yaml_file? || no_layout?) + end + + # The URL template where the document would be accessible. + # + # Returns the URL template for the document. + def url_template + collection.url_template + end + + # Construct a Hash of key-value pairs which contain a mapping between + # a key in the URL template and the corresponding value for this document. + # + # Returns the Hash of key-value pairs for replacement in the URL. + def url_placeholders + @url_placeholders ||= Drops::UrlDrop.new(self) + end + + # The permalink for this Document. + # Permalink is set via the data Hash. + # + # Returns the permalink or nil if no permalink was set in the data. + def permalink + data && data.is_a?(Hash) && data["permalink"] + end + + # The computed URL for the document. See `Jekyll::URL#to_s` for more details. + # + # Returns the computed URL for the document. + def url + @url ||= URL.new( + :template => url_template, + :placeholders => url_placeholders, + :permalink => permalink + ).to_s + end + + def [](key) + data[key] + end + + # The full path to the output file. + # + # base_directory - the base path of the output directory + # + # Returns the full path to the output file of this document. + def destination(base_directory) + @destination ||= {} + @destination[base_directory] ||= begin + path = site.in_dest_dir(base_directory, URL.unescape_path(url)) + if url.end_with? "/" + path = File.join(path, "index.html") + else + path << output_ext unless path.end_with? output_ext + end + path + end + end + + # Write the generated Document file to the destination directory. + # + # dest - The String path to the destination dir. + # + # Returns nothing. + def write(dest) + path = destination(dest) + FileUtils.mkdir_p(File.dirname(path)) + Jekyll.logger.debug "Writing:", path + File.write(path, output, :mode => "wb") + + trigger_hooks(:post_write) + end + + # Whether the file is published or not, as indicated in YAML front-matter + # + # Returns 'false' if the 'published' key is specified in the + # YAML front-matter and is 'false'. Otherwise returns 'true'. + def published? + !(data.key?("published") && data["published"] == false) + end + + # Read in the file and assign the content and data based on the file contents. + # Merge the frontmatter of the file with the frontmatter default + # values + # + # Returns nothing. + def read(opts = {}) + Jekyll.logger.debug "Reading:", relative_path + + if yaml_file? + @data = SafeYAML.load_file(path) + else + begin + merge_defaults + read_content(**opts) + read_post_data + rescue StandardError => e + handle_read_error(e) + end + end + end + + # Create a Liquid-understandable version of this Document. + # + # Returns a Hash representing this Document's data. + def to_liquid + @to_liquid ||= Drops::DocumentDrop.new(self) + end + + # The inspect string for this document. + # Includes the relative path and the collection label. + # + # Returns the inspect string for this document. + def inspect + "#<#{self.class} #{relative_path} collection=#{collection.label}>" + end + + # The string representation for this document. + # + # Returns the content of the document + def to_s + output || content || "NO CONTENT" + end + + # Compare this document against another document. + # Comparison is a comparison between the 2 paths of the documents. + # + # Returns -1, 0, +1 or nil depending on whether this doc's path is less than, + # equal or greater than the other doc's path. See String#<=> for more details. + def <=>(other) + return nil unless other.respond_to?(:data) + + cmp = data["date"] <=> other.data["date"] + cmp = path <=> other.path if cmp.nil? || cmp.zero? + cmp + end + + # Determine whether this document should be written. + # Based on the Collection to which it belongs. + # + # True if the document has a collection and if that collection's #write? + # method returns true, and if the site's Publisher will publish the document. + # False otherwise. + # + # rubocop:disable Naming/MemoizedInstanceVariableName + def write? + return @write_p if defined?(@write_p) + + @write_p = collection&.write? && site.publisher.publish?(self) + end + # rubocop:enable Naming/MemoizedInstanceVariableName + + # The Document excerpt_separator, from the YAML Front-Matter or site + # default excerpt_separator value + # + # Returns the document excerpt_separator + def excerpt_separator + @excerpt_separator ||= (data["excerpt_separator"] || site.config["excerpt_separator"]).to_s + end + + # Whether to generate an excerpt + # + # Returns true if the excerpt separator is configured. + def generate_excerpt? + !excerpt_separator.empty? + end + + def next_doc + pos = collection.docs.index { |post| post.equal?(self) } + collection.docs[pos + 1] if pos && pos < collection.docs.length - 1 + end + + def previous_doc + pos = collection.docs.index { |post| post.equal?(self) } + collection.docs[pos - 1] if pos && pos.positive? + end + + def trigger_hooks(hook_name, *args) + Jekyll::Hooks.trigger collection.label.to_sym, hook_name, self, *args if collection + Jekyll::Hooks.trigger :documents, hook_name, self, *args + end + + def id + @id ||= File.join(File.dirname(url), (data["slug"] || basename_without_ext).to_s) + end + + # Calculate related posts. + # + # Returns an Array of related Posts. + def related_posts + @related_posts ||= Jekyll::RelatedPosts.new(self).build + end + + # Override of method_missing to check in @data for the key. + def method_missing(method, *args, &blck) + if data.key?(method.to_s) + Jekyll::Deprecator.deprecation_message "Document##{method} is now a key in the #data hash." + Jekyll.logger.warn "", "Called by #{caller(1..1)[0]}." + data[method.to_s] + else + super + end + end + + def respond_to_missing?(method, *) + data.key?(method.to_s) || super + end + + # Add superdirectories of the special_dir to categories. + # In the case of es/_posts, 'es' is added as a category. + # In the case of _posts/es, 'es' is NOT added as a category. + # + # Returns nothing. + def categories_from_path(special_dir) + if relative_path.start_with?(special_dir) + superdirs = [] + else + superdirs = relative_path.sub(Document.superdirs_regex(special_dir), "") + superdirs = superdirs.split(File::SEPARATOR) + superdirs.reject! { |c| c.empty? || c == special_dir || c == basename } + end + + merge_data!({ "categories" => superdirs }, :source => "file path") + end + + def populate_categories + categories = Array(data["categories"]) + Utils.pluralized_array_from_hash( + data, "category", "categories" + ) + categories.map!(&:to_s) + categories.flatten! + categories.uniq! + + merge_data!({ "categories" => categories }) + end + + def populate_tags + tags = Utils.pluralized_array_from_hash(data, "tag", "tags") + tags.flatten! + + merge_data!({ "tags" => tags }) + end + + private + + def merge_categories!(other) + if other.key?("categories") && !other["categories"].nil? + other["categories"] = other["categories"].split if other["categories"].is_a?(String) + + if data["categories"].is_a?(Array) + other["categories"] = data["categories"] | other["categories"] + end + end + end + + def merge_date!(source) + if data.key?("date") + data["date"] = Utils.parse_date( + data["date"].to_s, + "Document '#{relative_path}' does not have a valid date in the #{source}." + ) + end + end + + def merge_defaults + defaults = @site.frontmatter_defaults.all(relative_path, type) + merge_data!(defaults, :source => "front matter defaults") unless defaults.empty? + end + + def read_content(**opts) + self.content = File.read(path, **Utils.merged_file_read_opts(site, opts)) + if content =~ YAML_FRONT_MATTER_REGEXP + self.content = Regexp.last_match.post_match + data_file = SafeYAML.load(Regexp.last_match(1)) + merge_data!(data_file, :source => "YAML front matter") if data_file + end + end + + def read_post_data + populate_title + populate_categories + populate_tags + generate_excerpt + end + + def handle_read_error(error) + 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}" + end + + if site.config["strict_front_matter"] || error.is_a?(Jekyll::Errors::FatalException) + raise error + end + end + + def populate_title + if relative_path =~ DATE_FILENAME_MATCHER + date, slug, ext = Regexp.last_match.captures + modify_date(date) + elsif relative_path =~ DATELESS_FILENAME_MATCHER + slug, ext = Regexp.last_match.captures + end + # `slug` will be nil for documents without an extension since the regex patterns + # above tests for an extension as well. + # In such cases, assign `basename_without_ext` as the slug. + slug ||= basename_without_ext + + # slugs shouldn't end with a period + # `String#gsub!` removes all trailing periods (in comparison to `String#chomp!`) + slug.gsub!(%r!\.*\z!, "") + + # Try to ensure the user gets a title. + data["title"] ||= Utils.titleize_slug(slug) + # Only overwrite slug & ext if they aren't specified. + data["slug"] ||= slug + data["ext"] ||= ext + end + + def modify_date(date) + if !data["date"] || data["date"].to_i == site.time.to_i + merge_data!({ "date" => date }, :source => "filename") + end + end + + def generate_excerpt + data["excerpt"] ||= Jekyll::Excerpt.new(self) if generate_excerpt? + end + end +end diff --git a/lib/jekyll/drops/collection_drop.rb b/lib/jekyll/drops/collection_drop.rb new file mode 100644 index 0000000..4fe95a5 --- /dev/null +++ b/lib/jekyll/drops/collection_drop.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +module Jekyll + module Drops + class CollectionDrop < Drop + extend Forwardable + + mutable false + + delegate_method_as :write?, :output + delegate_methods :label, :docs, :files, :directory, :relative_directory + + private delegate_method_as :metadata, :fallback_data + + def to_s + docs.to_s + end + end + end +end diff --git a/lib/jekyll/drops/document_drop.rb b/lib/jekyll/drops/document_drop.rb new file mode 100644 index 0000000..0cff374 --- /dev/null +++ b/lib/jekyll/drops/document_drop.rb @@ -0,0 +1,74 @@ +# frozen_string_literal: true + +module Jekyll + module Drops + class DocumentDrop < Drop + extend Forwardable + + NESTED_OBJECT_FIELD_BLACKLIST = %w( + content output excerpt next previous + ).freeze + + mutable false + + delegate_method_as :relative_path, :path + private delegate_method_as :data, :fallback_data + + delegate_methods :id, :output, :content, :to_s, :relative_path, :url, :date + data_delegators "title", "categories", "tags" + + def collection + @obj.collection.label + end + + def excerpt + fallback_data["excerpt"].to_s + end + + def name + fallback_data["name"] || @obj.basename + end + + def <=>(other) + return nil unless other.is_a? DocumentDrop + + cmp = self["date"] <=> other["date"] + cmp = self["path"] <=> other["path"] if cmp.nil? || cmp.zero? + cmp + end + + def previous + @obj.previous_doc.to_liquid + end + + def next + @obj.next_doc.to_liquid + end + + # Generate a Hash for use in generating JSON. + # This is useful if fields need to be cleared before the JSON can generate. + # + # state - the JSON::State object which determines the state of current processing. + # + # Returns a Hash ready for JSON generation. + def hash_for_json(state = nil) + to_h.tap do |hash| + if state && state.depth >= 2 + hash["previous"] = collapse_document(hash["previous"]) if hash["previous"] + hash["next"] = collapse_document(hash["next"]) if hash["next"] + end + end + end + + # Generate a Hash which breaks the recursive chain. + # Certain fields which are normally available are omitted. + # + # Returns a Hash with only non-recursive fields present. + def collapse_document(doc) + doc.keys.each_with_object({}) do |(key, _), result| + result[key] = doc[key] unless NESTED_OBJECT_FIELD_BLACKLIST.include?(key) + end + end + end + end +end diff --git a/lib/jekyll/drops/drop.rb b/lib/jekyll/drops/drop.rb new file mode 100644 index 0000000..87eed39 --- /dev/null +++ b/lib/jekyll/drops/drop.rb @@ -0,0 +1,293 @@ +# frozen_string_literal: true + +module Jekyll + module Drops + class Drop < Liquid::Drop + include Enumerable + + NON_CONTENT_METHODS = [:fallback_data, :collapse_document].freeze + NON_CONTENT_METHOD_NAMES = NON_CONTENT_METHODS.map(&:to_s).freeze + private_constant :NON_CONTENT_METHOD_NAMES + + # A private stash to avoid repeatedly generating the setter method name string for + # a call to `Drops::Drop#[]=`. + # The keys of the stash below have a very high probability of being called upon during + # the course of various `Jekyll::Renderer#run` calls. + SETTER_KEYS_STASH = { + "content" => "content=", + "layout" => "layout=", + "page" => "page=", + "paginator" => "paginator=", + "highlighter_prefix" => "highlighter_prefix=", + "highlighter_suffix" => "highlighter_suffix=", + }.freeze + private_constant :SETTER_KEYS_STASH + + class << self + # Get or set whether the drop class is mutable. + # Mutability determines whether or not pre-defined fields may be + # overwritten. + # + # is_mutable - Boolean set mutability of the class (default: nil) + # + # Returns the mutability of the class + def mutable(is_mutable = nil) + @is_mutable = is_mutable || false + end + + def mutable? + @is_mutable + end + + # public delegation helper methods that calls onto Drop's instance + # variable `@obj`. + + # Generate private Drop instance_methods for each symbol in the given list. + # + # Returns nothing. + def private_delegate_methods(*symbols) + symbols.each { |symbol| private delegate_method(symbol) } + nil + end + + # Generate public Drop instance_methods for each symbol in the given list. + # + # Returns nothing. + def delegate_methods(*symbols) + symbols.each { |symbol| delegate_method(symbol) } + nil + end + + # Generate public Drop instance_method for given symbol that calls `@obj.<sym>`. + # + # Returns delegated method symbol. + def delegate_method(symbol) + define_method(symbol) { @obj.send(symbol) } + end + + # Generate public Drop instance_method named `delegate` that calls `@obj.<original>`. + # + # Returns delegated method symbol. + def delegate_method_as(original, delegate) + define_method(delegate) { @obj.send(original) } + end + + # Generate public Drop instance_methods for each string entry in the given list. + # The generated method(s) access(es) `@obj`'s data hash. + # + # Returns nothing. + def data_delegators(*strings) + strings.each do |key| + data_delegator(key) if key.is_a?(String) + end + nil + end + + # Generate public Drop instance_methods for given string `key`. + # The generated method access(es) `@obj`'s data hash. + # + # Returns method symbol. + def data_delegator(key) + define_method(key.to_sym) { @obj.data[key] } + end + + # Array of stringified instance methods that do not end with the assignment operator. + # + # (<klass>.instance_methods always generates a new Array object so it can be mutated) + # + # Returns array of strings. + def getter_method_names + @getter_method_names ||= instance_methods.map!(&:to_s).tap do |list| + list.reject! { |item| item.end_with?("=") } + end + end + end + + # Create a new Drop + # + # obj - the Jekyll Site, Collection, or Document required by the + # drop. + # + # Returns nothing + def initialize(obj) + @obj = obj + end + + # Access a method in the Drop or a field in the underlying hash data. + # If mutable, checks the mutations first. Then checks the methods, + # and finally check the underlying hash (e.g. document front matter) + # if all the previous places didn't match. + # + # key - the string key whose value to fetch + # + # Returns the value for the given key, or nil if none exists + def [](key) + if self.class.mutable? && mutations.key?(key) + mutations[key] + elsif self.class.invokable? key + public_send key + else + fallback_data[key] + end + end + alias_method :invoke_drop, :[] + + # Set a field in the Drop. If mutable, sets in the mutations and + # returns. If not mutable, checks first if it's trying to override a + # Drop method and raises a DropMutationException if so. If not + # mutable and the key is not a method on the Drop, then it sets the + # key to the value in the underlying hash (e.g. document front + # matter) + # + # key - the String key whose value to set + # val - the Object to set the key's value to + # + # Returns the value the key was set to unless the Drop is not mutable + # and the key matches a method in which case it raises a + # DropMutationException. + def []=(key, val) + setter = SETTER_KEYS_STASH[key] || "#{key}=" + if respond_to?(setter) + public_send(setter, val) + elsif respond_to?(key.to_s) + if self.class.mutable? + mutations[key] = val + else + raise Errors::DropMutationException, "Key #{key} cannot be set in the drop." + end + else + fallback_data[key] = val + end + end + + # Generates a list of strings which correspond to content getter + # methods. + # + # Returns an Array of strings which represent method-specific keys. + def content_methods + @content_methods ||= \ + self.class.getter_method_names \ + - Jekyll::Drops::Drop.getter_method_names \ + - NON_CONTENT_METHOD_NAMES + end + + # Check if key exists in Drop + # + # key - the string key whose value to fetch + # + # Returns true if the given key is present + def key?(key) + return false if key.nil? + return true if self.class.mutable? && mutations.key?(key) + + respond_to?(key) || fallback_data.key?(key) + end + + # Generates a list of keys with user content as their values. + # This gathers up the Drop methods and keys of the mutations and + # underlying data hashes and performs a set union to ensure a list + # of unique keys for the Drop. + # + # Returns an Array of unique keys for content for the Drop. + def keys + (content_methods | + mutations.keys | + fallback_data.keys).flatten + end + + # Generate a Hash representation of the Drop by resolving each key's + # value. It includes Drop methods, mutations, and the underlying object's + # data. See the documentation for Drop#keys for more. + # + # Returns a Hash with all the keys and values resolved. + def to_h + keys.each_with_object({}) do |(key, _), result| + result[key] = self[key] + end + end + alias_method :to_hash, :to_h + + # Inspect the drop's keys and values through a JSON representation + # of its keys and values. + # + # Returns a pretty generation of the hash representation of the Drop. + def inspect + JSON.pretty_generate to_h + end + + # Generate a Hash for use in generating JSON. + # This is useful if fields need to be cleared before the JSON can generate. + # + # Returns a Hash ready for JSON generation. + def hash_for_json(*) + to_h + end + + # Generate a JSON representation of the Drop. + # + # state - the JSON::State object which determines the state of current processing. + # + # Returns a JSON representation of the Drop in a String. + def to_json(state = nil) + JSON.generate(hash_for_json(state), state) + end + + # Collects all the keys and passes each to the block in turn. + # + # block - a block which accepts one argument, the key + # + # Returns nothing. + def each_key(&block) + keys.each(&block) + end + + def each + each_key.each do |key| + yield key, self[key] + end + end + + def merge(other, &block) + dup.tap do |me| + if block.nil? + me.merge!(other) + else + me.merge!(other, block) + end + end + end + + def merge!(other) + other.each_key do |key| + if block_given? + self[key] = yield key, self[key], other[key] + else + if Utils.mergable?(self[key]) && Utils.mergable?(other[key]) + self[key] = Utils.deep_merge_hashes(self[key], other[key]) + next + end + + self[key] = other[key] unless other[key].nil? + end + end + end + + # Imitate Hash.fetch method in Drop + # + # Returns value if key is present in Drop, otherwise returns default value + # KeyError is raised if key is not present and no default value given + def fetch(key, default = nil, &block) + return self[key] if key?(key) + raise KeyError, %(key not found: "#{key}") if default.nil? && block.nil? + return yield(key) unless block.nil? + return default unless default.nil? + end + + private + + def mutations + @mutations ||= {} + end + end + end +end diff --git a/lib/jekyll/drops/excerpt_drop.rb b/lib/jekyll/drops/excerpt_drop.rb new file mode 100644 index 0000000..82d3cdf --- /dev/null +++ b/lib/jekyll/drops/excerpt_drop.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module Jekyll + module Drops + class ExcerptDrop < DocumentDrop + def layout + @obj.doc.data["layout"] + end + + def date + @obj.doc.date + end + + def excerpt + nil + end + + def name + @obj.doc.data["name"] || @obj.doc.basename + end + end + end +end diff --git a/lib/jekyll/drops/jekyll_drop.rb b/lib/jekyll/drops/jekyll_drop.rb new file mode 100644 index 0000000..63187cc --- /dev/null +++ b/lib/jekyll/drops/jekyll_drop.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +module Jekyll + module Drops + class JekyllDrop < Liquid::Drop + class << self + def global + @global ||= JekyllDrop.new + end + end + + def version + Jekyll::VERSION + end + + def environment + Jekyll.env + end + + def to_h + @to_h ||= { + "version" => version, + "environment" => environment, + } + end + + def to_json(state = nil) + JSON.generate(to_h, state) + end + end + end +end diff --git a/lib/jekyll/drops/site_drop.rb b/lib/jekyll/drops/site_drop.rb new file mode 100644 index 0000000..cc3c60f --- /dev/null +++ b/lib/jekyll/drops/site_drop.rb @@ -0,0 +1,66 @@ +# frozen_string_literal: true + +module Jekyll + module Drops + class SiteDrop < Drop + extend Forwardable + + mutable false + + delegate_method_as :site_data, :data + delegate_methods :time, :pages, :static_files, :tags, :categories + + private delegate_method_as :config, :fallback_data + + def [](key) + if key != "posts" && @obj.collections.key?(key) + @obj.collections[key].docs + else + super(key) + end + end + + def key?(key) + (key != "posts" && @obj.collections.key?(key)) || super + end + + def posts + @site_posts ||= @obj.posts.docs.sort { |a, b| b <=> a } + end + + def html_pages + @site_html_pages ||= @obj.pages.select do |page| + page.html? || page.url.end_with?("/") + end + end + + def collections + @site_collections ||= @obj.collections.values.sort_by(&:label).map(&:to_liquid) + end + + # `Site#documents` cannot be memoized so that `Site#docs_to_write` can access the + # latest state of the attribute. + # + # Since this method will be called after `Site#pre_render` hook, the `Site#documents` + # array shouldn't thereafter change and can therefore be safely memoized to prevent + # additional computation of `Site#documents`. + def documents + @documents ||= @obj.documents + end + + # `{{ site.related_posts }}` is how posts can get posts related to + # them, either through LSI if it's enabled, or through the most + # recent posts. + # We should remove this in 4.0 and switch to `{{ post.related_posts }}`. + def related_posts + return nil unless @current_document.is_a?(Jekyll::Document) + + @current_document.related_posts + end + attr_writer :current_document + + # return nil for `{{ site.config }}` even if --config was passed via CLI + def config; end + end + end +end diff --git a/lib/jekyll/drops/static_file_drop.rb b/lib/jekyll/drops/static_file_drop.rb new file mode 100644 index 0000000..d00973b --- /dev/null +++ b/lib/jekyll/drops/static_file_drop.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +module Jekyll + module Drops + class StaticFileDrop < Drop + extend Forwardable + delegate_methods :name, :extname, :modified_time, :basename + delegate_method_as :relative_path, :path + delegate_method_as :type, :collection + + private delegate_method_as :data, :fallback_data + end + end +end diff --git a/lib/jekyll/drops/theme_drop.rb b/lib/jekyll/drops/theme_drop.rb new file mode 100644 index 0000000..b462532 --- /dev/null +++ b/lib/jekyll/drops/theme_drop.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +module Jekyll + module Drops + class ThemeDrop < Drop + delegate_methods :root + delegate_method_as :runtime_dependencies, :dependencies + + def authors + @authors ||= gemspec.authors.join(", ") + end + + def version + @version ||= gemspec.version.to_s + end + + def description + @description ||= gemspec.description || gemspec.summary + end + + def metadata + @metadata ||= gemspec.metadata + end + + private + + def gemspec + @gemspec ||= @obj.send(:gemspec) + end + + def fallback_data + @fallback_data ||= {} + end + end + end +end diff --git a/lib/jekyll/drops/unified_payload_drop.rb b/lib/jekyll/drops/unified_payload_drop.rb new file mode 100644 index 0000000..709ad71 --- /dev/null +++ b/lib/jekyll/drops/unified_payload_drop.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +module Jekyll + module Drops + class UnifiedPayloadDrop < Drop + mutable true + + attr_accessor :content, :page, :layout, :paginator, + :highlighter_prefix, :highlighter_suffix + + def jekyll + JekyllDrop.global + end + + def site + @site_drop ||= SiteDrop.new(@obj) + end + + def theme + @theme_drop ||= ThemeDrop.new(@obj.theme) if @obj.theme + end + + private + + def fallback_data + @fallback_data ||= {} + end + end + end +end diff --git a/lib/jekyll/drops/url_drop.rb b/lib/jekyll/drops/url_drop.rb new file mode 100644 index 0000000..de58c95 --- /dev/null +++ b/lib/jekyll/drops/url_drop.rb @@ -0,0 +1,140 @@ +# frozen_string_literal: true + +module Jekyll + module Drops + class UrlDrop < Drop + extend Forwardable + + mutable false + + delegate_method :output_ext + delegate_method_as :cleaned_relative_path, :path + + def collection + @obj.collection.label + end + + def name + Utils.slugify(@obj.basename_without_ext) + end + + def title + Utils.slugify(@obj.data["slug"], :mode => "pretty", :cased => true) || + Utils.slugify(@obj.basename_without_ext, :mode => "pretty", :cased => true) + end + + def slug + Utils.slugify(@obj.data["slug"]) || Utils.slugify(@obj.basename_without_ext) + end + + def categories + category_set = Set.new + Array(@obj.data["categories"]).each do |category| + category_set << category.to_s.downcase + end + category_set.to_a.join("/") + end + + # Similar to output from #categories, but each category will be downcased and + # all non-alphanumeric characters of the category replaced with a hyphen. + def slugified_categories + Array(@obj.data["categories"]).each_with_object(Set.new) do |category, set| + set << Utils.slugify(category.to_s) + end.to_a.join("/") + end + + # CCYY + def year + @obj.date.strftime("%Y") + end + + # MM: 01..12 + def month + @obj.date.strftime("%m") + end + + # DD: 01..31 + def day + @obj.date.strftime("%d") + end + + # hh: 00..23 + def hour + @obj.date.strftime("%H") + end + + # mm: 00..59 + def minute + @obj.date.strftime("%M") + end + + # ss: 00..59 + def second + @obj.date.strftime("%S") + end + + # D: 1..31 + def i_day + @obj.date.strftime("%-d") + end + + # M: 1..12 + def i_month + @obj.date.strftime("%-m") + end + + # MMM: Jan..Dec + def short_month + @obj.date.strftime("%b") + end + + # MMMM: January..December + def long_month + @obj.date.strftime("%B") + end + + # YY: 00..99 + def short_year + @obj.date.strftime("%y") + end + + # CCYYw, ISO week year + # may differ from CCYY for the first days of January and last days of December + def w_year + @obj.date.strftime("%G") + end + + # WW: 01..53 + # %W and %U do not comply with ISO 8601-1 + def week + @obj.date.strftime("%V") + end + + # d: 1..7 (Monday..Sunday) + def w_day + @obj.date.strftime("%u") + end + + # dd: Mon..Sun + def short_day + @obj.date.strftime("%a") + end + + # ddd: Monday..Sunday + def long_day + @obj.date.strftime("%A") + end + + # DDD: 001..366 + def y_day + @obj.date.strftime("%j") + end + + private + + def fallback_data + @fallback_data ||= {} + end + end + end +end diff --git a/lib/jekyll/entry_filter.rb b/lib/jekyll/entry_filter.rb new file mode 100644 index 0000000..4e70a5d --- /dev/null +++ b/lib/jekyll/entry_filter.rb @@ -0,0 +1,117 @@ +# frozen_string_literal: true + +module Jekyll + class EntryFilter + attr_reader :site + + SPECIAL_LEADING_CHAR_REGEX = %r!\A#{Regexp.union([".", "_", "#", "~"])}!o.freeze + + def initialize(site, base_directory = nil) + @site = site + @base_directory = derive_base_directory( + @site, base_directory.to_s.dup + ) + end + + def base_directory + @base_directory.to_s + end + + def derive_base_directory(site, base_dir) + base_dir[site.source] = "" if base_dir.start_with?(site.source) + base_dir + end + + def relative_to_source(entry) + File.join( + base_directory, entry + ) + end + + def filter(entries) + entries.reject do |e| + # Reject this entry if it is just a "dot" representation. + # e.g.: '.', '..', '_movies/.', 'music/..', etc + next true if e.end_with?(".") + + # Check if the current entry is explicitly included and cache the result + included = included?(e) + + # Reject current entry if it is excluded but not explicitly included as well. + next true if excluded?(e) && !included + + # Reject current entry if it is a symlink. + next true if symlink?(e) + + # Do not reject current entry if it is explicitly included. + next false if included + + # Reject current entry if it is special or a backup file. + special?(e) || backup?(e) + end + end + + def included?(entry) + glob_include?(site.include, entry) || + glob_include?(site.include, File.basename(entry)) + end + + def special?(entry) + SPECIAL_LEADING_CHAR_REGEX.match?(entry) || + SPECIAL_LEADING_CHAR_REGEX.match?(File.basename(entry)) + end + + def backup?(entry) + entry.end_with?("~") + end + + def excluded?(entry) + glob_include?(site.exclude - site.include, relative_to_source(entry)).tap do |excluded| + if excluded + Jekyll.logger.debug( + "EntryFilter:", + "excluded #{relative_to_source(entry)}" + ) + end + end + end + + # -- + # Check if a file is a symlink. + # NOTE: This can be converted to allowing even in safe, + # since we use Pathutil#in_path? now. + # -- + def symlink?(entry) + site.safe && File.symlink?(entry) && symlink_outside_site_source?(entry) + end + + # -- + # Check if given path is outside of current site's configured source directory. + # -- + def symlink_outside_site_source?(entry) + !File.realpath(entry).start_with?(site.in_source_dir) + end + + # Check if an entry matches a specific pattern. + # Returns true if path matches against any glob pattern, else false. + def glob_include?(enumerator, entry) + entry_with_source = PathManager.join(site.source, entry) + entry_is_directory = File.directory?(entry_with_source) + + enumerator.any? do |pattern| + case pattern + when String + pattern_with_source = PathManager.join(site.source, pattern) + + File.fnmatch?(pattern_with_source, entry_with_source) || + entry_with_source.start_with?(pattern_with_source) || + (pattern_with_source == "#{entry_with_source}/" if entry_is_directory) + when Regexp + pattern.match?(entry_with_source) + else + false + end + end + end + end +end diff --git a/lib/jekyll/errors.rb b/lib/jekyll/errors.rb new file mode 100644 index 0000000..8d659e8 --- /dev/null +++ b/lib/jekyll/errors.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +module Jekyll + module Errors + FatalException = Class.new(::RuntimeError) + + InvalidThemeName = Class.new(FatalException) + + DropMutationException = Class.new(FatalException) + InvalidPermalinkError = Class.new(FatalException) + InvalidYAMLFrontMatterError = Class.new(FatalException) + MissingDependencyException = Class.new(FatalException) + + InvalidDateError = Class.new(FatalException) + InvalidPostNameError = Class.new(FatalException) + PostURLError = Class.new(FatalException) + InvalidURLError = Class.new(FatalException) + InvalidConfigurationError = Class.new(FatalException) + end +end diff --git a/lib/jekyll/excerpt.rb b/lib/jekyll/excerpt.rb new file mode 100644 index 0000000..6318520 --- /dev/null +++ b/lib/jekyll/excerpt.rb @@ -0,0 +1,200 @@ +# frozen_string_literal: true + +module Jekyll + class Excerpt + extend Forwardable + + attr_accessor :content, :doc, :ext + attr_writer :output + + def_delegators :@doc, + :site, :name, :ext, :extname, + :collection, :related_posts, :type, + :coffeescript_file?, :yaml_file?, + :url, :next_doc, :previous_doc + + private :coffeescript_file?, :yaml_file? + + # Initialize this Excerpt instance. + # + # doc - The Document. + # + # Returns the new Excerpt. + def initialize(doc) + self.doc = doc + self.content = extract_excerpt(doc.content) + end + + # Fetch YAML front-matter data from related doc, without layout key + # + # Returns Hash of doc data + def data + @data ||= doc.data.dup + @data.delete("layout") + @data.delete("excerpt") + @data + end + + def trigger_hooks(*); end + + # 'Path' of the excerpt. + # + # Returns the path for the doc this excerpt belongs to with #excerpt appended + def path + File.join(doc.path, "#excerpt") + end + + # 'Relative Path' of the excerpt. + # + # Returns the relative_path for the doc this excerpt belongs to with #excerpt appended + def relative_path + @relative_path ||= File.join(doc.relative_path, "#excerpt") + end + + # Check if excerpt includes a string + # + # Returns true if the string passed in + def include?(something) + output&.include?(something) || content.include?(something) + end + + # The UID for this doc (useful in feeds). + # e.g. /2008/11/05/my-awesome-doc + # + # Returns the String UID. + def id + "#{doc.id}#excerpt" + end + + def to_s + output || content + end + + def to_liquid + Jekyll::Drops::ExcerptDrop.new(self) + end + + # Returns the shorthand String identifier of this doc. + def inspect + "<#{self.class} id=#{id}>" + end + + def output + @output ||= Renderer.new(doc.site, self, site.site_payload).run + end + + def place_in_layout? + false + end + + def render_with_liquid? + return false if data["render_with_liquid"] == false + + !(coffeescript_file? || yaml_file? || !Utils.has_liquid_construct?(content)) + end + + protected + + # Internal: Extract excerpt from the content + # + # By default excerpt is your first paragraph of a doc: everything before + # the first two new lines: + # + # --- + # title: Example + # --- + # + # First paragraph with [link][1]. + # + # Second paragraph. + # + # [1]: http://example.com/ + # + # This is fairly good option for Markdown and Textile files. But might cause + # problems for HTML docs (which is quite unusual for Jekyll). If default + # excerpt delimiter is not good for you, you might want to set your own via + # configuration option `excerpt_separator`. For example, following is a good + # alternative for HTML docs: + # + # # file: _config.yml + # excerpt_separator: "<!-- more -->" + # + # Notice that all markdown-style link references will be appended to the + # excerpt. So the example doc above will have this excerpt source: + # + # First paragraph with [link][1]. + # + # [1]: http://example.com/ + # + # Excerpts are rendered same time as content is rendered. + # + # Returns excerpt String + + LIQUID_TAG_REGEX = %r!{%-?\s*(\w+)\s*.*?-?%}!m.freeze + MKDWN_LINK_REF_REGEX = %r!^ {0,3}(?:(\[[^\]]+\])(:.+))$!.freeze + + def extract_excerpt(doc_content) + head, _, tail = doc_content.to_s.partition(doc.excerpt_separator) + return head if tail.empty? + + head = sanctify_liquid_tags(head) if head.include?("{%") + definitions = extract_markdown_link_reference_definitions(head, tail) + return head if definitions.empty? + + head << "\n\n" << definitions.join("\n") + end + + private + + # append appropriate closing tag(s) (for each Liquid block), to the `head` if the + # partitioning resulted in leaving the closing tag somewhere in the `tail` partition. + def sanctify_liquid_tags(head) + modified = false + tag_names = head.scan(LIQUID_TAG_REGEX) + tag_names.flatten! + tag_names.reverse_each do |tag_name| + next unless liquid_block?(tag_name) + next if endtag_regex_stash(tag_name).match?(head) + + modified = true + head << "\n{% end#{tag_name} %}" + end + + print_build_warning if modified + head + end + + def extract_markdown_link_reference_definitions(head, tail) + [].tap do |definitions| + tail.scan(MKDWN_LINK_REF_REGEX).each do |segments| + definitions << segments.join if head.include?(segments[0]) + end + end + end + + def endtag_regex_stash(tag_name) + @endtag_regex_stash ||= {} + @endtag_regex_stash[tag_name] ||= %r!{%-?\s*end#{tag_name}.*?\s*-?%}!m + end + + def liquid_block?(tag_name) + return false unless tag_name.is_a?(String) + return false unless Liquid::Template.tags[tag_name] + + Liquid::Template.tags[tag_name].ancestors.include?(Liquid::Block) + rescue NoMethodError + Jekyll.logger.error "Error:", + "A Liquid tag in the excerpt of #{doc.relative_path} couldn't be parsed." + raise + end + + def print_build_warning + Jekyll.logger.warn "Warning:", "Excerpt modified in #{doc.relative_path}!" + Jekyll.logger.warn "", "Found a Liquid block containing the excerpt separator " \ + "#{doc.excerpt_separator.inspect}." + Jekyll.logger.warn "", "The block has been modified with the appropriate closing tag." + Jekyll.logger.warn "", "Feel free to define a custom excerpt or excerpt_separator in the" + Jekyll.logger.warn "", "document's Front Matter if the generated excerpt is unsatisfactory." + end + end +end diff --git a/lib/jekyll/external.rb b/lib/jekyll/external.rb new file mode 100644 index 0000000..b484160 --- /dev/null +++ b/lib/jekyll/external.rb @@ -0,0 +1,75 @@ +# frozen_string_literal: true + +module Jekyll + module External + class << self + # + # Gems that, if installed, should be loaded. + # Usually contain subcommands. + # + def blessed_gems + %w( + jekyll-compose + jekyll-docs + jekyll-import + ) + end + + # + # Require a gem or file if it's present, otherwise silently fail. + # + # names - a string gem name or array of gem names + # + def require_if_present(names) + Array(names).each do |name| + require name + rescue LoadError + Jekyll.logger.debug "Couldn't load #{name}. Skipping." + yield(name, version_constraint(name)) if block_given? + false + end + end + + # + # The version constraint required to activate a given gem. + # Usually the gem version requirement is "> 0," because any version + # will do. In the case of jekyll-docs, however, we require the exact + # same version as Jekyll. + # + # Returns a String version constraint in a parseable form for + # RubyGems. + def version_constraint(gem_name) + return "= #{Jekyll::VERSION}" if gem_name.to_s.eql?("jekyll-docs") + + "> 0" + end + + # + # Require a gem or gems. If it's not present, show a very nice error + # message that explains everything and is much more helpful than the + # normal LoadError. + # + # names - a string gem name or array of gem names + # + def require_with_graceful_fail(names) + Array(names).each do |name| + Jekyll.logger.debug "Requiring:", name.to_s + require name + rescue LoadError => e + Jekyll.logger.error "Dependency Error:", <<~MSG + Yikes! It looks like you don't have #{name} or one of its dependencies installed. + In order to use Jekyll as currently configured, you'll need to install this gem. + + If you've run Jekyll with `bundle exec`, ensure that you have included the #{name} + gem in your Gemfile as well. + + The full error message from Ruby is: '#{e.message}' + + If you run into trouble, you can find helpful resources at https://jekyllrb.com/help/! + MSG + raise Jekyll::Errors::MissingDependencyException, name + end + end + end + end +end diff --git a/lib/jekyll/filters.rb b/lib/jekyll/filters.rb new file mode 100644 index 0000000..5422d52 --- /dev/null +++ b/lib/jekyll/filters.rb @@ -0,0 +1,532 @@ +# frozen_string_literal: true + +require_all "jekyll/filters" + +module Jekyll + module Filters + include URLFilters + include GroupingFilters + include DateFilters + + # Convert a Markdown string into HTML output. + # + # input - The Markdown String to convert. + # + # Returns the HTML formatted String. + def markdownify(input) + @context.registers[:site].find_converter_instance( + Jekyll::Converters::Markdown + ).convert(input.to_s) + end + + # Convert quotes into smart quotes. + # + # input - The String to convert. + # + # Returns the smart-quotified String. + def smartify(input) + @context.registers[:site].find_converter_instance( + Jekyll::Converters::SmartyPants + ).convert(input.to_s) + end + + # Convert a Sass string into CSS output. + # + # input - The Sass String to convert. + # + # Returns the CSS formatted String. + def sassify(input) + @context.registers[:site].find_converter_instance( + Jekyll::Converters::Sass + ).convert(input) + end + + # Convert a Scss string into CSS output. + # + # input - The Scss String to convert. + # + # Returns the CSS formatted String. + def scssify(input) + @context.registers[:site].find_converter_instance( + Jekyll::Converters::Scss + ).convert(input) + end + + # Slugify a filename or title. + # + # input - The filename or title to slugify. + # mode - how string is slugified + # + # Returns the given filename or title as a lowercase URL String. + # See Utils.slugify for more detail. + def slugify(input, mode = nil) + Utils.slugify(input, :mode => mode) + end + + # XML escape a string for use. Replaces any special characters with + # appropriate HTML entity replacements. + # + # input - The String to escape. + # + # Examples + # + # xml_escape('foo "bar" <baz>') + # # => "foo "bar" <baz>" + # + # Returns the escaped String. + def xml_escape(input) + input.to_s.encode(:xml => :attr).gsub(%r!\A"|"\Z!, "") + end + + # CGI escape a string for use in a URL. Replaces any special characters + # with appropriate %XX replacements. + # + # input - The String to escape. + # + # Examples + # + # cgi_escape('foo,bar;baz?') + # # => "foo%2Cbar%3Bbaz%3F" + # + # Returns the escaped String. + def cgi_escape(input) + CGI.escape(input) + end + + # URI escape a string. + # + # input - The String to escape. + # + # Examples + # + # uri_escape('foo, bar \\baz?') + # # => "foo,%20bar%20%5Cbaz?" + # + # Returns the escaped String. + def uri_escape(input) + Addressable::URI.normalize_component(input) + end + + # Replace any whitespace in the input string with a single space + # + # input - The String on which to operate. + # + # Returns the formatted String + def normalize_whitespace(input) + input.to_s.gsub(%r!\s+!, " ").tap(&:strip!) + end + + # Count the number of words in the input string. + # + # input - The String on which to operate. + # + # Returns the Integer word count. + def number_of_words(input, mode = nil) + cjk_charset = '\p{Han}\p{Katakana}\p{Hiragana}\p{Hangul}' + cjk_regex = %r![#{cjk_charset}]!o + word_regex = %r![^#{cjk_charset}\s]+!o + + case mode + when "cjk" + input.scan(cjk_regex).length + input.scan(word_regex).length + when "auto" + cjk_count = input.scan(cjk_regex).length + cjk_count.zero? ? input.split.length : cjk_count + input.scan(word_regex).length + else + input.split.length + end + end + + # Join an array of things into a string by separating with commas and the + # word "and" for the last one. + # + # array - The Array of Strings to join. + # connector - Word used to connect the last 2 items in the array + # + # Examples + # + # array_to_sentence_string(["apples", "oranges", "grapes"]) + # # => "apples, oranges, and grapes" + # + # Returns the formatted String. + def array_to_sentence_string(array, connector = "and") + case array.length + when 0 + "" + when 1 + array[0].to_s + when 2 + "#{array[0]} #{connector} #{array[1]}" + else + "#{array[0...-1].join(", ")}, #{connector} #{array[-1]}" + end + end + + # Convert the input into json string + # + # input - The Array or Hash to be converted + # + # Returns the converted json string + def jsonify(input) + as_liquid(input).to_json + end + + # Filter an array of objects + # + # input - the object array. + # property - the property within each object to filter by. + # value - the desired value. + # Cannot be an instance of Array nor Hash since calling #to_s on them returns + # their `#inspect` string object. + # + # Returns the filtered array of objects + def where(input, property, value) + return input if !property || value.is_a?(Array) || value.is_a?(Hash) + return input unless input.respond_to?(:select) + + input = input.values if input.is_a?(Hash) + input_id = input.hash + + # implement a hash based on method parameters to cache the end-result + # for given parameters. + @where_filter_cache ||= {} + @where_filter_cache[input_id] ||= {} + @where_filter_cache[input_id][property] ||= {} + + # stash or retrieve results to return + @where_filter_cache[input_id][property][value] ||= input.select do |object| + compare_property_vs_target(item_property(object, property), value) + end.to_a + end + + # Filters an array of objects against an expression + # + # input - the object array + # variable - the variable to assign each item to in the expression + # expression - a Liquid comparison expression passed in as a string + # + # Returns the filtered array of objects + def where_exp(input, variable, expression) + return input unless input.respond_to?(:select) + + input = input.values if input.is_a?(Hash) # FIXME + + condition = parse_condition(expression) + @context.stack do + input.select do |object| + @context[variable] = object + condition.evaluate(@context) + end + end || [] + end + + # Search an array of objects and returns the first object that has the queried attribute + # with the given value or returns nil otherwise. + # + # input - the object array. + # property - the property within each object to search by. + # value - the desired value. + # Cannot be an instance of Array nor Hash since calling #to_s on them returns + # their `#inspect` string object. + # + # Returns the found object or nil + # + # rubocop:disable Metrics/CyclomaticComplexity + def find(input, property, value) + return input if !property || value.is_a?(Array) || value.is_a?(Hash) + return input unless input.respond_to?(:find) + + input = input.values if input.is_a?(Hash) + input_id = input.hash + + # implement a hash based on method parameters to cache the end-result for given parameters. + @find_filter_cache ||= {} + @find_filter_cache[input_id] ||= {} + @find_filter_cache[input_id][property] ||= {} + + # stash or retrieve results to return + # Since `enum.find` can return nil or false, we use a placeholder string "<__NO MATCH__>" + # to validate caching. + result = @find_filter_cache[input_id][property][value] ||= input.find do |object| + compare_property_vs_target(item_property(object, property), value) + end || "<__NO MATCH__>" + + return nil if result == "<__NO MATCH__>" + + result + end + # rubocop:enable Metrics/CyclomaticComplexity + + # Searches an array of objects against an expression and returns the first object for which + # the expression evaluates to true, or returns nil otherwise. + # + # input - the object array + # variable - the variable to assign each item to in the expression + # expression - a Liquid comparison expression passed in as a string + # + # Returns the found object or nil + def find_exp(input, variable, expression) + return input unless input.respond_to?(:find) + + input = input.values if input.is_a?(Hash) + + condition = parse_condition(expression) + @context.stack do + input.find do |object| + @context[variable] = object + condition.evaluate(@context) + end + end + end + + # Convert the input into integer + # + # input - the object string + # + # Returns the integer value + def to_integer(input) + return 1 if input == true + return 0 if input == false + + input.to_i + end + + # Sort an array of objects + # + # input - the object array + # property - property within each object to filter by + # nils ('first' | 'last') - nils appear before or after non-nil values + # + # Returns the filtered array of objects + def sort(input, property = nil, nils = "first") + raise ArgumentError, "Cannot sort a null object." if input.nil? + + if property.nil? + input.sort + else + case nils + when "first" + order = - 1 + when "last" + order = + 1 + else + raise ArgumentError, "Invalid nils order: " \ + "'#{nils}' is not a valid nils order. It must be 'first' or 'last'." + end + + sort_input(input, property, order) + end + end + + def pop(array, num = 1) + return array unless array.is_a?(Array) + + num = Liquid::Utils.to_integer(num) + new_ary = array.dup + new_ary.pop(num) + new_ary + end + + def push(array, input) + return array unless array.is_a?(Array) + + new_ary = array.dup + new_ary.push(input) + new_ary + end + + def shift(array, num = 1) + return array unless array.is_a?(Array) + + num = Liquid::Utils.to_integer(num) + new_ary = array.dup + new_ary.shift(num) + new_ary + end + + def unshift(array, input) + return array unless array.is_a?(Array) + + new_ary = array.dup + new_ary.unshift(input) + new_ary + end + + def sample(input, num = 1) + return input unless input.respond_to?(:sample) + + num = Liquid::Utils.to_integer(num) rescue 1 + if num == 1 + input.sample + else + input.sample(num) + end + end + + # Convert an object into its String representation for debugging + # + # input - The Object to be converted + # + # Returns a String representation of the object. + def inspect(input) + xml_escape(input.inspect) + end + + private + + # Sort the input Enumerable by the given property. + # If the property doesn't exist, return the sort order respective of + # which item doesn't have the property. + # We also utilize the Schwartzian transform to make this more efficient. + def sort_input(input, property, order) + input.map { |item| [item_property(item, property), item] } + .sort! do |a_info, b_info| + a_property = a_info.first + b_property = b_info.first + + if !a_property.nil? && b_property.nil? + - order + elsif a_property.nil? && !b_property.nil? + + order + else + a_property <=> b_property || a_property.to_s <=> b_property.to_s + end + end + .map!(&:last) + end + + # `where` filter helper + # + def compare_property_vs_target(property, target) + case target + when NilClass + return true if property.nil? + when Liquid::Expression::MethodLiteral # `empty` or `blank` + target = target.to_s + return true if property == target || Array(property).join == target + else + target = target.to_s + if property.is_a? String + return true if property == target + else + Array(property).each do |prop| + return true if prop.to_s == target + end + end + end + + false + end + + def item_property(item, property) + @item_property_cache ||= @context.registers[:site].filter_cache[:item_property] ||= {} + @item_property_cache[property] ||= {} + @item_property_cache[property][item] ||= begin + property = property.to_s + property = if item.respond_to?(:to_liquid) + read_liquid_attribute(item.to_liquid, property) + elsif item.respond_to?(:data) + item.data[property] + else + item[property] + end + + parse_sort_input(property) + end + end + + def read_liquid_attribute(liquid_data, property) + return liquid_data[property] unless property.include?(".") + + property.split(".").reduce(liquid_data) do |data, key| + data.respond_to?(:[]) && data[key] + end + end + + FLOAT_LIKE = %r!\A\s*-?(?:\d+\.?\d*|\.\d+)\s*\Z!.freeze + INTEGER_LIKE = %r!\A\s*-?\d+\s*\Z!.freeze + private_constant :FLOAT_LIKE, :INTEGER_LIKE + + # return numeric values as numbers for proper sorting + def parse_sort_input(property) + stringified = property.to_s + return property.to_i if INTEGER_LIKE.match?(stringified) + return property.to_f if FLOAT_LIKE.match?(stringified) + + property + end + + def as_liquid(item) + case item + when Hash + item.each_with_object({}) { |(k, v), result| result[as_liquid(k)] = as_liquid(v) } + when Array + item.map { |i| as_liquid(i) } + else + if item.respond_to?(:to_liquid) + liquidated = item.to_liquid + # prevent infinite recursion for simple types (which return `self`) + if liquidated == item + item + else + as_liquid(liquidated) + end + else + item + end + end + end + + # ----------- The following set of code was *adapted* from Liquid::If + # ----------- ref: https://github.com/Shopify/liquid/blob/ffb0ace30315bbcf3548a0383fab531452060ae8/lib/liquid/tags/if.rb#L84-L107 + + # Parse a string to a Liquid Condition + def parse_condition(exp) + parser = Liquid::Parser.new(exp) + condition = parse_binary_comparison(parser) + + parser.consume(:end_of_string) + condition + end + + # Generate a Liquid::Condition object from a Liquid::Parser object additionally processing + # the parsed expression based on whether the expression consists of binary operations with + # Liquid operators `and` or `or` + # + # - parser: an instance of Liquid::Parser + # + # Returns an instance of Liquid::Condition + def parse_binary_comparison(parser) + condition = parse_comparison(parser) + first_condition = condition + while (binary_operator = parser.id?("and") || parser.id?("or")) + child_condition = parse_comparison(parser) + condition.send(binary_operator, child_condition) + condition = child_condition + end + first_condition + end + + # Generates a Liquid::Condition object from a Liquid::Parser object based on whether the parsed + # expression involves a "comparison" operator (e.g. <, ==, >, !=, etc) + # + # - parser: an instance of Liquid::Parser + # + # Returns an instance of Liquid::Condition + def parse_comparison(parser) + left_operand = Liquid::Expression.parse(parser.expression) + operator = parser.consume?(:comparison) + + # No comparison-operator detected. Initialize a Liquid::Condition using only left operand + return Liquid::Condition.new(left_operand) unless operator + + # Parse what remained after extracting the left operand and the `:comparison` operator + # and initialize a Liquid::Condition object using the operands and the comparison-operator + Liquid::Condition.new(left_operand, operator, Liquid::Expression.parse(parser.expression)) + end + end +end + +Liquid::Template.register_filter( + Jekyll::Filters +) diff --git a/lib/jekyll/filters/date_filters.rb b/lib/jekyll/filters/date_filters.rb new file mode 100644 index 0000000..966b49b --- /dev/null +++ b/lib/jekyll/filters/date_filters.rb @@ -0,0 +1,110 @@ +# frozen_string_literal: true + +module Jekyll + module Filters + module DateFilters + # Format a date in short format e.g. "27 Jan 2011". + # Ordinal format is also supported, in both the UK + # (e.g. "27th Jan 2011") and US ("e.g. Jan 27th, 2011") formats. + # UK format is the default. + # + # date - the Time to format. + # type - if "ordinal" the returned String will be in ordinal format + # style - if "US" the returned String will be in US format. + # Otherwise it will be in UK format. + # + # Returns the formatting String. + def date_to_string(date, type = nil, style = nil) + stringify_date(date, "%b", type, style) + end + + # Format a date in long format e.g. "27 January 2011". + # Ordinal format is also supported, in both the UK + # (e.g. "27th January 2011") and US ("e.g. January 27th, 2011") formats. + # UK format is the default. + # + # date - the Time to format. + # type - if "ordinal" the returned String will be in ordinal format + # style - if "US" the returned String will be in US format. + # Otherwise it will be in UK format. + # + # Returns the formatted String. + def date_to_long_string(date, type = nil, style = nil) + stringify_date(date, "%B", type, style) + end + + # Format a date for use in XML. + # + # date - The Time to format. + # + # Examples + # + # date_to_xmlschema(Time.now) + # # => "2011-04-24T20:34:46+08:00" + # + # Returns the formatted String. + def date_to_xmlschema(date) + return date if date.to_s.empty? + + time(date).xmlschema + end + + # Format a date according to RFC-822 + # + # date - The Time to format. + # + # Examples + # + # date_to_rfc822(Time.now) + # # => "Sun, 24 Apr 2011 12:34:46 +0000" + # + # Returns the formatted String. + def date_to_rfc822(date) + return date if date.to_s.empty? + + time(date).rfc822 + end + + private + + # month_type: Notations that evaluate to 'Month' via `Time#strftime` ("%b", "%B") + # type: nil (default) or "ordinal" + # style: nil (default) or "US" + # + # Returns a stringified date or the empty input. + def stringify_date(date, month_type, type = nil, style = nil) + return date if date.to_s.empty? + + time = time(date) + if type == "ordinal" + day = time.day + ordinal_day = "#{day}#{ordinal(day)}" + return time.strftime("#{month_type} #{ordinal_day}, %Y") if style == "US" + + return time.strftime("#{ordinal_day} #{month_type} %Y") + end + time.strftime("%d #{month_type} %Y") + end + + def ordinal(number) + return "th" if (11..13).cover?(number) + + case number % 10 + when 1 then "st" + when 2 then "nd" + when 3 then "rd" + else "th" + end + end + + def time(input) + date = Liquid::Utils.to_date(input) + unless date.respond_to?(:to_time) + raise Errors::InvalidDateError, + "Invalid Date: '#{input.inspect}' is not a valid datetime." + end + date.to_time.dup.localtime + end + end + end +end diff --git a/lib/jekyll/filters/grouping_filters.rb b/lib/jekyll/filters/grouping_filters.rb new file mode 100644 index 0000000..c9cdc65 --- /dev/null +++ b/lib/jekyll/filters/grouping_filters.rb @@ -0,0 +1,64 @@ +# frozen_string_literal: true + +module Jekyll + module Filters + module GroupingFilters + # Group an array of items by a property + # + # input - the inputted Enumerable + # property - the property + # + # Returns an array of Hashes, each looking something like this: + # {"name" => "larry" + # "items" => [...] } # all the items where `property` == "larry" + def group_by(input, property) + if groupable?(input) + groups = input.group_by { |item| item_property(item, property).to_s } + grouped_array(groups) + else + input + end + end + + # Group an array of items by an expression + # + # input - the object array + # variable - the variable to assign each item to in the expression + # expression -a Liquid comparison expression passed in as a string + # + # Returns the filtered array of objects + def group_by_exp(input, variable, expression) + return input unless groupable?(input) + + parsed_expr = parse_expression(expression) + @context.stack do + groups = input.group_by do |item| + @context[variable] = item + parsed_expr.render(@context) + end + grouped_array(groups) + end + end + + private + + def parse_expression(str) + Liquid::Variable.new(str, Liquid::ParseContext.new) + end + + def groupable?(element) + element.respond_to?(:group_by) + end + + def grouped_array(groups) + groups.each_with_object([]) do |item, array| + array << { + "name" => item.first, + "items" => item.last, + "size" => item.last.size, + } + end + end + end + end +end diff --git a/lib/jekyll/filters/url_filters.rb b/lib/jekyll/filters/url_filters.rb new file mode 100644 index 0000000..aa28c5a --- /dev/null +++ b/lib/jekyll/filters/url_filters.rb @@ -0,0 +1,98 @@ +# frozen_string_literal: true + +module Jekyll + module Filters + module URLFilters + # Produces an absolute URL based on site.url and site.baseurl. + # + # input - the URL to make absolute. + # + # Returns the absolute URL as a String. + def absolute_url(input) + return if input.nil? + + cache = if input.is_a?(String) + (@context.registers[:site].filter_cache[:absolute_url] ||= {}) + else + (@context.registers[:cached_absolute_url] ||= {}) + end + cache[input] ||= compute_absolute_url(input) + + # Duplicate cached string so that the cached value is never mutated by + # a subsequent filter. + cache[input].dup + end + + # Produces a URL relative to the domain root based on site.baseurl + # unless it is already an absolute url with an authority (host). + # + # input - the URL to make relative to the domain root + # + # Returns a URL relative to the domain root as a String. + def relative_url(input) + return if input.nil? + + cache = if input.is_a?(String) + (@context.registers[:site].filter_cache[:relative_url] ||= {}) + else + (@context.registers[:cached_relative_url] ||= {}) + end + cache[input] ||= compute_relative_url(input) + + # Duplicate cached string so that the cached value is never mutated by + # a subsequent filter. + cache[input].dup + end + + # Strips trailing `/index.html` from URLs to create pretty permalinks + # + # input - the URL with a possible `/index.html` + # + # Returns a URL with the trailing `/index.html` removed + def strip_index(input) + return if input.nil? || input.to_s.empty? + + input.sub(%r!/index\.html?$!, "/") + end + + private + + def compute_absolute_url(input) + input = input.url if input.respond_to?(:url) + return input if Addressable::URI.parse(input.to_s).absolute? + + site = @context.registers[:site] + site_url = site.config["url"] + return relative_url(input) if site_url.nil? || site_url == "" + + Addressable::URI.parse( + site_url.to_s + relative_url(input) + ).normalize.to_s + end + + def compute_relative_url(input) + input = input.url if input.respond_to?(:url) + return input if Addressable::URI.parse(input.to_s).absolute? + + parts = [sanitized_baseurl, input] + Addressable::URI.parse( + parts.map! { |part| ensure_leading_slash(part.to_s) }.join + ).normalize.to_s + end + + def sanitized_baseurl + site = @context.registers[:site] + baseurl = site.config["baseurl"] + return "" if baseurl.nil? + + baseurl.to_s.chomp("/") + end + + def ensure_leading_slash(input) + return input if input.nil? || input.empty? || input.start_with?("/") + + "/#{input}" + end + end + end +end diff --git a/lib/jekyll/frontmatter_defaults.rb b/lib/jekyll/frontmatter_defaults.rb new file mode 100644 index 0000000..5ad896b --- /dev/null +++ b/lib/jekyll/frontmatter_defaults.rb @@ -0,0 +1,238 @@ +# frozen_string_literal: true + +module Jekyll + # This class handles custom defaults for YAML frontmatter settings. + # These are set in _config.yml and apply both to internal use (e.g. layout) + # and the data available to liquid. + # + # It is exposed via the frontmatter_defaults method on the site class. + class FrontmatterDefaults + # Initializes a new instance. + def initialize(site) + @site = site + end + + def reset + @glob_cache = {} if @glob_cache + end + + def update_deprecated_types(set) + return set unless set.key?("scope") && set["scope"].key?("type") + + set["scope"]["type"] = + case set["scope"]["type"] + when "page" + Deprecator.defaults_deprecate_type("page", "pages") + "pages" + when "post" + Deprecator.defaults_deprecate_type("post", "posts") + "posts" + when "draft" + Deprecator.defaults_deprecate_type("draft", "drafts") + "drafts" + else + set["scope"]["type"] + end + + set + end + + def ensure_time!(set) + return set unless set.key?("values") && set["values"].key?("date") + return set if set["values"]["date"].is_a?(Time) + + set["values"]["date"] = Utils.parse_date( + set["values"]["date"], + "An invalid date format was found in a front-matter default set: #{set}" + ) + set + end + + # Finds a default value for a given setting, filtered by path and type + # + # path - the path (relative to the source) of the page, + # post or :draft the default is used in + # type - a symbol indicating whether a :page, + # a :post or a :draft calls this method + # + # Returns the default value or nil if none was found + def find(path, type, setting) + value = nil + old_scope = nil + + matching_sets(path, type).each do |set| + if set["values"].key?(setting) && has_precedence?(old_scope, set["scope"]) + value = set["values"][setting] + old_scope = set["scope"] + end + end + value + end + + # Collects a hash with all default values for a page or post + # + # path - the relative path of the page or post + # type - a symbol indicating the type (:post, :page or :draft) + # + # Returns a hash with all default values (an empty hash if there are none) + def all(path, type) + defaults = {} + old_scope = nil + matching_sets(path, type).each do |set| + if has_precedence?(old_scope, set["scope"]) + defaults = Utils.deep_merge_hashes(defaults, set["values"]) + old_scope = set["scope"] + else + defaults = Utils.deep_merge_hashes(set["values"], defaults) + end + end + defaults + end + + private + + # Checks if a given default setting scope matches the given path and type + # + # scope - the hash indicating the scope, as defined in _config.yml + # path - the path to check for + # type - the type (:post, :page or :draft) to check for + # + # Returns true if the scope applies to the given type and path + def applies?(scope, path, type) + applies_type?(scope, type) && applies_path?(scope, path) + end + + def applies_path?(scope, path) + rel_scope_path = scope["path"] + return true if !rel_scope_path.is_a?(String) || rel_scope_path.empty? + + sanitized_path = sanitize_path(path) + + if rel_scope_path.include?("*") + glob_scope(sanitized_path, rel_scope_path) + else + path_is_subpath?(sanitized_path, strip_collections_dir(rel_scope_path)) + end + end + + def glob_scope(sanitized_path, rel_scope_path) + site_source = Pathname.new(@site.source) + abs_scope_path = site_source.join(rel_scope_path).to_s + + glob_cache(abs_scope_path).each do |scope_path| + scope_path = Pathname.new(scope_path).relative_path_from(site_source).to_s + scope_path = strip_collections_dir(scope_path) + Jekyll.logger.debug "Globbed Scope Path:", scope_path + return true if path_is_subpath?(sanitized_path, scope_path) + end + false + end + + def glob_cache(path) + @glob_cache ||= {} + @glob_cache[path] ||= Dir.glob(path) + end + + def path_is_subpath?(path, parent_path) + path.start_with?(parent_path) + end + + def strip_collections_dir(path) + collections_dir = @site.config["collections_dir"] + slashed_coll_dir = collections_dir.empty? ? "/" : "#{collections_dir}/" + return path if collections_dir.empty? || !path.to_s.start_with?(slashed_coll_dir) + + path.sub(slashed_coll_dir, "") + end + + # Determines whether the scope applies to type. + # The scope applies to the type if: + # 1. no 'type' is specified + # 2. the 'type' in the scope is the same as the type asked about + # + # scope - the Hash defaults set being asked about application + # type - the type of the document being processed / asked about + # its defaults. + # + # Returns true if either of the above conditions are satisfied, + # otherwise returns false + def applies_type?(scope, type) + !scope.key?("type") || type&.to_sym.eql?(scope["type"].to_sym) + end + + # Checks if a given set of default values is valid + # + # set - the default value hash, as defined in _config.yml + # + # Returns true if the set is valid and can be used in this class + def valid?(set) + set.is_a?(Hash) && set["values"].is_a?(Hash) + end + + # Determines if a new scope has precedence over an old one + # + # old_scope - the old scope hash, or nil if there's none + # new_scope - the new scope hash + # + # Returns true if the new scope has precedence over the older + # rubocop: disable Naming/PredicateName + def has_precedence?(old_scope, new_scope) + return true if old_scope.nil? + + new_path = sanitize_path(new_scope["path"]) + old_path = sanitize_path(old_scope["path"]) + + if new_path.length != old_path.length + new_path.length >= old_path.length + elsif new_scope.key?("type") + true + else + !old_scope.key? "type" + end + end + # rubocop: enable Naming/PredicateName + + # Collects a list of sets that match the given path and type + # + # Returns an array of hashes + def matching_sets(path, type) + @matched_set_cache ||= {} + @matched_set_cache[path] ||= {} + @matched_set_cache[path][type] ||= valid_sets.select do |set| + !set.key?("scope") || applies?(set["scope"], path, type) + end + end + + # Returns a list of valid sets + # + # This is not cached to allow plugins to modify the configuration + # and have their changes take effect + # + # Returns an array of hashes + def valid_sets + sets = @site.config["defaults"] + return [] unless sets.is_a?(Array) + + sets.map do |set| + if valid?(set) + ensure_time!(update_deprecated_types(set)) + else + Jekyll.logger.warn "Defaults:", "An invalid front-matter default set was found:" + Jekyll.logger.warn set.to_s + nil + end + end.tap(&:compact!) + end + + # Sanitizes the given path by removing a leading slash + def sanitize_path(path) + if path.nil? || path.empty? + "" + elsif path.start_with?("/") + path.gsub(%r!\A/|(?<=[^/])\z!, "") + else + path + end + end + end +end diff --git a/lib/jekyll/generator.rb b/lib/jekyll/generator.rb new file mode 100644 index 0000000..649715f --- /dev/null +++ b/lib/jekyll/generator.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +module Jekyll + Generator = Class.new(Plugin) +end diff --git a/lib/jekyll/hooks.rb b/lib/jekyll/hooks.rb new file mode 100644 index 0000000..482d802 --- /dev/null +++ b/lib/jekyll/hooks.rb @@ -0,0 +1,107 @@ +# frozen_string_literal: true + +module Jekyll + module Hooks + DEFAULT_PRIORITY = 20 + + # compatibility layer for octopress-hooks users + PRIORITY_MAP = { + :low => 10, + :normal => 20, + :high => 30, + }.freeze + + # initial empty hooks + @registry = { + :site => { + :after_init => [], + :after_reset => [], + :post_read => [], + :pre_render => [], + :post_render => [], + :post_write => [], + }, + :pages => { + :post_init => [], + :pre_render => [], + :post_convert => [], + :post_render => [], + :post_write => [], + }, + :posts => { + :post_init => [], + :pre_render => [], + :post_convert => [], + :post_render => [], + :post_write => [], + }, + :documents => { + :post_init => [], + :pre_render => [], + :post_convert => [], + :post_render => [], + :post_write => [], + }, + :clean => { + :on_obsolete => [], + }, + } + + # map of all hooks and their priorities + @hook_priority = {} + + NotAvailable = Class.new(RuntimeError) + Uncallable = Class.new(RuntimeError) + + # register hook(s) to be called later, public API + def self.register(owners, event, priority: DEFAULT_PRIORITY, &block) + Array(owners).each do |owner| + register_one(owner, event, priority_value(priority), &block) + end + end + + # Ensure the priority is a Fixnum + def self.priority_value(priority) + return priority if priority.is_a?(Integer) + + PRIORITY_MAP[priority] || DEFAULT_PRIORITY + end + + # register a single hook to be called later, internal API + def self.register_one(owner, event, priority, &block) + @registry[owner] ||= { + :post_init => [], + :pre_render => [], + :post_convert => [], + :post_render => [], + :post_write => [], + } + + unless @registry[owner][event] + raise NotAvailable, "Invalid hook. #{owner} supports only the following hooks " \ + "#{@registry[owner].keys.inspect}" + end + + raise Uncallable, "Hooks must respond to :call" unless block.respond_to? :call + + insert_hook owner, event, priority, &block + end + + def self.insert_hook(owner, event, priority, &block) + @hook_priority[block] = [-priority, @hook_priority.size] + @registry[owner][event] << block + end + + # interface for Jekyll core components to trigger hooks + def self.trigger(owner, event, *args) + # proceed only if there are hooks to call + hooks = @registry.dig(owner, event) + return if hooks.nil? || hooks.empty? + + # sort and call hooks according to priority and load order + hooks.sort_by { |h| @hook_priority[h] }.each do |hook| + hook.call(*args) + end + end + end +end diff --git a/lib/jekyll/inclusion.rb b/lib/jekyll/inclusion.rb new file mode 100644 index 0000000..179ddb0 --- /dev/null +++ b/lib/jekyll/inclusion.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +module Jekyll + class Inclusion + attr_reader :site, :name, :path + private :site + + def initialize(site, base, name) + @site = site + @name = name + @path = PathManager.join(base, name) + end + + def render(context) + @template ||= site.liquid_renderer.file(path).parse(content) + @template.render!(context) + rescue Liquid::Error => e + e.template_name = path + e.markup_context = "included " if e.markup_context.nil? + raise e + end + + def content + @content ||= File.read(path, **site.file_read_opts) + end + + def inspect + "#{self.class} #{path.inspect}" + end + alias_method :to_s, :inspect + end +end diff --git a/lib/jekyll/layout.rb b/lib/jekyll/layout.rb new file mode 100644 index 0000000..af3ea71 --- /dev/null +++ b/lib/jekyll/layout.rb @@ -0,0 +1,55 @@ +# frozen_string_literal: true + +module Jekyll + class Layout + include Convertible + + attr_accessor :content, # content of layout + :data, # the Hash that holds the metadata for this layout + :ext # extension of layout + + attr_reader :name, # name of layout + :path, # path to layout + :site, # the Site object + :relative_path # path to layout relative to its base + + # Initialize a new Layout. + # + # site - The Site. + # base - The String path to the source. + # name - The String filename of the post file. + def initialize(site, base, name) + @site = site + @base = base + @name = name + + if site.theme && site.theme.layouts_path.eql?(base) + @base_dir = site.theme.root + @path = site.in_theme_dir(base, name) + else + @base_dir = site.source + @path = site.in_source_dir(base, name) + end + @relative_path = @path.sub(@base_dir, "") + + self.data = {} + + process(name) + read_yaml(base, name) + end + + # Extract information from the layout filename. + # + # name - The String filename of the layout file. + # + # Returns nothing. + def process(name) + self.ext = File.extname(name) + end + + # Returns the object as a debug String. + def inspect + "#<#{self.class} @path=#{@path.inspect}>" + end + end +end diff --git a/lib/jekyll/liquid_extensions.rb b/lib/jekyll/liquid_extensions.rb new file mode 100644 index 0000000..87e06ef --- /dev/null +++ b/lib/jekyll/liquid_extensions.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +module Jekyll + module LiquidExtensions + # Lookup a Liquid variable in the given context. + # + # context - the Liquid context in question. + # variable - the variable name, as a string. + # + # Returns the value of the variable in the context + # or the variable name if not found. + def lookup_variable(context, variable) + lookup = context + + variable.split(".").each do |value| + lookup = lookup[value] + end + + lookup || variable + end + end +end diff --git a/lib/jekyll/liquid_renderer.rb b/lib/jekyll/liquid_renderer.rb new file mode 100644 index 0000000..7655e04 --- /dev/null +++ b/lib/jekyll/liquid_renderer.rb @@ -0,0 +1,80 @@ +# frozen_string_literal: true + +require_relative "liquid_renderer/file" +require_relative "liquid_renderer/table" + +module Jekyll + class LiquidRenderer + def initialize(site) + @site = site + Liquid::Template.error_mode = @site.config["liquid"]["error_mode"].to_sym + reset + end + + def reset + @stats = {} + @cache = {} + end + + def file(filename) + filename = normalize_path(filename) + LiquidRenderer::File.new(self, filename).tap do + @stats[filename] ||= new_profile_hash + end + end + + def increment_bytes(filename, bytes) + @stats[filename][:bytes] += bytes + end + + def increment_time(filename, time) + @stats[filename][:time] += time + end + + def increment_count(filename) + @stats[filename][:count] += 1 + end + + def stats_table(num_of_rows = 50) + LiquidRenderer::Table.new(@stats).to_s(num_of_rows) + end + + def self.format_error(error, path) + "#{error.message} in #{path}" + end + + # A persistent cache to store and retrieve parsed templates based on the filename + # via `LiquidRenderer::File#parse` + # + # It is emptied when `self.reset` is called. + def cache + @cache ||= {} + end + + private + + def normalize_path(filename) + @normalize_path ||= {} + @normalize_path[filename] ||= begin + theme_dir = @site.theme&.root + case filename + when %r!\A(#{Regexp.escape(@site.source)}/)(?<rest>.*)!io + Regexp.last_match(:rest) + when %r!(/gems/.*)*/gems/(?<dirname>[^/]+)(?<rest>.*)!, + %r!(?<dirname>[^/]+/lib)(?<rest>.*)! + "#{Regexp.last_match(:dirname)}#{Regexp.last_match(:rest)}" + when theme_dir && %r!\A#{Regexp.escape(theme_dir)}/(?<rest>.*)!io + PathManager.join(@site.theme.basename, Regexp.last_match(:rest)) + when %r!\A/(.*)! + Regexp.last_match(1) + else + filename + end + end + end + + def new_profile_hash + Hash.new { |hash, key| hash[key] = 0 } + end + end +end diff --git a/lib/jekyll/liquid_renderer/file.rb b/lib/jekyll/liquid_renderer/file.rb new file mode 100644 index 0000000..146c416 --- /dev/null +++ b/lib/jekyll/liquid_renderer/file.rb @@ -0,0 +1,77 @@ +# frozen_string_literal: true + +module Jekyll + class LiquidRenderer + class File + def initialize(renderer, filename) + @renderer = renderer + @filename = filename + end + + def parse(content) + measure_time do + @renderer.cache[@filename] ||= Liquid::Template.parse(content, :line_numbers => true) + end + @template = @renderer.cache[@filename] + + self + end + + def render(*args) + reset_template_assigns + + measure_time do + measure_bytes do + measure_counts do + @template.render(*args) + end + end + end + end + + # This method simply 'rethrows any error' before attempting to render the template. + def render!(*args) + reset_template_assigns + + measure_time do + measure_bytes do + measure_counts do + @template.render!(*args) + end + end + end + end + + def warnings + @template.warnings + end + + private + + # clear assigns to `Liquid::Template` instance prior to rendering since + # `Liquid::Template` instances are cached in Jekyll 4. + def reset_template_assigns + @template.instance_assigns.clear + end + + def measure_counts + @renderer.increment_count(@filename) + yield + end + + def measure_bytes + yield.tap do |str| + @renderer.increment_bytes(@filename, str.bytesize) + end + end + + def measure_time + before = Time.now + yield + ensure + after = Time.now + @renderer.increment_time(@filename, after - before) + end + end + end +end diff --git a/lib/jekyll/liquid_renderer/table.rb b/lib/jekyll/liquid_renderer/table.rb new file mode 100644 index 0000000..b354b15 --- /dev/null +++ b/lib/jekyll/liquid_renderer/table.rb @@ -0,0 +1,55 @@ +# frozen_string_literal: true + +module Jekyll + class LiquidRenderer + class Table + GAUGES = [:count, :bytes, :time].freeze + + def initialize(stats) + @stats = stats + end + + def to_s(num_of_rows = 50) + Jekyll::Profiler.tabulate(data_for_table(num_of_rows)) + end + + private + + # rubocop:disable Metrics/AbcSize + def data_for_table(num_of_rows) + sorted = @stats.sort_by { |_, file_stats| -file_stats[:time] } + sorted = sorted.slice(0, num_of_rows) + + table = [header_labels] + totals = Hash.new { |hash, key| hash[key] = 0 } + + sorted.each do |filename, file_stats| + GAUGES.each { |gauge| totals[gauge] += file_stats[gauge] } + row = [] + row << filename + row << file_stats[:count].to_s + row << format_bytes(file_stats[:bytes]) + row << format("%.3f", file_stats[:time]) + table << row + end + + footer = [] + footer << "TOTAL (for #{sorted.size} files)" + footer << totals[:count].to_s + footer << format_bytes(totals[:bytes]) + footer << format("%.3f", totals[:time]) + table << footer + end + # rubocop:enable Metrics/AbcSize + + def header_labels + GAUGES.map { |gauge| gauge.to_s.capitalize }.unshift("Filename") + end + + def format_bytes(bytes) + bytes /= 1024.0 + format("%.2fK", bytes) + end + end + end +end diff --git a/lib/jekyll/log_adapter.rb b/lib/jekyll/log_adapter.rb new file mode 100644 index 0000000..6ddfa4a --- /dev/null +++ b/lib/jekyll/log_adapter.rb @@ -0,0 +1,151 @@ +# frozen_string_literal: true + +module Jekyll + class LogAdapter + attr_reader :writer, :messages, :level + + LOG_LEVELS = { + :debug => ::Logger::DEBUG, + :info => ::Logger::INFO, + :warn => ::Logger::WARN, + :error => ::Logger::ERROR, + }.freeze + + # Public: Create a new instance of a log writer + # + # writer - Logger compatible instance + # log_level - (optional, symbol) the log level + # + # Returns nothing + def initialize(writer, level = :info) + @messages = [] + @writer = writer + self.log_level = level + end + + # Public: Set the log level on the writer + # + # level - (symbol) the log level + # + # Returns nothing + def log_level=(level) + writer.level = level if level.is_a?(Integer) && level.between?(0, 3) + writer.level = LOG_LEVELS[level] || + raise(ArgumentError, "unknown log level") + @level = level + end + + def adjust_verbosity(options = {}) + # Quiet always wins. + if options[:quiet] + self.log_level = :error + elsif options[:verbose] + self.log_level = :debug + end + debug "Logging at level:", LOG_LEVELS.key(writer.level).to_s + debug "Jekyll Version:", Jekyll::VERSION + end + + # Public: Print a debug message + # + # topic - the topic of the message, e.g. "Configuration file", "Deprecation", etc. + # message - the message detail + # + # Returns nothing + def debug(topic, message = nil, &block) + write(:debug, topic, message, &block) + end + + # Public: Print a message + # + # topic - the topic of the message, e.g. "Configuration file", "Deprecation", etc. + # message - the message detail + # + # Returns nothing + def info(topic, message = nil, &block) + write(:info, topic, message, &block) + end + + # Public: Print a message + # + # topic - the topic of the message, e.g. "Configuration file", "Deprecation", etc. + # message - the message detail + # + # Returns nothing + def warn(topic, message = nil, &block) + write(:warn, topic, message, &block) + end + + # Public: Print an error message + # + # topic - the topic of the message, e.g. "Configuration file", "Deprecation", etc. + # message - the message detail + # + # Returns nothing + def error(topic, message = nil, &block) + write(:error, topic, message, &block) + end + + # Public: Print an error message and immediately abort the process + # + # topic - the topic of the message, e.g. "Configuration file", "Deprecation", etc. + # message - the message detail (can be omitted) + # + # Returns nothing + def abort_with(topic, message = nil, &block) + error(topic, message, &block) + abort + end + + # Internal: Build a topic method + # + # topic - the topic of the message, e.g. "Configuration file", "Deprecation", etc. + # message - the message detail + # + # Returns the formatted message + def message(topic, message = nil) + raise ArgumentError, "block or message, not both" if block_given? && message + + message = yield if block_given? + message = message.to_s.gsub(%r!\s+!, " ") + topic = formatted_topic(topic, block_given?) + out = topic + message + messages << out + out + end + + # Internal: Format the topic + # + # topic - the topic of the message, e.g. "Configuration file", "Deprecation", etc. + # colon - + # + # Returns the formatted topic statement + def formatted_topic(topic, colon = false) + "#{topic}#{colon ? ": " : " "}".rjust(20) + end + + # Internal: Check if the message should be written given the log level. + # + # level_of_message - the Symbol level of message, one of :debug, :info, :warn, :error + # + # Returns whether the message should be written. + def write_message?(level_of_message) + LOG_LEVELS.fetch(level) <= LOG_LEVELS.fetch(level_of_message) + end + + # Internal: Log a message. + # + # level_of_message - the Symbol level of message, one of :debug, :info, :warn, :error + # topic - the String topic or full message + # message - the String message (optional) + # block - a block containing the message (optional) + # + # Returns false if the message was not written, otherwise returns the value of calling + # the appropriate writer method, e.g. writer.info. + def write(level_of_message, topic, message = nil, &block) + return false unless write_message?(level_of_message) + + writer.public_send(level_of_message, message(topic, message, &block)) + end + end +end diff --git a/lib/jekyll/mime.types b/lib/jekyll/mime.types new file mode 100644 index 0000000..10f713c --- /dev/null +++ b/lib/jekyll/mime.types @@ -0,0 +1,940 @@ +# Woah there. Do not edit this file directly. +# This file is generated automatically by script/vendor-mimes. + +application/andrew-inset ez +application/applixware aw +application/atom+xml atom +application/atomcat+xml atomcat +application/atomdeleted+xml atomdeleted +application/atomsvc+xml atomsvc +application/atsc-dwd+xml dwd +application/atsc-held+xml held +application/atsc-rsat+xml rsat +application/bdoc bdoc +application/calendar+xml xcs +application/ccxml+xml ccxml +application/cdfx+xml cdfx +application/cdmi-capability cdmia +application/cdmi-container cdmic +application/cdmi-domain cdmid +application/cdmi-object cdmio +application/cdmi-queue cdmiq +application/cpl+xml cpl +application/cu-seeme cu +application/dash+xml mpd +application/dash-patch+xml mpp +application/davmount+xml davmount +application/docbook+xml dbk +application/dssc+der dssc +application/dssc+xml xdssc +application/ecmascript ecma es +application/emma+xml emma +application/emotionml+xml emotionml +application/epub+zip epub +application/exi exi +application/express exp +application/fdt+xml fdt +application/font-tdpfr pfr +application/geo+json geojson +application/gml+xml gml +application/gpx+xml gpx +application/gxf gxf +application/gzip gz +application/hjson hjson +application/hyperstudio stk +application/inkml+xml ink inkml +application/ipfix ipfix +application/its+xml its +application/java-archive ear jar war +application/java-serialized-object ser +application/java-vm class +application/javascript js mjs +application/json json map +application/json5 json5 +application/jsonml+json jsonml +application/ld+json jsonld +application/lgr+xml lgr +application/lost+xml lostxml +application/mac-binhex40 hqx +application/mac-compactpro cpt +application/mads+xml mads +application/manifest+json webmanifest +application/marc mrc +application/marcxml+xml mrcx +application/mathematica ma mb nb +application/mathml+xml mathml +application/mbox mbox +application/media-policy-dataset+xml mpf +application/mediaservercontrol+xml mscml +application/metalink+xml metalink +application/metalink4+xml meta4 +application/mets+xml mets +application/mmt-aei+xml maei +application/mmt-usd+xml musd +application/mods+xml mods +application/mp21 m21 mp21 +application/mp4 m4p mp4s +application/msword doc dot +application/mxf mxf +application/n-quads nq +application/n-triples nt +application/node cjs +application/octet-stream bin bpk buffer deb deploy dist distz dll dmg dms dump elc exe img iso lrf mar msi msm msp pkg so +application/oda oda +application/oebps-package+xml opf +application/ogg ogx +application/omdoc+xml omdoc +application/onenote onepkg onetmp onetoc onetoc2 +application/oxps oxps +application/p2p-overlay+xml relo +application/patch-ops-error+xml xer +application/pdf pdf +application/pgp-encrypted pgp +application/pgp-keys asc +application/pgp-signature sig +application/pics-rules prf +application/pkcs10 p10 +application/pkcs7-mime p7c p7m +application/pkcs7-signature p7s +application/pkcs8 p8 +application/pkix-attr-cert ac +application/pkix-cert cer +application/pkix-crl crl +application/pkix-pkipath pkipath +application/pkixcmp pki +application/pls+xml pls +application/postscript ai eps ps +application/provenance+xml provx +application/prs.cww cww +application/pskc+xml pskcxml +application/raml+yaml raml +application/rdf+xml owl rdf +application/reginfo+xml rif +application/relax-ng-compact-syntax rnc +application/resource-lists+xml rl +application/resource-lists-diff+xml rld +application/rls-services+xml rs +application/route-apd+xml rapd +application/route-s-tsid+xml sls +application/route-usd+xml rusd +application/rpki-ghostbusters gbr +application/rpki-manifest mft +application/rpki-roa roa +application/rsd+xml rsd +application/rss+xml rss +application/rtf rtf +application/sbml+xml sbml +application/scvp-cv-request scq +application/scvp-cv-response scs +application/scvp-vp-request spq +application/scvp-vp-response spp +application/sdp sdp +application/senml+xml senmlx +application/sensml+xml sensmlx +application/set-payment-initiation setpay +application/set-registration-initiation setreg +application/shf+xml shf +application/sieve sieve siv +application/smil+xml smi smil +application/sparql-query rq +application/sparql-results+xml srx +application/srgs gram +application/srgs+xml grxml +application/sru+xml sru +application/ssdl+xml ssdl +application/ssml+xml ssml +application/swid+xml swidtag +application/tei+xml tei teicorpus +application/thraud+xml tfi +application/timestamped-data tsd +application/toml toml +application/trig trig +application/ttml+xml ttml +application/ubjson ubj +application/urc-ressheet+xml rsheet +application/urc-targetdesc+xml td +application/vnd.1000minds.decision-model+xml 1km +application/vnd.3gpp.pic-bw-large plb +application/vnd.3gpp.pic-bw-small psb +application/vnd.3gpp.pic-bw-var pvb +application/vnd.3gpp2.tcap tcap +application/vnd.3m.post-it-notes pwn +application/vnd.accpac.simply.aso aso +application/vnd.accpac.simply.imp imp +application/vnd.acucobol acu +application/vnd.acucorp acutc atc +application/vnd.adobe.air-application-installer-package+zip air +application/vnd.adobe.formscentral.fcdt fcdt +application/vnd.adobe.fxp fxp fxpl +application/vnd.adobe.xdp+xml xdp +application/vnd.adobe.xfdf xfdf +application/vnd.age age +application/vnd.ahead.space ahead +application/vnd.airzip.filesecure.azf azf +application/vnd.airzip.filesecure.azs azs +application/vnd.amazon.ebook azw +application/vnd.americandynamics.acc acc +application/vnd.amiga.ami ami +application/vnd.android.package-archive apk +application/vnd.anser-web-certificate-issue-initiation cii +application/vnd.anser-web-funds-transfer-initiation fti +application/vnd.antix.game-component atx +application/vnd.apple.installer+xml mpkg +application/vnd.apple.keynote key +application/vnd.apple.mpegurl m3u8 +application/vnd.apple.numbers numbers +application/vnd.apple.pages pages +application/vnd.apple.pkpass pkpass +application/vnd.aristanetworks.swi swi +application/vnd.astraea-software.iota iota +application/vnd.audiograph aep +application/vnd.balsamiq.bmml+xml bmml +application/vnd.blueice.multipass mpm +application/vnd.bmi bmi +application/vnd.businessobjects rep +application/vnd.chemdraw+xml cdxml +application/vnd.chipnuts.karaoke-mmd mmd +application/vnd.cinderella cdy +application/vnd.citationstyles.style+xml csl +application/vnd.claymore cla +application/vnd.cloanto.rp9 rp9 +application/vnd.clonk.c4group c4d c4f c4g c4p c4u +application/vnd.cluetrust.cartomobile-config c11amc +application/vnd.cluetrust.cartomobile-config-pkg c11amz +application/vnd.commonspace csp +application/vnd.contact.cmsg cdbcmsg +application/vnd.cosmocaller cmc +application/vnd.crick.clicker clkx +application/vnd.crick.clicker.keyboard clkk +application/vnd.crick.clicker.palette clkp +application/vnd.crick.clicker.template clkt +application/vnd.crick.clicker.wordbank clkw +application/vnd.criticaltools.wbs+xml wbs +application/vnd.ctc-posml pml +application/vnd.cups-ppd ppd +application/vnd.curl.car car +application/vnd.curl.pcurl pcurl +application/vnd.dart dart +application/vnd.data-vision.rdz rdz +application/vnd.dbf dbf +application/vnd.dece.data uvd uvf uvvd uvvf +application/vnd.dece.ttml+xml uvt uvvt +application/vnd.dece.unspecified uvvx uvx +application/vnd.dece.zip uvvz uvz +application/vnd.denovo.fcselayout-link fe_launch +application/vnd.dna dna +application/vnd.dolby.mlp mlp +application/vnd.dpgraph dpg +application/vnd.dreamfactory dfac +application/vnd.ds-keypoint kpxx +application/vnd.dvb.ait ait +application/vnd.dvb.service svc +application/vnd.dynageo geo +application/vnd.ecowin.chart mag +application/vnd.enliven nml +application/vnd.epson.esf esf +application/vnd.epson.msf msf +application/vnd.epson.quickanime qam +application/vnd.epson.salt slt +application/vnd.epson.ssf ssf +application/vnd.eszigno3+xml es3 et3 +application/vnd.ezpix-album ez2 +application/vnd.ezpix-package ez3 +application/vnd.fdf fdf +application/vnd.fdsn.mseed mseed +application/vnd.fdsn.seed dataless seed +application/vnd.flographit gph +application/vnd.fluxtime.clip ftc +application/vnd.framemaker book fm frame maker +application/vnd.frogans.fnc fnc +application/vnd.frogans.ltf ltf +application/vnd.fsc.weblaunch fsc +application/vnd.fujitsu.oasys oas +application/vnd.fujitsu.oasys2 oa2 +application/vnd.fujitsu.oasys3 oa3 +application/vnd.fujitsu.oasysgp fg5 +application/vnd.fujitsu.oasysprs bh2 +application/vnd.fujixerox.ddd ddd +application/vnd.fujixerox.docuworks xdw +application/vnd.fujixerox.docuworks.binder xbd +application/vnd.fuzzysheet fzs +application/vnd.genomatix.tuxedo txd +application/vnd.geogebra.file ggb +application/vnd.geogebra.tool ggt +application/vnd.geometry-explorer gex gre +application/vnd.geonext gxt +application/vnd.geoplan g2w +application/vnd.geospace g3w +application/vnd.gmx gmx +application/vnd.google-apps.document gdoc +application/vnd.google-apps.presentation gslides +application/vnd.google-apps.spreadsheet gsheet +application/vnd.google-earth.kml+xml kml +application/vnd.google-earth.kmz kmz +application/vnd.grafeq gqf gqs +application/vnd.groove-account gac +application/vnd.groove-help ghf +application/vnd.groove-identity-message gim +application/vnd.groove-injector grv +application/vnd.groove-tool-message gtm +application/vnd.groove-tool-template tpl +application/vnd.groove-vcard vcg +application/vnd.hal+xml hal +application/vnd.handheld-entertainment+xml zmm +application/vnd.hbci hbci +application/vnd.hhe.lesson-player les +application/vnd.hp-hpgl hpgl +application/vnd.hp-hpid hpid +application/vnd.hp-hps hps +application/vnd.hp-jlyt jlt +application/vnd.hp-pcl pcl +application/vnd.hp-pclxl pclxl +application/vnd.hydrostatix.sof-data sfd-hdstx +application/vnd.ibm.minipay mpy +application/vnd.ibm.modcap afp list3820 listafp +application/vnd.ibm.rights-management irm +application/vnd.ibm.secure-container sc +application/vnd.iccprofile icc icm +application/vnd.igloader igl +application/vnd.immervision-ivp ivp +application/vnd.immervision-ivu ivu +application/vnd.insors.igm igm +application/vnd.intercon.formnet xpw xpx +application/vnd.intergeo i2g +application/vnd.intu.qbo qbo +application/vnd.intu.qfx qfx +application/vnd.ipunplugged.rcprofile rcprofile +application/vnd.irepository.package+xml irp +application/vnd.is-xpr xpr +application/vnd.isac.fcs fcs +application/vnd.jam jam +application/vnd.jcp.javame.midlet-rms rms +application/vnd.jisp jisp +application/vnd.joost.joda-archive joda +application/vnd.kahootz ktr ktz +application/vnd.kde.karbon karbon +application/vnd.kde.kchart chrt +application/vnd.kde.kformula kfo +application/vnd.kde.kivio flw +application/vnd.kde.kontour kon +application/vnd.kde.kpresenter kpr kpt +application/vnd.kde.kspread ksp +application/vnd.kde.kword kwd kwt +application/vnd.kenameaapp htke +application/vnd.kidspiration kia +application/vnd.kinar kne knp +application/vnd.koan skd skm skp skt +application/vnd.kodak-descriptor sse +application/vnd.las.las+xml lasxml +application/vnd.llamagraphics.life-balance.desktop lbd +application/vnd.llamagraphics.life-balance.exchange+xml lbe +application/vnd.lotus-1-2-3 123 +application/vnd.lotus-approach apr +application/vnd.lotus-freelance pre +application/vnd.lotus-notes nsf +application/vnd.lotus-organizer org +application/vnd.lotus-screencam scm +application/vnd.lotus-wordpro lwp +application/vnd.macports.portpkg portpkg +application/vnd.mapbox-vector-tile mvt +application/vnd.mcd mcd +application/vnd.medcalcdata mc1 +application/vnd.mediastation.cdkey cdkey +application/vnd.mfer mwf +application/vnd.mfmp mfm +application/vnd.micrografx.flo flo +application/vnd.micrografx.igx igx +application/vnd.mif mif +application/vnd.mobius.daf daf +application/vnd.mobius.dis dis +application/vnd.mobius.mbk mbk +application/vnd.mobius.mqy mqy +application/vnd.mobius.msl msl +application/vnd.mobius.plc plc +application/vnd.mobius.txf txf +application/vnd.mophun.application mpn +application/vnd.mophun.certificate mpc +application/vnd.mozilla.xul+xml xul +application/vnd.ms-artgalry cil +application/vnd.ms-cab-compressed cab +application/vnd.ms-excel xla xlc xlm xls xlt xlw +application/vnd.ms-excel.addin.macroenabled.12 xlam +application/vnd.ms-excel.sheet.binary.macroenabled.12 xlsb +application/vnd.ms-excel.sheet.macroenabled.12 xlsm +application/vnd.ms-excel.template.macroenabled.12 xltm +application/vnd.ms-fontobject eot +application/vnd.ms-htmlhelp chm +application/vnd.ms-ims ims +application/vnd.ms-lrm lrm +application/vnd.ms-officetheme thmx +application/vnd.ms-outlook msg +application/vnd.ms-pki.seccat cat +application/vnd.ms-pki.stl stl +application/vnd.ms-powerpoint pot pps ppt +application/vnd.ms-powerpoint.addin.macroenabled.12 ppam +application/vnd.ms-powerpoint.presentation.macroenabled.12 pptm +application/vnd.ms-powerpoint.slide.macroenabled.12 sldm +application/vnd.ms-powerpoint.slideshow.macroenabled.12 ppsm +application/vnd.ms-powerpoint.template.macroenabled.12 potm +application/vnd.ms-project mpt +application/vnd.ms-word.document.macroenabled.12 docm +application/vnd.ms-word.template.macroenabled.12 dotm +application/vnd.ms-works wcm wdb wks wps +application/vnd.ms-wpl wpl +application/vnd.ms-xpsdocument xps +application/vnd.mseq mseq +application/vnd.musician mus +application/vnd.muvee.style msty +application/vnd.mynfc taglet +application/vnd.neurolanguage.nlu nlu +application/vnd.nitf nitf ntf +application/vnd.noblenet-directory nnd +application/vnd.noblenet-sealer nns +application/vnd.noblenet-web nnw +application/vnd.nokia.n-gage.data ngdat +application/vnd.nokia.n-gage.symbian.install n-gage +application/vnd.nokia.radio-preset rpst +application/vnd.nokia.radio-presets rpss +application/vnd.novadigm.edm edm +application/vnd.novadigm.edx edx +application/vnd.novadigm.ext ext +application/vnd.oasis.opendocument.chart odc +application/vnd.oasis.opendocument.chart-template otc +application/vnd.oasis.opendocument.database odb +application/vnd.oasis.opendocument.formula odf +application/vnd.oasis.opendocument.formula-template odft +application/vnd.oasis.opendocument.graphics odg +application/vnd.oasis.opendocument.graphics-template otg +application/vnd.oasis.opendocument.image odi +application/vnd.oasis.opendocument.image-template oti +application/vnd.oasis.opendocument.presentation odp +application/vnd.oasis.opendocument.presentation-template otp +application/vnd.oasis.opendocument.spreadsheet ods +application/vnd.oasis.opendocument.spreadsheet-template ots +application/vnd.oasis.opendocument.text odt +application/vnd.oasis.opendocument.text-master odm +application/vnd.oasis.opendocument.text-template ott +application/vnd.oasis.opendocument.text-web oth +application/vnd.olpc-sugar xo +application/vnd.oma.dd2+xml dd2 +application/vnd.openblox.game+xml obgx +application/vnd.openofficeorg.extension oxt +application/vnd.openstreetmap.data+xml osm +application/vnd.openxmlformats-officedocument.presentationml.presentation pptx +application/vnd.openxmlformats-officedocument.presentationml.slide sldx +application/vnd.openxmlformats-officedocument.presentationml.slideshow ppsx +application/vnd.openxmlformats-officedocument.presentationml.template potx +application/vnd.openxmlformats-officedocument.spreadsheetml.sheet xlsx +application/vnd.openxmlformats-officedocument.spreadsheetml.template xltx +application/vnd.openxmlformats-officedocument.wordprocessingml.document docx +application/vnd.openxmlformats-officedocument.wordprocessingml.template dotx +application/vnd.osgeo.mapguide.package mgp +application/vnd.osgi.dp dp +application/vnd.osgi.subsystem esa +application/vnd.palm oprc pdb pqa +application/vnd.pawaafile paw +application/vnd.pg.format str +application/vnd.pg.osasli ei6 +application/vnd.picsel efif +application/vnd.pmi.widget wg +application/vnd.pocketlearn plf +application/vnd.powerbuilder6 pbd +application/vnd.previewsystems.box box +application/vnd.proteus.magazine mgz +application/vnd.publishare-delta-tree qps +application/vnd.pvi.ptid1 ptid +application/vnd.quark.quarkxpress qwd qwt qxb qxd qxl qxt +application/vnd.rar rar +application/vnd.realvnc.bed bed +application/vnd.recordare.musicxml mxl +application/vnd.recordare.musicxml+xml musicxml +application/vnd.rig.cryptonote cryptonote +application/vnd.rim.cod cod +application/vnd.rn-realmedia rm +application/vnd.rn-realmedia-vbr rmvb +application/vnd.route66.link66+xml link66 +application/vnd.sailingtracker.track st +application/vnd.seemail see +application/vnd.sema sema +application/vnd.semd semd +application/vnd.semf semf +application/vnd.shana.informed.formdata ifm +application/vnd.shana.informed.formtemplate itp +application/vnd.shana.informed.interchange iif +application/vnd.shana.informed.package ipk +application/vnd.simtech-mindmapper twd twds +application/vnd.smaf mmf +application/vnd.smart.teacher teacher +application/vnd.software602.filler.form+xml fo +application/vnd.solent.sdkm+xml sdkd sdkm +application/vnd.spotfire.dxp dxp +application/vnd.spotfire.sfs sfs +application/vnd.stardivision.calc sdc +application/vnd.stardivision.draw sda +application/vnd.stardivision.impress sdd +application/vnd.stardivision.math smf +application/vnd.stardivision.writer sdw vor +application/vnd.stardivision.writer-global sgl +application/vnd.stepmania.package smzip +application/vnd.stepmania.stepchart sm +application/vnd.sun.wadl+xml wadl +application/vnd.sun.xml.calc sxc +application/vnd.sun.xml.calc.template stc +application/vnd.sun.xml.draw sxd +application/vnd.sun.xml.draw.template std +application/vnd.sun.xml.impress sxi +application/vnd.sun.xml.impress.template sti +application/vnd.sun.xml.math sxm +application/vnd.sun.xml.writer sxw +application/vnd.sun.xml.writer.global sxg +application/vnd.sun.xml.writer.template stw +application/vnd.sus-calendar sus susp +application/vnd.svd svd +application/vnd.symbian.install sis sisx +application/vnd.syncml+xml xsm +application/vnd.syncml.dm+wbxml bdm +application/vnd.syncml.dm+xml xdm +application/vnd.syncml.dmddf+xml ddf +application/vnd.tao.intent-module-archive tao +application/vnd.tcpdump.pcap cap dmp pcap +application/vnd.tmobile-livetv tmo +application/vnd.trid.tpt tpt +application/vnd.triscape.mxs mxs +application/vnd.trueapp tra +application/vnd.ufdl ufd ufdl +application/vnd.uiq.theme utz +application/vnd.umajin umj +application/vnd.unity unityweb +application/vnd.uoml+xml uoml +application/vnd.vcx vcx +application/vnd.visio vsd vss vst vsw +application/vnd.visionary vis +application/vnd.vsf vsf +application/vnd.wap.wbxml wbxml +application/vnd.wap.wmlc wmlc +application/vnd.wap.wmlscriptc wmlsc +application/vnd.webturbo wtb +application/vnd.wolfram.player nbp +application/vnd.wordperfect wpd +application/vnd.wqd wqd +application/vnd.wt.stf stf +application/vnd.xara xar +application/vnd.xfdl xfdl +application/vnd.yamaha.hv-dic hvd +application/vnd.yamaha.hv-script hvs +application/vnd.yamaha.hv-voice hvp +application/vnd.yamaha.openscoreformat osf +application/vnd.yamaha.openscoreformat.osfpvg+xml osfpvg +application/vnd.yamaha.smaf-audio saf +application/vnd.yamaha.smaf-phrase spf +application/vnd.yellowriver-custom-menu cmp +application/vnd.zul zir zirz +application/vnd.zzazz.deck+xml zaz +application/voicexml+xml vxml +application/wasm wasm +application/watcherinfo+xml wif +application/widget wgt +application/winhlp hlp +application/wsdl+xml wsdl +application/wspolicy+xml wspolicy +application/x-7z-compressed 7z +application/x-abiword abw +application/x-ace-compressed ace +application/x-arj arj +application/x-authorware-bin aab u32 vox x32 +application/x-authorware-map aam +application/x-authorware-seg aas +application/x-bcpio bcpio +application/x-bittorrent torrent +application/x-blorb blb blorb +application/x-bzip bz +application/x-bzip2 boz bz2 +application/x-cbr cb7 cba cbr cbt cbz +application/x-cdlink vcd +application/x-cfs-compressed cfs +application/x-chat chat +application/x-chess-pgn pgn +application/x-chrome-extension crx +application/x-cocoa cco +application/x-conference nsc +application/x-cpio cpio +application/x-csh csh +application/x-debian-package udeb +application/x-dgc-compressed dgc +application/x-director cct cst cxt dcr dir dxr fgd swa w3d +application/x-doom wad +application/x-dtbncx+xml ncx +application/x-dtbook+xml dtb +application/x-dtbresource+xml res +application/x-dvi dvi +application/x-envoy evy +application/x-eva eva +application/x-font-bdf bdf +application/x-font-ghostscript gsf +application/x-font-linux-psf psf +application/x-font-pcf pcf +application/x-font-snf snf +application/x-font-type1 afm pfa pfb pfm +application/x-freearc arc +application/x-futuresplash spl +application/x-gca-compressed gca +application/x-glulx ulx +application/x-gnumeric gnumeric +application/x-gramps-xml gramps +application/x-gtar gtar +application/x-hdf hdf +application/x-httpd-php php +application/x-install-instructions install +application/x-java-archive-diff jardiff +application/x-java-jnlp-file jnlp +application/x-keepass2 kdbx +application/x-latex latex +application/x-lua-bytecode luac +application/x-lzh-compressed lha lzh +application/x-makeself run +application/x-mie mie +application/x-mobipocket-ebook mobi prc +application/x-ms-application application +application/x-ms-shortcut lnk +application/x-ms-wmd wmd +application/x-ms-wmz wmz +application/x-ms-xbap xbap +application/x-msaccess mdb +application/x-msbinder obd +application/x-mscardfile crd +application/x-msclip clp +application/x-msdownload bat com +application/x-msmediaview m13 m14 mvb +application/x-msmetafile emf emz wmf +application/x-msmoney mny +application/x-mspublisher pub +application/x-msschedule scd +application/x-msterminal trm +application/x-mswrite wri +application/x-netcdf cdf nc +application/x-ns-proxy-autoconfig pac +application/x-nzb nzb +application/x-perl pl pm +application/x-pkcs12 p12 pfx +application/x-pkcs7-certificates p7b spc +application/x-pkcs7-certreqresp p7r +application/x-redhat-package-manager rpm +application/x-research-info-systems ris +application/x-sea sea +application/x-sh sh +application/x-shar shar +application/x-shockwave-flash swf +application/x-silverlight-app xap +application/x-sql sql +application/x-stuffit sit +application/x-stuffitx sitx +application/x-subrip srt +application/x-sv4cpio sv4cpio +application/x-sv4crc sv4crc +application/x-t3vm-image t3 +application/x-tads gam +application/x-tar tar +application/x-tcl tcl tk +application/x-tex tex +application/x-tex-tfm tfm +application/x-texinfo texi texinfo +application/x-tgif obj +application/x-ustar ustar +application/x-virtualbox-hdd hdd +application/x-virtualbox-ova ova +application/x-virtualbox-ovf ovf +application/x-virtualbox-vbox vbox +application/x-virtualbox-vbox-extpack vbox-extpack +application/x-virtualbox-vdi vdi +application/x-virtualbox-vhd vhd +application/x-virtualbox-vmdk vmdk +application/x-wais-source src +application/x-web-app-manifest+json webapp +application/x-x509-ca-cert crt der pem +application/x-xfig fig +application/x-xliff+xml xlf +application/x-xpinstall xpi +application/x-xz xz +application/x-zmachine z1 z2 z3 z4 z5 z6 z7 z8 +application/xaml+xml xaml +application/xcap-att+xml xav +application/xcap-caps+xml xca +application/xcap-diff+xml xdf +application/xcap-el+xml xel +application/xcap-ns+xml xns +application/xenc+xml xenc +application/xhtml+xml xht xhtml +application/xml rng xml xsd xsl +application/xml-dtd dtd +application/xop+xml xop +application/xproc+xml xpl +application/xslt+xml xslt +application/xspf+xml xspf +application/xv+xml mxml xhvml xvm xvml +application/yang yang +application/yin+xml yin +application/zip zip +audio/3gpp 3gpp +audio/adpcm adp +audio/amr amr +audio/basic au snd +audio/midi kar mid midi rmi +audio/mobile-xmf mxmf +audio/mp3 mp3 +audio/mp4 m4a mp4a +audio/mpeg m2a m3a mp2 mp2a mpga +audio/ogg oga ogg opus spx +audio/s3m s3m +audio/silk sil +audio/vnd.dece.audio uva uvva +audio/vnd.digital-winds eol +audio/vnd.dra dra +audio/vnd.dts dts +audio/vnd.dts.hd dtshd +audio/vnd.lucent.voice lvp +audio/vnd.ms-playready.media.pya pya +audio/vnd.nuera.ecelp4800 ecelp4800 +audio/vnd.nuera.ecelp7470 ecelp7470 +audio/vnd.nuera.ecelp9600 ecelp9600 +audio/vnd.rip rip +audio/wav wav +audio/webm weba +audio/x-aac aac +audio/x-aiff aif aifc aiff +audio/x-caf caf +audio/x-flac flac +audio/x-matroska mka +audio/x-mpegurl m3u +audio/x-ms-wax wax +audio/x-ms-wma wma +audio/x-pn-realaudio ra ram +audio/x-pn-realaudio-plugin rmp +audio/xm xm +chemical/x-cdx cdx +chemical/x-cif cif +chemical/x-cmdf cmdf +chemical/x-cml cml +chemical/x-csml csml +chemical/x-xyz xyz +font/collection ttc +font/otf otf +font/ttf ttf +font/woff woff +font/woff2 woff2 +image/aces exr +image/apng apng +image/avci avci +image/avcs avcs +image/avif avif +image/bmp bmp +image/cgm cgm +image/dicom-rle drle +image/fits fits +image/g3fax g3 +image/gif gif +image/heic heic +image/heic-sequence heics +image/heif heif +image/heif-sequence heifs +image/hej2k hej2 +image/hsj2 hsj2 +image/ief ief +image/jls jls +image/jp2 jp2 jpg2 +image/jpeg jpe jpeg jpg +image/jph jph +image/jphc jhc +image/jpm jpm +image/jpx jpf jpx +image/jxr jxr +image/jxra jxra +image/jxrs jxrs +image/jxs jxs +image/jxsc jxsc +image/jxsi jxsi +image/jxss jxss +image/ktx ktx +image/ktx2 ktx2 +image/png png +image/prs.btif btif +image/prs.pti pti +image/sgi sgi +image/svg+xml svg svgz +image/t38 t38 +image/tiff tif tiff +image/tiff-fx tfx +image/vnd.adobe.photoshop psd +image/vnd.airzip.accelerator.azv azv +image/vnd.dece.graphic uvg uvi uvvg uvvi +image/vnd.djvu djv djvu +image/vnd.dvb.subtitle sub +image/vnd.dwg dwg +image/vnd.dxf dxf +image/vnd.fastbidsheet fbs +image/vnd.fpx fpx +image/vnd.fst fst +image/vnd.fujixerox.edmics-mmr mmr +image/vnd.fujixerox.edmics-rlc rlc +image/vnd.microsoft.icon ico +image/vnd.ms-dds dds +image/vnd.ms-modi mdi +image/vnd.ms-photo wdp +image/vnd.net-fpx npx +image/vnd.pco.b16 b16 +image/vnd.tencent.tap tap +image/vnd.valve.source.texture vtf +image/vnd.wap.wbmp wbmp +image/vnd.xiff xif +image/vnd.zbrush.pcx pcx +image/webp webp +image/x-3ds 3ds +image/x-cmu-raster ras +image/x-cmx cmx +image/x-freehand fh fh4 fh5 fh7 fhc +image/x-jng jng +image/x-mrsid-image sid +image/x-pict pct pic +image/x-portable-anymap pnm +image/x-portable-bitmap pbm +image/x-portable-graymap pgm +image/x-portable-pixmap ppm +image/x-rgb rgb +image/x-tga tga +image/x-xbitmap xbm +image/x-xpixmap xpm +image/x-xwindowdump xwd +message/disposition-notification disposition-notification +message/global u8msg +message/global-delivery-status u8dsn +message/global-disposition-notification u8mdn +message/global-headers u8hdr +message/rfc822 eml mime +message/vnd.wfa.wsc wsc +model/3mf 3mf +model/gltf+json gltf +model/gltf-binary glb +model/iges iges igs +model/mesh mesh msh silo +model/mtl mtl +model/step+xml stpx +model/step+zip stpz +model/step-xml+zip stpxz +model/vnd.collada+xml dae +model/vnd.dwf dwf +model/vnd.gdl gdl +model/vnd.gtw gtw +model/vnd.mts mts +model/vnd.opengex ogex +model/vnd.parasolid.transmit.binary x_b +model/vnd.parasolid.transmit.text x_t +model/vnd.sap.vds vds +model/vnd.usdz+zip usdz +model/vnd.valve.source.compiled-map bsp +model/vnd.vtu vtu +model/vrml vrml wrl +model/x3d+binary x3db x3dbz +model/x3d+vrml x3dv x3dvz +model/x3d+xml x3d x3dz +text/cache-manifest appcache manifest +text/calendar ics ifb +text/coffeescript coffee litcoffee +text/css css +text/csv csv +text/html htm html shtml +text/jade jade +text/jsx jsx +text/less less +text/markdown markdown md +text/mathml mml +text/mdx mdx +text/n3 n3 +text/plain conf def in ini list log text txt +text/prs.lines.tag dsc +text/richtext rtx +text/sgml sgm sgml +text/shex shex +text/slim slim slm +text/spdx spdx +text/stylus styl stylus +text/tab-separated-values tsv +text/troff man me ms roff t tr +text/turtle ttl +text/uri-list uri uris urls +text/vcard vcard +text/vnd.curl curl +text/vnd.curl.dcurl dcurl +text/vnd.curl.mcurl mcurl +text/vnd.curl.scurl scurl +text/vnd.familysearch.gedcom ged +text/vnd.fly fly +text/vnd.fmi.flexstor flx +text/vnd.graphviz gv +text/vnd.in3d.3dml 3dml +text/vnd.in3d.spot spot +text/vnd.sun.j2me.app-descriptor jad +text/vnd.wap.wml wml +text/vnd.wap.wmlscript wmls +text/vtt vtt +text/x-asm asm s +text/x-c c cc cpp cxx dic h hh +text/x-component htc +text/x-fortran f f77 f90 for +text/x-handlebars-template hbs +text/x-java-source java +text/x-lua lua +text/x-markdown mkd +text/x-nfo nfo +text/x-opml opml +text/x-pascal p pas +text/x-processing pde +text/x-sass sass +text/x-scss scss +text/x-setext etx +text/x-sfv sfv +text/x-suse-ymp ymp +text/x-uuencode uu +text/x-vcalendar vcs +text/x-vcard vcf +text/yaml yaml yml +video/3gpp 3gp +video/3gpp2 3g2 +video/h261 h261 +video/h263 h263 +video/h264 h264 +video/iso.segment m4s +video/jpeg jpgv +video/jpm jpgm +video/mj2 mj2 mjp2 +video/mp2t ts +video/mp4 mp4 mp4v mpg4 +video/mpeg m1v m2v mpe mpeg mpg +video/ogg ogv +video/quicktime mov qt +video/vnd.dece.hd uvh uvvh +video/vnd.dece.mobile uvm uvvm +video/vnd.dece.pd uvp uvvp +video/vnd.dece.sd uvs uvvs +video/vnd.dece.video uvv uvvv +video/vnd.dvb.file dvb +video/vnd.fvt fvt +video/vnd.mpegurl m4u mxu +video/vnd.ms-playready.media.pyv pyv +video/vnd.uvvu.mp4 uvu uvvu +video/vnd.vivo viv +video/webm webm +video/x-f4v f4v +video/x-fli fli +video/x-flv flv +video/x-m4v m4v +video/x-matroska mk3d mks mkv +video/x-mng mng +video/x-ms-asf asf asx +video/x-ms-vob vob +video/x-ms-wm wm +video/x-ms-wmv wmv +video/x-ms-wmx wmx +video/x-ms-wvx wvx +video/x-msvideo avi +video/x-sgi-movie movie +video/x-smv smv +x-conference/x-cooltalk ice \ No newline at end of file diff --git a/lib/jekyll/page.rb b/lib/jekyll/page.rb new file mode 100644 index 0000000..64821a8 --- /dev/null +++ b/lib/jekyll/page.rb @@ -0,0 +1,215 @@ +# frozen_string_literal: true + +module Jekyll + class Page + include Convertible + + attr_writer :dir + attr_accessor :basename, :content, :data, :ext, :name, :output, :pager, :site + + alias_method :extname, :ext + + # Attributes for Liquid templates + ATTRIBUTES_FOR_LIQUID = %w( + content + dir + excerpt + name + path + url + ).freeze + + # A set of extensions that are considered HTML or HTML-like so we + # should not alter them, this includes .xhtml through XHTM5. + + HTML_EXTENSIONS = %w( + .html + .xhtml + .htm + ).freeze + + # Initialize a new Page. + # + # site - The Site object. + # base - The String path to the source. + # dir - The String path between the source and the file. + # name - The String filename of the file. + def initialize(site, base, dir, name) + @site = site + @base = base + @dir = dir + @name = name + @path = if site.in_theme_dir(base) == base # we're in a theme + site.in_theme_dir(base, dir, name) + else + site.in_source_dir(base, dir, name) + end + + process(name) + read_yaml(PathManager.join(base, dir), name) + generate_excerpt if site.config["page_excerpts"] + + data.default_proc = proc do |_, key| + site.frontmatter_defaults.find(relative_path, type, key) + end + + Jekyll::Hooks.trigger :pages, :post_init, self + end + + # The generated directory into which the page will be placed + # upon generation. This is derived from the permalink or, if + # permalink is absent, will be '/' + # + # Returns the String destination directory. + def dir + url.end_with?("/") ? url : url_dir + end + + # The full path and filename of the post. Defined in the YAML of the post + # body. + # + # Returns the String permalink or nil if none has been set. + def permalink + data.nil? ? nil : data["permalink"] + end + + # The template of the permalink. + # + # Returns the template String. + def template + if !html? + "/:path/:basename:output_ext" + elsif index? + "/:path/" + else + Utils.add_permalink_suffix("/:path/:basename", site.permalink_style) + end + end + + # The generated relative url of this page. e.g. /about.html. + # + # Returns the String url. + def url + @url ||= URL.new( + :template => template, + :placeholders => url_placeholders, + :permalink => permalink + ).to_s + end + + # Returns a hash of URL placeholder names (as symbols) mapping to the + # desired placeholder replacements. For details see "url.rb" + def url_placeholders + { + :path => @dir, + :basename => basename, + :output_ext => output_ext, + } + end + + # Extract information from the page filename. + # + # name - The String filename of the page file. + # + # NOTE: `String#gsub` removes all trailing periods (in comparison to `String#chomp`) + # Returns nothing. + def process(name) + return unless name + + self.ext = File.extname(name) + self.basename = name[0..-ext.length - 1].gsub(%r!\.*\z!, "") + end + + # Add any necessary layouts to this post + # + # layouts - The Hash of {"name" => "layout"}. + # site_payload - The site payload Hash. + # + # Returns String rendered page. + def render(layouts, site_payload) + site_payload["page"] = to_liquid + site_payload["paginator"] = pager.to_liquid + + do_layout(site_payload, layouts) + end + + # The path to the source file + # + # Returns the path to the source file + def path + data.fetch("path") { relative_path } + end + + # The path to the page source file, relative to the site source + def relative_path + @relative_path ||= PathManager.join(@dir, @name).delete_prefix("/") + end + + # Obtain destination path. + # + # dest - The String path to the destination dir. + # + # Returns the destination file path String. + def destination(dest) + @destination ||= {} + @destination[dest] ||= begin + path = site.in_dest_dir(dest, URL.unescape_path(url)) + path = File.join(path, "index") if url.end_with?("/") + path << output_ext unless path.end_with? output_ext + path + end + end + + # Returns the object as a debug String. + def inspect + "#<#{self.class} @relative_path=#{relative_path.inspect}>" + end + + # Returns the Boolean of whether this Page is HTML or not. + def html? + HTML_EXTENSIONS.include?(output_ext) + end + + # Returns the Boolean of whether this Page is an index file or not. + def index? + basename == "index" + end + + def trigger_hooks(hook_name, *args) + Jekyll::Hooks.trigger :pages, hook_name, self, *args + end + + def write? + true + end + + def excerpt_separator + @excerpt_separator ||= (data["excerpt_separator"] || site.config["excerpt_separator"]).to_s + end + + def excerpt + return @excerpt if defined?(@excerpt) + + @excerpt = data["excerpt"] ? data["excerpt"].to_s : nil + end + + def generate_excerpt? + !excerpt_separator.empty? && instance_of?(Jekyll::Page) && html? + end + + private + + def generate_excerpt + return unless generate_excerpt? + + data["excerpt"] ||= Jekyll::PageExcerpt.new(self) + end + + def url_dir + @url_dir ||= begin + value = File.dirname(url) + value.end_with?("/") ? value : "#{value}/" + end + end + end +end diff --git a/lib/jekyll/page_excerpt.rb b/lib/jekyll/page_excerpt.rb new file mode 100644 index 0000000..dd79fca --- /dev/null +++ b/lib/jekyll/page_excerpt.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +module Jekyll + class PageExcerpt < Excerpt + attr_reader :doc + alias_method :id, :relative_path + + EXCERPT_ATTRIBUTES = (Page::ATTRIBUTES_FOR_LIQUID - %w(excerpt)).freeze + private_constant :EXCERPT_ATTRIBUTES + + def to_liquid + @to_liquid ||= doc.to_liquid(EXCERPT_ATTRIBUTES) + end + + def render_with_liquid? + return false if data["render_with_liquid"] == false + + Jekyll::Utils.has_liquid_construct?(content) + end + + def inspect + "#<#{self.class} id=#{id.inspect}>" + end + end +end diff --git a/lib/jekyll/page_without_a_file.rb b/lib/jekyll/page_without_a_file.rb new file mode 100644 index 0000000..e314f97 --- /dev/null +++ b/lib/jekyll/page_without_a_file.rb @@ -0,0 +1,14 @@ +# 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 + end +end diff --git a/lib/jekyll/path_manager.rb b/lib/jekyll/path_manager.rb new file mode 100644 index 0000000..5d97522 --- /dev/null +++ b/lib/jekyll/path_manager.rb @@ -0,0 +1,74 @@ +# frozen_string_literal: true + +module Jekyll + # A singleton class that caches frozen instances of path strings returned from its methods. + # + # NOTE: + # This class exists because `File.join` allocates an Array and returns a new String on every + # call using **the same arguments**. Caching the result means reduced memory usage. + # However, the caches are never flushed so that they can be used even when a site is + # regenerating. The results are frozen to deter mutation of the cached string. + # + # Therefore, employ this class only for situations where caching the result is necessary + # for performance reasons. + # + class PathManager + # This class cannot be initialized from outside + private_class_method :new + + class << self + # Wraps `File.join` to cache the frozen result. + # Reassigns `nil`, empty strings and empty arrays to a frozen empty string beforehand. + # + # Returns a frozen string. + def join(base, item) + base = "" if base.nil? || base.empty? + item = "" if item.nil? || item.empty? + @join ||= {} + @join[base] ||= {} + @join[base][item] ||= File.join(base, item).freeze + end + + # Ensures the questionable path is prefixed with the base directory + # and prepends the questionable path with the base directory if false. + # + # Returns a frozen string. + def sanitized_path(base_directory, questionable_path) + @sanitized_path ||= {} + @sanitized_path[base_directory] ||= {} + @sanitized_path[base_directory][questionable_path] ||= if questionable_path.nil? + base_directory.freeze + else + sanitize_and_join( + base_directory, questionable_path + ).freeze + end + end + + private + + def sanitize_and_join(base_directory, questionable_path) + clean_path = if questionable_path.start_with?("~") + questionable_path.dup.insert(0, "/") + else + questionable_path + end + clean_path = File.expand_path(clean_path, "/") + return clean_path if clean_path.eql?(base_directory) + + # remove any remaining extra leading slashes not stripped away by calling + # `File.expand_path` above. + clean_path.squeeze!("/") + return clean_path if clean_path.start_with?(slashed_dir_cache(base_directory)) + + clean_path.sub!(%r!\A\w:/!, "/") + join(base_directory, clean_path) + end + + def slashed_dir_cache(base_directory) + @slashed_dir_cache ||= {} + @slashed_dir_cache[base_directory] ||= base_directory.sub(%r!\z!, "/") + end + end + end +end diff --git a/lib/jekyll/plugin.rb b/lib/jekyll/plugin.rb new file mode 100644 index 0000000..1157e12 --- /dev/null +++ b/lib/jekyll/plugin.rb @@ -0,0 +1,92 @@ +# frozen_string_literal: true + +module Jekyll + class Plugin + PRIORITIES = { + :low => -10, + :highest => 100, + :lowest => -100, + :normal => 0, + :high => 10, + }.freeze + + # + + def self.inherited(const) + catch_inheritance(const) do |const_| + catch_inheritance(const_) + end + end + + # + + def self.catch_inheritance(const) + const.define_singleton_method :inherited do |const_| + (@children ||= Set.new).add const_ + yield const_ if block_given? + end + end + + # + + def self.descendants + @children ||= Set.new + out = @children.map(&:descendants) + out << self unless superclass == Plugin + Set.new(out).flatten + end + + # Get or set the priority of this plugin. When called without an + # argument it returns the priority. When an argument is given, it will + # set the priority. + # + # priority - The Symbol priority (default: nil). Valid options are: + # :lowest, :low, :normal, :high, :highest + # + # Returns the Symbol priority. + def self.priority(priority = nil) + @priority ||= nil + @priority = priority if priority && PRIORITIES.key?(priority) + @priority || :normal + end + + # Get or set the safety of this plugin. When called without an argument + # it returns the safety. When an argument is given, it will set the + # safety. + # + # safe - The Boolean safety (default: nil). + # + # Returns the safety Boolean. + def self.safe(safe = nil) + @safe = safe unless defined?(@safe) && safe.nil? + @safe || false + end + + # Spaceship is priority [higher -> lower] + # + # other - The class to be compared. + # + # Returns -1, 0, 1. + def self.<=>(other) + PRIORITIES[other.priority] <=> PRIORITIES[priority] + end + + # Spaceship is priority [higher -> lower] + # + # other - The class to be compared. + # + # Returns -1, 0, 1. + def <=>(other) + self.class <=> other.class + end + + # Initialize a new plugin. This should be overridden by the subclass. + # + # config - The Hash of configuration options. + # + # Returns a new instance. + def initialize(config = {}) + # no-op for default + end + end +end diff --git a/lib/jekyll/plugin_manager.rb b/lib/jekyll/plugin_manager.rb new file mode 100644 index 0000000..84c555a --- /dev/null +++ b/lib/jekyll/plugin_manager.rb @@ -0,0 +1,123 @@ +# frozen_string_literal: true + +module Jekyll + class PluginManager + attr_reader :site + + # Create an instance of this class. + # + # site - the instance of Jekyll::Site we're concerned with + # + # Returns nothing + def initialize(site) + @site = site + end + + # Require all the plugins which are allowed. + # + # Returns nothing + def conscientious_require + require_theme_deps if site.theme + require_plugin_files + require_gems + deprecation_checks + end + + # Require each of the gem plugins specified. + # + # Returns nothing. + def require_gems + Jekyll::External.require_with_graceful_fail( + site.gems.select { |plugin| plugin_allowed?(plugin) } + ) + end + + # Require each of the runtime_dependencies specified by the theme's gemspec. + # + # Returns false only if no dependencies have been specified, otherwise nothing. + def require_theme_deps + return false unless site.theme.runtime_dependencies + + site.theme.runtime_dependencies.each do |dep| + next if dep.name == "jekyll" + + External.require_with_graceful_fail(dep.name) if plugin_allowed?(dep.name) + end + end + + def self.require_from_bundler + if !ENV["JEKYLL_NO_BUNDLER_REQUIRE"] && gemfile_exists? + require "bundler" + + Bundler.setup + required_gems = Bundler.require(:jekyll_plugins) + message = "Required #{required_gems.map(&:name).join(", ")}" + Jekyll.logger.debug("PluginManager:", message) + ENV["JEKYLL_NO_BUNDLER_REQUIRE"] = "true" + + true + else + false + end + end + + # Check for the existence of a Gemfile. + # + # Returns true if a Gemfile exists in the places bundler will look + def self.gemfile_exists? + File.file?("Gemfile") || (ENV["BUNDLE_GEMFILE"] && File.file?(ENV["BUNDLE_GEMFILE"])) + end + + # Check whether a gem plugin is allowed to be used during this build. + # + # plugin_name - the name of the plugin + # + # Returns true if the plugin name is in the whitelist or if the site is not + # in safe mode. + def plugin_allowed?(plugin_name) + !site.safe || whitelist.include?(plugin_name) + end + + # Build an array of allowed plugin gem names. + # + # Returns an array of strings, each string being the name of a gem name + # that is allowed to be used. + def whitelist + @whitelist ||= Array[site.config["whitelist"]].flatten + end + + # Require all .rb files if safe mode is off + # + # Returns nothing. + def require_plugin_files + unless site.safe + plugins_path.each do |plugin_search_path| + plugin_files = Utils.safe_glob(plugin_search_path, File.join("**", "*.rb")) + Jekyll::External.require_with_graceful_fail(plugin_files) + end + end + end + + # Public: Setup the plugin search path + # + # Returns an Array of plugin search paths + def plugins_path + if site.config["plugins_dir"].eql? Jekyll::Configuration::DEFAULTS["plugins_dir"] + [site.in_source_dir(site.config["plugins_dir"])] + else + Array(site.config["plugins_dir"]).map { |d| File.expand_path(d) } + end + end + + def deprecation_checks + pagination_included = (site.config["plugins"] || []).include?("jekyll-paginate") || + defined?(Jekyll::Paginate) + if site.config["paginate"] && !pagination_included + Jekyll::Deprecator.deprecation_message <<~MSG + You appear to have pagination turned on, but you haven't included the `jekyll-paginate` + gem. Ensure you have `plugins: [jekyll-paginate]` in your configuration file. + MSG + end + end + end +end diff --git a/lib/jekyll/profiler.rb b/lib/jekyll/profiler.rb new file mode 100644 index 0000000..9dd879f --- /dev/null +++ b/lib/jekyll/profiler.rb @@ -0,0 +1,55 @@ +# frozen_string_literal: true + +module Jekyll + class Profiler + TERMINAL_TABLE_STYLES = { + :alignment => :right, + :border_top => false, + :border_bottom => false, + }.freeze + private_constant :TERMINAL_TABLE_STYLES + + def self.tabulate(table_rows) + require "terminal-table" + + rows = table_rows.dup + header = rows.shift + output = +"\n" + + table = Terminal::Table.new do |t| + t << header + t << :separator + rows.each { |row| t << row } + t.style = TERMINAL_TABLE_STYLES + t.align_column(0, :left) + end + + output << table.to_s << "\n" + end + + def initialize(site) + @site = site + end + + def profile_process + profile_data = { "PHASE" => "TIME" } + total_time = 0 + + [:reset, :read, :generate, :render, :cleanup, :write].each do |method| + start_time = Time.now + @site.send(method) + end_time = (Time.now - start_time).round(4) + profile_data[method.to_s.upcase] = format("%.4f", end_time) + total_time += end_time + end + + profile_data["TOTAL TIME"] = format("%.4f", total_time) + + Jekyll.logger.info "\nBuild Process Summary:" + Jekyll.logger.info Profiler.tabulate(Array(profile_data)) + + Jekyll.logger.info "\nSite Render Stats:" + @site.print_stats + end + end +end diff --git a/lib/jekyll/publisher.rb b/lib/jekyll/publisher.rb new file mode 100644 index 0000000..26fe4a3 --- /dev/null +++ b/lib/jekyll/publisher.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module Jekyll + class Publisher + def initialize(site) + @site = site + end + + def publish?(thing) + can_be_published?(thing) && !hidden_in_the_future?(thing) + end + + def hidden_in_the_future?(thing) + thing.respond_to?(:date) && !@site.future && thing.date.to_i > @site.time.to_i + end + + private + + def can_be_published?(thing) + thing.data.fetch("published", true) || @site.unpublished + end + end +end diff --git a/lib/jekyll/reader.rb b/lib/jekyll/reader.rb new file mode 100644 index 0000000..5d15177 --- /dev/null +++ b/lib/jekyll/reader.rb @@ -0,0 +1,209 @@ +# frozen_string_literal: true + +module Jekyll + class Reader + attr_reader :site + + def initialize(site) + @site = site + end + + # Read Site data from disk and load it into internal data structures. + # + # Returns nothing. + def read + @site.layouts = LayoutReader.new(site).read + read_directories + read_included_excludes + sort_files! + CollectionReader.new(site).read + ThemeAssetsReader.new(site).read + read_data + end + + # Read and merge the data files. + # If a theme is specified and it contains data, it will be read. + # Site data will overwrite theme data with the same key using the + # semantics of Utils.deep_merge_hashes. + # + # Returns nothing. + def read_data + @site.data = DataReader.new(site).read(site.config["data_dir"]) + return unless site.theme&.data_path + + theme_data = DataReader.new( + site, + :in_source_dir => site.method(:in_theme_dir) + ).read(site.theme.data_path) + @site.data = Jekyll::Utils.deep_merge_hashes(theme_data, @site.data) + end + + # Sorts posts, pages, and static files. + def sort_files! + site.collections.each_value { |c| c.docs.sort! } + site.pages.sort_by!(&:name) + site.static_files.sort_by!(&:relative_path) + end + + # Recursively traverse directories to find pages and static files + # that will become part of the site according to the rules in + # filter_entries. + # + # dir - The String relative path of the directory to read. Default: ''. + # + # Returns nothing. + def read_directories(dir = "") + base = site.in_source_dir(dir) + + return unless File.directory?(base) + + dot_dirs = [] + dot_pages = [] + dot_static_files = [] + + dot = Dir.chdir(base) { filter_entries(Dir.entries("."), base) } + dot.each do |entry| + file_path = @site.in_source_dir(base, entry) + if File.directory?(file_path) + dot_dirs << entry + elsif Utils.has_yaml_header?(file_path) + dot_pages << entry + else + dot_static_files << entry + end + end + + retrieve_posts(dir) + retrieve_dirs(base, dir, dot_dirs) + retrieve_pages(dir, dot_pages) + retrieve_static_files(dir, dot_static_files) + end + + # Retrieves all the posts(posts/drafts) from the given directory + # and add them to the site and sort them. + # + # dir - The String representing the directory to retrieve the posts from. + # + # Returns nothing. + def retrieve_posts(dir) + return if outside_configured_directory?(dir) + + site.posts.docs.concat(post_reader.read_posts(dir)) + site.posts.docs.concat(post_reader.read_drafts(dir)) if site.show_drafts + end + + # Recursively traverse directories with the read_directories function. + # + # base - The String representing the site's base directory. + # dir - The String representing the directory to traverse down. + # dot_dirs - The Array of subdirectories in the dir. + # + # Returns nothing. + def retrieve_dirs(_base, dir, dot_dirs) + dot_dirs.each do |file| + dir_path = site.in_source_dir(dir, file) + rel_path = PathManager.join(dir, file) + @site.reader.read_directories(rel_path) unless @site.dest.chomp("/") == dir_path + end + end + + # Retrieve all the pages from the current directory, + # add them to the site and sort them. + # + # dir - The String representing the directory retrieve the pages from. + # dot_pages - The Array of pages in the dir. + # + # Returns nothing. + def retrieve_pages(dir, dot_pages) + site.pages.concat(PageReader.new(site, dir).read(dot_pages)) + end + + # Retrieve all the static files from the current directory, + # add them to the site and sort them. + # + # dir - The directory retrieve the static files from. + # dot_static_files - The static files in the dir. + # + # Returns nothing. + def retrieve_static_files(dir, dot_static_files) + site.static_files.concat(StaticFileReader.new(site, dir).read(dot_static_files)) + end + + # Filter out any files/directories that are hidden or backup files (start + # with "." or "#" or end with "~"), or contain site content (start with "_"), + # or are excluded in the site configuration, unless they are web server + # files such as '.htaccess'. + # + # entries - The Array of String file/directory entries to filter. + # base_directory - The string representing the optional base directory. + # + # Returns the Array of filtered entries. + def filter_entries(entries, base_directory = nil) + EntryFilter.new(site, base_directory).filter(entries) + end + + # Read the entries from a particular directory for processing + # + # dir - The String representing the relative path of the directory to read. + # subfolder - The String representing the directory to read. + # + # Returns the list of entries to process + def get_entries(dir, subfolder) + base = site.in_source_dir(dir, subfolder) + return [] unless File.exist?(base) + + entries = Dir.chdir(base) { filter_entries(Dir["**/*"], base) } + entries.delete_if { |e| File.directory?(site.in_source_dir(base, e)) } + end + + private + + # Internal + # + # Determine if the directory is supposed to contain posts and drafts. + # If the user has defined a custom collections_dir, then attempt to read + # posts and drafts only from within that directory. + # + # Returns true if a custom collections_dir has been set but current directory lies + # outside that directory. + def outside_configured_directory?(dir) + collections_dir = site.config["collections_dir"] + !collections_dir.empty? && !dir.start_with?("/#{collections_dir}") + end + + # Create a single PostReader instance to retrieve drafts and posts from all valid + # directories in current site. + def post_reader + @post_reader ||= PostReader.new(site) + end + + def read_included_excludes + entry_filter = EntryFilter.new(site) + + site.include.each do |entry| + entry_path = site.in_source_dir(entry) + next if File.directory?(entry_path) + next if entry_filter.symlink?(entry_path) + + read_included_file(entry_path) if File.file?(entry_path) + end + end + + def read_included_file(entry_path) + if Utils.has_yaml_header?(entry_path) + conditionally_generate_entry(entry_path, site.pages, PageReader) + else + conditionally_generate_entry(entry_path, site.static_files, StaticFileReader) + end + end + + def conditionally_generate_entry(entry_path, container, reader) + return if container.find { |item| site.in_source_dir(item.relative_path) == entry_path } + + dir, files = File.split(entry_path) + dir.sub!(site.source, "") + files = Array(files) + container.concat(reader.new(site, dir).read(files)) + end + end +end diff --git a/lib/jekyll/readers/collection_reader.rb b/lib/jekyll/readers/collection_reader.rb new file mode 100644 index 0000000..6d18275 --- /dev/null +++ b/lib/jekyll/readers/collection_reader.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module Jekyll + class CollectionReader + SPECIAL_COLLECTIONS = %w(posts data).freeze + + attr_reader :site, :content + + def initialize(site) + @site = site + @content = {} + end + + # Read in all collections specified in the configuration + # + # Returns nothing. + def read + site.collections.each_value do |collection| + collection.read unless SPECIAL_COLLECTIONS.include?(collection.label) + end + end + end +end diff --git a/lib/jekyll/readers/data_reader.rb b/lib/jekyll/readers/data_reader.rb new file mode 100644 index 0000000..80b57bd --- /dev/null +++ b/lib/jekyll/readers/data_reader.rb @@ -0,0 +1,113 @@ +# frozen_string_literal: true + +module Jekyll + class DataReader + attr_reader :site, :content + + def initialize(site, in_source_dir: nil) + @site = site + @content = {} + @entry_filter = EntryFilter.new(site) + @in_source_dir = in_source_dir || @site.method(:in_source_dir) + @source_dir = @in_source_dir.call("/") + end + + # Read all the files in <dir> and adds them to @content + # + # dir - The String relative path of the directory to read. + # + # Returns @content, a Hash of the .yaml, .yml, + # .json, and .csv files in the base directory + def read(dir) + base = @in_source_dir.call(dir) + read_data_to(base, @content) + @content + end + + # Read and parse all .yaml, .yml, .json, .csv and .tsv + # files under <dir> and add them to the <data> variable. + # + # dir - The string absolute path of the directory to read. + # data - The variable to which data will be added. + # + # Returns nothing + def read_data_to(dir, data) + return unless File.directory?(dir) && !@entry_filter.symlink?(dir) + + entries = Dir.chdir(dir) do + Dir["*.{yaml,yml,json,csv,tsv}"] + Dir["*"].select { |fn| File.directory?(fn) } + end + + entries.each do |entry| + path = @in_source_dir.call(dir, entry) + next if @entry_filter.symlink?(path) + + if File.directory?(path) + read_data_to(path, data[sanitize_filename(entry)] = {}) + else + key = sanitize_filename(File.basename(entry, ".*")) + data[key] = read_data_file(path) + end + end + end + + # Determines how to read a data file. + # + # Returns the contents of the data file. + def read_data_file(path) + Jekyll.logger.debug "Reading:", path.sub(@source_dir, "") + + case File.extname(path).downcase + when ".csv" + CSV.read(path, **csv_config).map { |row| convert_row(row) } + when ".tsv" + CSV.read(path, **tsv_config).map { |row| convert_row(row) } + else + SafeYAML.load_file(path) + end + end + + def sanitize_filename(name) + name.gsub(%r![^\w\s-]+|(?<=^|\b\s)\s+(?=$|\s?\b)!, "") + .gsub(%r!\s+!, "_") + end + + private + + # @return [Hash] + def csv_config + @csv_config ||= read_config("csv_reader") + end + + # @return [Hash] + def tsv_config + @tsv_config ||= read_config("tsv_reader", { :col_sep => "\t" }) + end + + # @param config_key [String] + # @param overrides [Hash] + # @return [Hash] + # @see https://ruby-doc.org/stdlib-2.5.0/libdoc/csv/rdoc/CSV.html#Converters + def read_config(config_key, overrides = {}) + reader_config = config[config_key] || {} + + defaults = { + :converters => reader_config.fetch("csv_converters", []).map(&:to_sym), + :headers => reader_config.fetch("headers", true), + :encoding => reader_config.fetch("encoding", config["encoding"]), + } + + defaults.merge(overrides) + end + + def config + @config ||= site.config + end + + # @param row [Array, CSV::Row] + # @return [Array, Hash] + def convert_row(row) + row.instance_of?(CSV::Row) ? row.to_hash : row + end + end +end diff --git a/lib/jekyll/readers/layout_reader.rb b/lib/jekyll/readers/layout_reader.rb new file mode 100644 index 0000000..981867b --- /dev/null +++ b/lib/jekyll/readers/layout_reader.rb @@ -0,0 +1,62 @@ +# frozen_string_literal: true + +module Jekyll + class LayoutReader + attr_reader :site + + def initialize(site) + @site = site + @layouts = {} + end + + def read + layout_entries.each do |layout_file| + @layouts[layout_name(layout_file)] = \ + Layout.new(site, layout_directory, layout_file) + end + + theme_layout_entries.each do |layout_file| + @layouts[layout_name(layout_file)] ||= \ + Layout.new(site, theme_layout_directory, layout_file) + end + + @layouts + end + + def layout_directory + @layout_directory ||= site.in_source_dir(site.config["layouts_dir"]) + end + + def theme_layout_directory + @theme_layout_directory ||= site.theme.layouts_path if site.theme + end + + private + + def layout_entries + entries_in layout_directory + end + + def theme_layout_entries + theme_layout_directory ? entries_in(theme_layout_directory) : [] + end + + def entries_in(dir) + entries = [] + within(dir) do + entries = EntryFilter.new(site).filter(Dir["**/*.*"]) + end + entries + end + + def layout_name(file) + file.split(".")[0..-2].join(".") + end + + def within(directory) + return unless File.exist?(directory) + + Dir.chdir(directory) { yield } + end + end +end diff --git a/lib/jekyll/readers/page_reader.rb b/lib/jekyll/readers/page_reader.rb new file mode 100644 index 0000000..e72f767 --- /dev/null +++ b/lib/jekyll/readers/page_reader.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +module Jekyll + class PageReader + attr_reader :site, :dir, :unfiltered_content + + def initialize(site, dir) + @site = site + @dir = dir + @unfiltered_content = [] + end + + # Create a new `Jekyll::Page` object for each entry in a given array. + # + # files - An array of file names inside `@dir` + # + # Returns an array of publishable `Jekyll::Page` objects. + def read(files) + files.each do |page| + @unfiltered_content << Page.new(@site, @site.source, @dir, page) + end + @unfiltered_content.select { |page| site.publisher.publish?(page) } + end + end +end diff --git a/lib/jekyll/readers/post_reader.rb b/lib/jekyll/readers/post_reader.rb new file mode 100644 index 0000000..25c5f98 --- /dev/null +++ b/lib/jekyll/readers/post_reader.rb @@ -0,0 +1,85 @@ +# frozen_string_literal: true + +module Jekyll + class PostReader + attr_reader :site, :unfiltered_content + + def initialize(site) + @site = site + end + + # Read all the files in <source>/<dir>/_drafts and create a new + # Document object with each one. + # + # dir - The String relative path of the directory to read. + # + # Returns nothing. + def read_drafts(dir) + read_publishable(dir, "_drafts", Document::DATELESS_FILENAME_MATCHER) + end + + # Read all the files in <source>/<dir>/_posts and create a new Document + # object with each one. + # + # dir - The String relative path of the directory to read. + # + # Returns nothing. + def read_posts(dir) + read_publishable(dir, "_posts", Document::DATE_FILENAME_MATCHER) + end + + # Read all the files in <source>/<dir>/<magic_dir> and create a new + # Document object with each one insofar as it matches the regexp matcher. + # + # dir - The String relative path of the directory to read. + # + # Returns nothing. + def read_publishable(dir, magic_dir, matcher) + read_content(dir, magic_dir, matcher) + .tap { |docs| docs.each(&:read) } + .select { |doc| processable?(doc) } + end + + # Read all the content files from <source>/<dir>/magic_dir + # and return them with the type klass. + # + # dir - The String relative path of the directory to read. + # magic_dir - The String relative directory to <dir>, + # looks for content here. + # klass - The return type of the content. + # + # Returns klass type of content files + def read_content(dir, magic_dir, matcher) + @site.reader.get_entries(dir, magic_dir).map do |entry| + next unless matcher.match?(entry) + + path = @site.in_source_dir(File.join(dir, magic_dir, entry)) + Document.new(path, + :site => @site, + :collection => @site.posts) + end.tap(&:compact!) + end + + private + + def processable?(doc) + if doc.content.nil? + Jekyll.logger.debug "Skipping:", "Content in #{doc.relative_path} is nil" + false + elsif !doc.content.valid_encoding? + Jekyll.logger.debug "Skipping:", "#{doc.relative_path} is not valid UTF-8" + false + else + publishable?(doc) + end + end + + def publishable?(doc) + site.publisher.publish?(doc).tap do |will_publish| + if !will_publish && site.publisher.hidden_in_the_future?(doc) + Jekyll.logger.warn "Skipping:", "#{doc.relative_path} has a future date" + end + end + end + end +end diff --git a/lib/jekyll/readers/static_file_reader.rb b/lib/jekyll/readers/static_file_reader.rb new file mode 100644 index 0000000..5c8a677 --- /dev/null +++ b/lib/jekyll/readers/static_file_reader.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +module Jekyll + class StaticFileReader + attr_reader :site, :dir, :unfiltered_content + + def initialize(site, dir) + @site = site + @dir = dir + @unfiltered_content = [] + end + + # Create a new StaticFile object for every entry in a given list of basenames. + # + # files - an array of file basenames. + # + # Returns an array of static files. + def read(files) + files.each do |file| + @unfiltered_content << StaticFile.new(@site, @site.source, @dir, file) + end + @unfiltered_content + end + end +end diff --git a/lib/jekyll/readers/theme_assets_reader.rb b/lib/jekyll/readers/theme_assets_reader.rb new file mode 100644 index 0000000..b20a3a0 --- /dev/null +++ b/lib/jekyll/readers/theme_assets_reader.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +module Jekyll + class ThemeAssetsReader + attr_reader :site + + def initialize(site) + @site = site + end + + def read + return unless site.theme&.assets_path + + Find.find(site.theme.assets_path) do |path| + next if File.directory?(path) + + if File.symlink?(path) + Jekyll.logger.warn "Theme reader:", "Ignored symlinked asset: #{path}" + else + read_theme_asset(path) + end + end + end + + private + + def read_theme_asset(path) + base = site.theme.root + dir = File.dirname(path.sub("#{site.theme.root}/", "")) + name = File.basename(path) + + if Utils.has_yaml_header?(path) + append_unless_exists site.pages, + Jekyll::Page.new(site, base, dir, name) + else + append_unless_exists site.static_files, + Jekyll::StaticFile.new(site, base, "/#{dir}", name) + end + end + + def append_unless_exists(haystack, new_item) + if haystack.any? { |file| file.relative_path == new_item.relative_path } + Jekyll.logger.debug "Theme:", + "Ignoring #{new_item.relative_path} in theme due to existing file " \ + "with that path in site." + return + end + + haystack << new_item + end + end +end diff --git a/lib/jekyll/regenerator.rb b/lib/jekyll/regenerator.rb new file mode 100644 index 0000000..88c6a81 --- /dev/null +++ b/lib/jekyll/regenerator.rb @@ -0,0 +1,195 @@ +# frozen_string_literal: true + +module Jekyll + class Regenerator + attr_reader :site, :metadata, :cache + attr_accessor :disabled + private :disabled, :disabled= + + def initialize(site) + @site = site + + # Read metadata from file + read_metadata + + # Initialize cache to an empty hash + clear_cache + end + + # Checks if a renderable object needs to be regenerated + # + # Returns a boolean. + def regenerate?(document) + return true if disabled + + case document + when Page + regenerate_page?(document) + when Document + regenerate_document?(document) + else + source_path = document.respond_to?(:path) ? document.path : nil + dest_path = document.destination(@site.dest) if document.respond_to?(:destination) + source_modified_or_dest_missing?(source_path, dest_path) + end + end + + # Add a path to the metadata + # + # Returns true, also on failure. + def add(path) + return true unless File.exist?(path) + + metadata[path] = { + "mtime" => File.mtime(path), + "deps" => [], + } + cache[path] = true + end + + # Force a path to regenerate + # + # Returns true. + def force(path) + cache[path] = true + end + + # Clear the metadata and cache + # + # Returns nothing + def clear + @metadata = {} + clear_cache + end + + # Clear just the cache + # + # Returns nothing + def clear_cache + @cache = {} + end + + # Checks if the source has been modified or the + # destination is missing + # + # returns a boolean + def source_modified_or_dest_missing?(source_path, dest_path) + modified?(source_path) || (dest_path && !File.exist?(dest_path)) + end + + # Checks if a path's (or one of its dependencies) + # mtime has changed + # + # Returns a boolean. + def modified?(path) + return true if disabled? + + # objects that don't have a path are always regenerated + return true if path.nil? + + # Check for path in cache + return cache[path] if cache.key? path + + if metadata[path] + # If we have seen this file before, + # check if it or one of its dependencies has been modified + existing_file_modified?(path) + else + # If we have not seen this file before, add it to the metadata and regenerate it + add(path) + end + end + + # Add a dependency of a path + # + # Returns nothing. + def add_dependency(path, dependency) + return if metadata[path].nil? || disabled + + unless metadata[path]["deps"].include? dependency + metadata[path]["deps"] << dependency + add(dependency) unless metadata.include?(dependency) + end + regenerate? dependency + end + + # Write the metadata to disk + # + # Returns nothing. + def write_metadata + unless disabled? + Jekyll.logger.debug "Writing Metadata:", ".jekyll-metadata" + File.binwrite(metadata_file, Marshal.dump(metadata)) + end + end + + # Produce the absolute path of the metadata file + # + # Returns the String path of the file. + def metadata_file + @metadata_file ||= site.in_source_dir(".jekyll-metadata") + end + + # Check if metadata has been disabled + # + # Returns a Boolean (true for disabled, false for enabled). + def disabled? + self.disabled = !site.incremental? if disabled.nil? + disabled + end + + private + + # Read metadata from the metadata file, if no file is found, + # initialize with an empty hash + # + # Returns the read metadata. + def read_metadata + @metadata = + if !disabled? && File.file?(metadata_file) + content = File.binread(metadata_file) + + begin + Marshal.load(content) + rescue TypeError + SafeYAML.load(content) + rescue ArgumentError => e + Jekyll.logger.warn("Failed to load #{metadata_file}: #{e}") + {} + end + else + {} + end + end + + def regenerate_page?(document) + document.asset_file? || document.data["regenerate"] || + source_modified_or_dest_missing?( + site.in_source_dir(document.relative_path), document.destination(@site.dest) + ) + end + + def regenerate_document?(document) + !document.write? || document.data["regenerate"] || + source_modified_or_dest_missing?( + document.path, document.destination(@site.dest) + ) + end + + def existing_file_modified?(path) + # If one of this file dependencies have been modified, + # set the regeneration bit for both the dependency and the file to true + metadata[path]["deps"].each do |dependency| + return cache[dependency] = cache[path] = true if modified?(dependency) + end + + if File.exist?(path) && metadata[path]["mtime"].eql?(File.mtime(path)) + # If this file has not been modified, set the regeneration bit to false + cache[path] = false + else + # If it has been modified, set it to true + add(path) + end + end + end +end diff --git a/lib/jekyll/related_posts.rb b/lib/jekyll/related_posts.rb new file mode 100644 index 0000000..98fc488 --- /dev/null +++ b/lib/jekyll/related_posts.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +module Jekyll + class RelatedPosts + class << self + attr_accessor :lsi + end + + attr_reader :post, :site + + def initialize(post) + @post = post + @site = post.site + Jekyll::External.require_with_graceful_fail("classifier-reborn") if site.lsi + end + + def build + return [] unless site.posts.docs.size > 1 + + if site.lsi + build_index + lsi_related_posts + else + most_recent_posts + end + end + + def build_index + self.class.lsi ||= begin + lsi = ClassifierReborn::LSI.new(:auto_rebuild => false) + Jekyll.logger.info("Populating LSI...") + + site.posts.docs.each do |x| + lsi.add_item(x) + end + + Jekyll.logger.info("Rebuilding index...") + lsi.build_index + Jekyll.logger.info("") + lsi + end + end + + def lsi_related_posts + self.class.lsi.find_related(post, 11) + end + + def most_recent_posts + @most_recent_posts ||= (site.posts.docs.last(11).reverse! - [post]).first(10) + end + end +end diff --git a/lib/jekyll/renderer.rb b/lib/jekyll/renderer.rb new file mode 100644 index 0000000..c365a0d --- /dev/null +++ b/lib/jekyll/renderer.rb @@ -0,0 +1,263 @@ +# frozen_string_literal: true + +module Jekyll + class Renderer + attr_reader :document, :site + attr_writer :layouts, :payload + + def initialize(site, document, site_payload = nil) + @site = site + @document = document + @payload = site_payload + @layouts = nil + end + + # Fetches the payload used in Liquid rendering. + # It can be written with #payload=(new_payload) + # Falls back to site.site_payload if no payload is set. + # + # Returns a Jekyll::Drops::UnifiedPayloadDrop + def payload + @payload ||= site.site_payload + end + + # The list of layouts registered for this Renderer. + # It can be written with #layouts=(new_layouts) + # Falls back to site.layouts if no layouts are registered. + # + # Returns a Hash of String => Jekyll::Layout identified + # as basename without the extension name. + def layouts + @layouts || site.layouts + end + + # Determine which converters to use based on this document's + # extension. + # + # Returns Array of Converter instances. + def converters + @converters ||= site.converters.select { |c| c.matches(document.extname) }.tap(&:sort!) + end + + # Determine the extname the outputted file should have + # + # Returns String the output extname including the leading period. + def output_ext + @output_ext ||= (permalink_ext || converter_output_ext) + end + + # Prepare payload and render the document + # + # Returns String rendered document output + def run + Jekyll.logger.debug "Rendering:", document.relative_path + + assign_pages! + assign_current_document! + assign_highlighter_options! + assign_layout_data! + + Jekyll.logger.debug "Pre-Render Hooks:", document.relative_path + document.trigger_hooks(:pre_render, payload) + + render_document + end + + # Render the document. + # + # Returns String rendered document output + # rubocop: disable Metrics/AbcSize, Metrics/MethodLength + def render_document + info = { + :registers => { :site => site, :page => payload["page"] }, + :strict_filters => liquid_options["strict_filters"], + :strict_variables => liquid_options["strict_variables"], + } + + output = document.content + if document.render_with_liquid? + Jekyll.logger.debug "Rendering Liquid:", document.relative_path + output = render_liquid(output, payload, info, document.path) + end + + Jekyll.logger.debug "Rendering Markup:", document.relative_path + output = convert(output.to_s) + document.content = output + + Jekyll.logger.debug "Post-Convert Hooks:", document.relative_path + document.trigger_hooks(:post_convert) + output = document.content + + if document.place_in_layout? + Jekyll.logger.debug "Rendering Layout:", document.relative_path + output = place_in_layouts(output, payload, info) + end + + output + end + # rubocop: enable Metrics/AbcSize, Metrics/MethodLength + + # Convert the document using the converters which match this renderer's document. + # + # Returns String the converted content. + def convert(content) + converters.reduce(content) do |output, converter| + converter.convert output + rescue StandardError => e + Jekyll.logger.error "Conversion error:", + "#{converter.class} encountered an error while " \ + "converting '#{document.relative_path}':" + Jekyll.logger.error("", e.to_s) + raise e + end + end + + # Render the given content with the payload and info + # + # content - + # payload - + # info - + # path - (optional) the path to the file, for use in ex + # + # Returns String the content, rendered by Liquid. + def render_liquid(content, payload, info, path = nil) + template = site.liquid_renderer.file(path).parse(content) + template.warnings.each do |e| + Jekyll.logger.warn "Liquid Warning:", + LiquidRenderer.format_error(e, path || document.relative_path) + end + template.render!(payload, info) + # rubocop: disable Lint/RescueException + rescue Exception => e + Jekyll.logger.error "Liquid Exception:", + LiquidRenderer.format_error(e, path || document.relative_path) + raise e + end + # rubocop: enable Lint/RescueException + + # Checks if the layout specified in the document actually exists + # + # layout - the layout to check + # + # Returns Boolean true if the layout is invalid, false if otherwise + def invalid_layout?(layout) + !document.data["layout"].nil? && layout.nil? && !(document.is_a? Jekyll::Excerpt) + end + + # Render layouts and place document content inside. + # + # Returns String rendered content + def place_in_layouts(content, payload, info) + output = content.dup + layout = layouts[document.data["layout"].to_s] + validate_layout(layout) + + used = Set.new([layout]) + + # Reset the payload layout data to ensure it starts fresh for each page. + payload["layout"] = nil + + while layout + output = render_layout(output, layout, info) + add_regenerator_dependencies(layout) + + next unless (layout = site.layouts[layout.data["layout"]]) + break if used.include?(layout) + + used << layout + end + output + end + + private + + # Checks if the layout specified in the document actually exists + # + # layout - the layout to check + # Returns nothing + def validate_layout(layout) + return unless invalid_layout?(layout) + + Jekyll.logger.warn "Build Warning:", "Layout '#{document.data["layout"]}' requested " \ + "in #{document.relative_path} does not exist." + end + + # Render layout content into document.output + # + # Returns String rendered content + def render_layout(output, layout, info) + payload["content"] = output + payload["layout"] = Utils.deep_merge_hashes(layout.data, payload["layout"] || {}) + + render_liquid( + layout.content, + payload, + info, + layout.path + ) + end + + def add_regenerator_dependencies(layout) + return unless document.write? + + site.regenerator.add_dependency( + site.in_source_dir(document.path), + layout.path + ) + end + + # Set page content to payload and assign pager if document has one. + # + # Returns nothing + def assign_pages! + payload["page"] = document.to_liquid + payload["paginator"] = (document.pager.to_liquid if document.respond_to?(:pager)) + end + + # Set related posts to payload if document is a post. + # + # Returns nothing + def assign_current_document! + payload["site"].current_document = document + end + + # Set highlighter prefix and suffix + # + # Returns nothing + def assign_highlighter_options! + payload["highlighter_prefix"] = converters.first.highlighter_prefix + payload["highlighter_suffix"] = converters.first.highlighter_suffix + end + + def assign_layout_data! + layout = layouts[document.data["layout"]] + payload["layout"] = Utils.deep_merge_hashes(layout.data, payload["layout"] || {}) if layout + end + + def permalink_ext + document_permalink = document.permalink + if document_permalink && !document_permalink.end_with?("/") + permalink_ext = File.extname(document_permalink) + permalink_ext unless permalink_ext.empty? + end + end + + def converter_output_ext + if output_exts.size == 1 + output_exts.last + else + output_exts[-2] + end + end + + def output_exts + @output_exts ||= converters.map do |c| + c.output_ext(document.extname) + end.tap(&:compact!) + end + + def liquid_options + @liquid_options ||= site.config["liquid"] + end + end +end diff --git a/lib/jekyll/site.rb b/lib/jekyll/site.rb new file mode 100644 index 0000000..d6c5a0b --- /dev/null +++ b/lib/jekyll/site.rb @@ -0,0 +1,582 @@ +# frozen_string_literal: true + +module Jekyll + class Site + attr_accessor :baseurl, :converters, :data, :drafts, :exclude, + :file_read_opts, :future, :gems, :generators, :highlighter, + :include, :inclusions, :keep_files, :layouts, :limit_posts, + :lsi, :pages, :permalink_style, :plugin_manager, :plugins, + :reader, :safe, :show_drafts, :static_files, :theme, :time, + :unpublished + + attr_reader :cache_dir, :config, :dest, :filter_cache, :includes_load_paths, + :liquid_renderer, :profiler, :regenerator, :source + + # Public: Initialize a new Site. + # + # config - A Hash containing site configuration details. + def initialize(config) + # Source and destination may not be changed after the site has been created. + @source = File.expand_path(config["source"]).freeze + @dest = File.expand_path(config["destination"]).freeze + + self.config = config + + @cache_dir = in_source_dir(config["cache_dir"]) + @filter_cache = {} + + @reader = Reader.new(self) + @profiler = Profiler.new(self) + @regenerator = Regenerator.new(self) + @liquid_renderer = LiquidRenderer.new(self) + + Jekyll.sites << self + + reset + setup + + Jekyll::Hooks.trigger :site, :after_init, self + end + + # Public: Set the site's configuration. This handles side-effects caused by + # changing values in the configuration. + # + # config - a Jekyll::Configuration, containing the new configuration. + # + # Returns the new configuration. + def config=(config) + @config = config.clone + + %w(safe lsi highlighter baseurl exclude include future unpublished + show_drafts limit_posts keep_files).each do |opt| + send("#{opt}=", config[opt]) + end + + # keep using `gems` to avoid breaking change + self.gems = config["plugins"] + + configure_cache + configure_plugins + configure_theme + configure_include_paths + configure_file_read_opts + + self.permalink_style = config["permalink"].to_sym + + # Read in a _config.yml from the current theme-gem at the very end. + @config = load_theme_configuration(config) if theme + @config + end + + # Public: Read, process, and write this Site to output. + # + # Returns nothing. + def process + return profiler.profile_process if config["profile"] + + reset + read + generate + render + cleanup + write + end + + def print_stats + Jekyll.logger.info @liquid_renderer.stats_table + end + + # rubocop:disable Metrics/AbcSize + # rubocop:disable Metrics/MethodLength + # + # Reset Site details. + # + # Returns nothing + def reset + self.time = if config["time"] + Utils.parse_date(config["time"].to_s, "Invalid time in _config.yml.") + else + Time.now + end + self.layouts = {} + self.inclusions = {} + self.pages = [] + self.static_files = [] + self.data = {} + @post_attr_hash = {} + @site_data = nil + @collections = nil + @documents = nil + @docs_to_write = nil + @regenerator.clear_cache + @liquid_renderer.reset + @site_cleaner = nil + frontmatter_defaults.reset + + raise ArgumentError, "limit_posts must be a non-negative number" if limit_posts.negative? + + Jekyll::Cache.clear_if_config_changed config + Jekyll::Hooks.trigger :site, :after_reset, self + nil + end + # rubocop:enable Metrics/MethodLength + # rubocop:enable Metrics/AbcSize + + # Load necessary libraries, plugins, converters, and generators. + # + # Returns nothing. + def setup + ensure_not_in_dest + + plugin_manager.conscientious_require + + self.converters = instantiate_subclasses(Jekyll::Converter) + self.generators = instantiate_subclasses(Jekyll::Generator) + end + + # Check that the destination dir isn't the source dir or a directory + # parent to the source dir. + def ensure_not_in_dest + dest_pathname = Pathname.new(dest) + Pathname.new(source).ascend do |path| + if path == dest_pathname + raise Errors::FatalException, + "Destination directory cannot be or contain the Source directory." + end + end + end + + # The list of collections and their corresponding Jekyll::Collection instances. + # If config['collections'] is set, a new instance is created + # for each item in the collection, a new hash is returned otherwise. + # + # Returns a Hash containing collection name-to-instance pairs. + def collections + @collections ||= collection_names.each_with_object({}) do |name, hsh| + hsh[name] = Jekyll::Collection.new(self, name) + end + end + + # The list of collection names. + # + # Returns an array of collection names from the configuration, + # or an empty array if the `collections` key is not set. + def collection_names + case config["collections"] + when Hash + config["collections"].keys + when Array + config["collections"] + when nil + [] + else + raise ArgumentError, "Your `collections` key must be a hash or an array." + end + end + + # Read Site data from disk and load it into internal data structures. + # + # Returns nothing. + def read + reader.read + limit_posts! + Jekyll::Hooks.trigger :site, :post_read, self + nil + end + + # Run each of the Generators. + # + # Returns nothing. + def generate + generators.each do |generator| + start = Time.now + generator.generate(self) + Jekyll.logger.debug "Generating:", + "#{generator.class} finished in #{Time.now - start} seconds." + end + nil + end + + # Render the site to the destination. + # + # Returns nothing. + def render + relative_permalinks_are_deprecated + + payload = site_payload + + Jekyll::Hooks.trigger :site, :pre_render, self, payload + + render_docs(payload) + render_pages(payload) + + Jekyll::Hooks.trigger :site, :post_render, self, payload + nil + end + + # Remove orphaned files and empty directories in destination. + # + # Returns nothing. + def cleanup + site_cleaner.cleanup! + nil + end + + # Write static files, pages, and posts. + # + # Returns nothing. + def write + Jekyll::Commands::Doctor.conflicting_urls(self) + each_site_file do |item| + item.write(dest) if regenerator.regenerate?(item) + end + regenerator.write_metadata + Jekyll::Hooks.trigger :site, :post_write, self + nil + end + + def posts + collections["posts"] ||= Collection.new(self, "posts") + end + + # Construct a Hash of Posts indexed by the specified Post attribute. + # + # post_attr - The String name of the Post attribute. + # + # Examples + # + # post_attr_hash('categories') + # # => { 'tech' => [<Post A>, <Post B>], + # # 'ruby' => [<Post B>] } + # + # Returns the Hash: { attr => posts } where + # attr - One of the values for the requested attribute. + # posts - The Array of Posts with the given attr value. + def post_attr_hash(post_attr) + # Build a hash map based on the specified post attribute ( post attr => + # array of posts ) then sort each array in reverse order. + @post_attr_hash[post_attr] ||= begin + hash = Hash.new { |h, key| h[key] = [] } + posts.docs.each do |p| + p.data[post_attr]&.each { |t| hash[t] << p } + end + hash.each_value { |posts| posts.sort!.reverse! } + hash + end + end + + def tags + post_attr_hash("tags") + end + + def categories + post_attr_hash("categories") + end + + # Prepare site data for site payload. The method maintains backward compatibility + # if the key 'data' is already used in _config.yml. + # + # Returns the Hash to be hooked to site.data. + def site_data + @site_data ||= (config["data"] || data) + end + + # The Hash payload containing site-wide data. + # + # Returns the Hash: { "site" => data } where data is a Hash with keys: + # "time" - The Time as specified in the configuration or the + # current time if none was specified. + # "posts" - The Array of Posts, sorted chronologically by post date + # and then title. + # "pages" - The Array of all Pages. + # "html_pages" - The Array of HTML Pages. + # "categories" - The Hash of category values and Posts. + # See Site#post_attr_hash for type info. + # "tags" - The Hash of tag values and Posts. + # See Site#post_attr_hash for type info. + def site_payload + Drops::UnifiedPayloadDrop.new self + end + alias_method :to_liquid, :site_payload + + # Get the implementation class for the given Converter. + # Returns the Converter instance implementing the given Converter. + # klass - The Class of the Converter to fetch. + def find_converter_instance(klass) + @find_converter_instance ||= {} + @find_converter_instance[klass] ||= converters.find do |converter| + converter.instance_of?(klass) + end || \ + raise("No Converters found for #{klass}") + end + + # klass - class or module containing the subclasses. + # Returns array of instances of subclasses of parameter. + # Create array of instances of the subclasses of the class or module + # passed in as argument. + + def instantiate_subclasses(klass) + klass.descendants.select { |c| !safe || c.safe }.tap do |result| + result.sort! + result.map! { |c| c.new(config) } + end + end + + # Warns the user if permanent links are relative to the parent + # directory. As this is a deprecated function of Jekyll. + # + # Returns + def relative_permalinks_are_deprecated + if config["relative_permalinks"] + Jekyll.logger.abort_with "Since v3.0, permalinks for pages " \ + "in subfolders must be relative to the " \ + "site source directory, not the parent " \ + "directory. Check https://jekyllrb.com/docs/upgrading/ " \ + "for more info." + end + end + + # Get the to be written documents + # + # Returns an Array of Documents which should be written + def docs_to_write + documents.select(&:write?) + end + + # Get the to be written static files + # + # Returns an Array of StaticFiles which should be written + def static_files_to_write + static_files.select(&:write?) + end + + # Get all the documents + # + # Returns an Array of all Documents + def documents + collections.each_with_object(Set.new) do |(_, collection), set| + set.merge(collection.docs).merge(collection.files) + end.to_a + end + + def each_site_file + seen_files = [] + %w(pages static_files_to_write docs_to_write).each do |type| + send(type).each do |item| + next if seen_files.include?(item) + + yield item + seen_files << item + end + end + end + + # Returns the FrontmatterDefaults or creates a new FrontmatterDefaults + # if it doesn't already exist. + # + # Returns The FrontmatterDefaults + def frontmatter_defaults + @frontmatter_defaults ||= FrontmatterDefaults.new(self) + end + + # Whether to perform a full rebuild without incremental regeneration + # + # Returns a Boolean: true for a full rebuild, false for normal build + def incremental?(override = {}) + override["incremental"] || config["incremental"] + end + + # Returns the publisher or creates a new publisher if it doesn't + # already exist. + # + # Returns The Publisher + def publisher + @publisher ||= Publisher.new(self) + end + + # Public: Prefix a given path with the source directory. + # + # paths - (optional) path elements to a file or directory within the + # source directory + # + # Returns a path which is prefixed with the source directory. + def in_source_dir(*paths) + paths.reduce(source) do |base, path| + Jekyll.sanitized_path(base, path) + end + end + + # Public: Prefix a given path with the theme directory. + # + # paths - (optional) path elements to a file or directory within the + # theme directory + # + # Returns a path which is prefixed with the theme root directory. + def in_theme_dir(*paths) + return nil unless theme + + paths.reduce(theme.root) do |base, path| + Jekyll.sanitized_path(base, path) + end + end + + # Public: Prefix a given path with the destination directory. + # + # paths - (optional) path elements to a file or directory within the + # destination directory + # + # Returns a path which is prefixed with the destination directory. + def in_dest_dir(*paths) + paths.reduce(dest) do |base, path| + Jekyll.sanitized_path(base, path) + end + end + + # Public: Prefix a given path with the cache directory. + # + # paths - (optional) path elements to a file or directory within the + # cache directory + # + # Returns a path which is prefixed with the cache directory. + def in_cache_dir(*paths) + paths.reduce(cache_dir) do |base, path| + Jekyll.sanitized_path(base, path) + end + end + + # Public: The full path to the directory that houses all the collections registered + # with the current site. + # + # Returns the source directory or the absolute path to the custom collections_dir + def collections_path + dir_str = config["collections_dir"] + @collections_path ||= dir_str.empty? ? source : in_source_dir(dir_str) + end + + # Public + # + # Returns the object as a debug String. + def inspect + "#<#{self.class} @source=#{@source}>" + end + + private + + def load_theme_configuration(config) + return config if config["ignore_theme_config"] == true + + theme_config_file = in_theme_dir("_config.yml") + return config unless File.exist?(theme_config_file) + + # Bail out if the theme_config_file is a symlink file irrespective of safe mode + return config if File.symlink?(theme_config_file) + + theme_config = SafeYAML.load_file(theme_config_file) + return config unless theme_config.is_a?(Hash) + + Jekyll.logger.info "Theme Config file:", theme_config_file + + # theme_config should not be overriding Jekyll's defaults + theme_config.delete_if { |key, _| Configuration::DEFAULTS.key?(key) } + + # Override theme_config with existing config and return the result. + # Additionally ensure we return a `Jekyll::Configuration` instance instead of a Hash. + Utils.deep_merge_hashes(theme_config, config) + .each_with_object(Jekyll::Configuration.new) do |(key, value), conf| + conf[key] = value + end + end + + # Limits the current posts; removes the posts which exceed the limit_posts + # + # Returns nothing + def limit_posts! + if limit_posts.positive? + limit = posts.docs.length < limit_posts ? posts.docs.length : limit_posts + posts.docs = posts.docs[-limit, limit] + end + end + + # Returns the Cleaner or creates a new Cleaner if it doesn't + # already exist. + # + # Returns The Cleaner + def site_cleaner + @site_cleaner ||= Cleaner.new(self) + end + + def hide_cache_dir_from_git + @cache_gitignore_path ||= in_source_dir(config["cache_dir"], ".gitignore") + return if File.exist?(@cache_gitignore_path) + + cache_dir_path = in_source_dir(config["cache_dir"]) + FileUtils.mkdir_p(cache_dir_path) unless File.directory?(cache_dir_path) + + File.open(@cache_gitignore_path, "wb") do |file| + file.puts("# ignore everything in this directory\n*") + end + end + + # Disable Marshaling cache to disk in Safe Mode + def configure_cache + Jekyll::Cache.cache_dir = in_source_dir(config["cache_dir"], "Jekyll/Cache") + if safe || config["disable_disk_cache"] + Jekyll::Cache.disable_disk_cache! + else + hide_cache_dir_from_git + end + end + + def configure_plugins + self.plugin_manager = Jekyll::PluginManager.new(self) + self.plugins = plugin_manager.plugins_path + end + + def configure_theme + self.theme = nil + return if config["theme"].nil? + + self.theme = + if config["theme"].is_a?(String) + Jekyll::Theme.new(config["theme"]) + else + Jekyll.logger.warn "Theme:", "value of 'theme' in config should be String to use " \ + "gem-based themes, but got #{config["theme"].class}" + nil + end + end + + def configure_include_paths + @includes_load_paths = Array(in_source_dir(config["includes_dir"].to_s)) + @includes_load_paths << theme.includes_path if theme&.includes_path + end + + def configure_file_read_opts + self.file_read_opts = {} + file_read_opts[:encoding] = config["encoding"] if config["encoding"] + self.file_read_opts = Jekyll::Utils.merged_file_read_opts(self, {}) + end + + def render_docs(payload) + collections.each_value do |collection| + collection.docs.each do |document| + render_regenerated(document, payload) + end + end + end + + def render_pages(payload) + pages.each do |page| + render_regenerated(page, payload) + end + end + + def render_regenerated(document, payload) + return unless regenerator.regenerate?(document) + + document.renderer.payload = payload + document.output = document.renderer.run + document.trigger_hooks(:post_render) + end + end +end diff --git a/lib/jekyll/static_file.rb b/lib/jekyll/static_file.rb new file mode 100644 index 0000000..f7d99f6 --- /dev/null +++ b/lib/jekyll/static_file.rb @@ -0,0 +1,205 @@ +# frozen_string_literal: true + +module Jekyll + class StaticFile + extend Forwardable + + attr_reader :relative_path, :extname, :name + + def_delegator :to_liquid, :to_json, :to_json + + class << self + # The cache of last modification times [path] -> mtime. + def mtimes + @mtimes ||= {} + end + + def reset_cache + @mtimes = nil + end + end + + # Initialize a new StaticFile. + # + # site - The Site. + # base - The String path to the <source>. + # dir - The String path between <source> and the file. + # name - The String filename of the file. + # rubocop: disable Metrics/ParameterLists + def initialize(site, base, dir, name, collection = nil) + @site = site + @base = base + @dir = dir + @name = name + @collection = collection + @relative_path = File.join(*[@dir, @name].compact) + @extname = File.extname(@name) + end + # rubocop: enable Metrics/ParameterLists + + # Returns source file path. + def path + @path ||= if !@collection.nil? && !@site.config["collections_dir"].empty? + File.join(*[@base, @site.config["collections_dir"], @dir, @name].compact) + else + File.join(*[@base, @dir, @name].compact) + end + end + + # Obtain destination path. + # + # dest - The String path to the destination dir. + # + # Returns destination file path. + def destination(dest) + @destination ||= {} + @destination[dest] ||= @site.in_dest_dir(dest, Jekyll::URL.unescape_path(url)) + end + + def destination_rel_dir + if @collection + File.dirname(url) + else + @dir + end + end + + def modified_time + @modified_time ||= File.stat(path).mtime + end + + # Returns last modification time for this file. + def mtime + modified_time.to_i + end + + # Is source path modified? + # + # Returns true if modified since last write. + def modified? + self.class.mtimes[path] != mtime + end + + # Whether to write the file to the filesystem + # + # Returns true unless the defaults for the destination path from + # _config.yml contain `published: false`. + def write? + publishable = defaults.fetch("published", true) + return publishable unless @collection + + publishable && @collection.write? + end + + # Write the static file to the destination directory (if modified). + # + # dest - The String path to the destination dir. + # + # Returns false if the file was not modified since last time (no-op). + def write(dest) + dest_path = destination(dest) + return false if File.exist?(dest_path) && !modified? + + self.class.mtimes[path] = mtime + + FileUtils.mkdir_p(File.dirname(dest_path)) + FileUtils.rm(dest_path) if File.exist?(dest_path) + copy_file(dest_path) + + true + end + + def data + @data ||= @site.frontmatter_defaults.all(relative_path, type) + end + + def to_liquid + @to_liquid ||= Drops::StaticFileDrop.new(self) + end + + # Generate "basename without extension" and strip away any trailing periods. + # NOTE: `String#gsub` removes all trailing periods (in comparison to `String#chomp`) + def basename + @basename ||= File.basename(name, extname).gsub(%r!\.*\z!, "") + end + + def placeholders + { + :collection => @collection.label, + :path => cleaned_relative_path, + :output_ext => "", + :name => basename, + :title => "", + } + end + + # Similar to Jekyll::Document#cleaned_relative_path. + # Generates a relative path with the collection's directory removed when applicable + # and additionally removes any multiple periods in the string. + # + # NOTE: `String#gsub!` removes all trailing periods (in comparison to `String#chomp!`) + # + # Examples: + # When `relative_path` is "_methods/site/my-cool-avatar...png": + # cleaned_relative_path + # # => "/site/my-cool-avatar" + # + # Returns the cleaned relative path of the static file. + def cleaned_relative_path + @cleaned_relative_path ||= begin + cleaned = relative_path[0..-extname.length - 1] + cleaned.gsub!(%r!\.*\z!, "") + cleaned.sub!(@collection.relative_directory, "") if @collection + cleaned + end + end + + # Applies a similar URL-building technique as Jekyll::Document that takes + # the collection's URL template into account. The default URL template can + # be overridden in the collection's configuration in _config.yml. + def url + @url ||= begin + base = if @collection.nil? + cleaned_relative_path + else + Jekyll::URL.new( + :template => @collection.url_template, + :placeholders => placeholders + ) + end.to_s.chomp("/") + base << extname + end + end + + # Returns the type of the collection if present, nil otherwise. + def type + @type ||= @collection.nil? ? nil : @collection.label.to_sym + end + + # Returns the front matter defaults defined for the file's URL and/or type + # as defined in _config.yml. + def defaults + @defaults ||= @site.frontmatter_defaults.all url, type + end + + # Returns a debug string on inspecting the static file. + # Includes only the relative path of the object. + def inspect + "#<#{self.class} @relative_path=#{relative_path.inspect}>" + end + + private + + def copy_file(dest_path) + if @site.safe || Jekyll.env == "production" + FileUtils.cp(path, dest_path) + else + FileUtils.copy_entry(path, dest_path) + end + + unless File.symlink?(dest_path) + File.utime(self.class.mtimes[path], self.class.mtimes[path], dest_path) + end + end + end +end diff --git a/lib/jekyll/stevenson.rb b/lib/jekyll/stevenson.rb new file mode 100644 index 0000000..4cfc7fb --- /dev/null +++ b/lib/jekyll/stevenson.rb @@ -0,0 +1,60 @@ +# frozen_string_literal: true + +module Jekyll + class Stevenson < ::Logger + def initialize + @progname = nil + @level = DEBUG + @default_formatter = Formatter.new + @logdev = $stdout + @formatter = proc do |_, _, _, msg| + msg.to_s + end + end + + def add(severity, message = nil, progname = nil) + severity ||= UNKNOWN + @logdev = logdevice(severity) + + return true if @logdev.nil? || severity < @level + + progname ||= @progname + if message.nil? + if block_given? + message = yield + else + message = progname + progname = @progname + end + end + @logdev.puts( + format_message(format_severity(severity), Time.now, progname, message) + ) + true + end + + # Log a +WARN+ message + def warn(progname = nil, &block) + add(WARN, nil, progname.yellow, &block) + end + + # Log an +ERROR+ message + def error(progname = nil, &block) + add(ERROR, nil, progname.red, &block) + end + + def close + # No LogDevice in use + end + + private + + def logdevice(severity) + if severity > INFO + $stderr + else + $stdout + end + end + end +end diff --git a/lib/jekyll/tags/highlight.rb b/lib/jekyll/tags/highlight.rb new file mode 100644 index 0000000..806665e --- /dev/null +++ b/lib/jekyll/tags/highlight.rb @@ -0,0 +1,114 @@ +# frozen_string_literal: true + +module Jekyll + module Tags + class HighlightBlock < Liquid::Block + include Liquid::StandardFilters + + # The regular expression syntax checker. Start with the language specifier. + # Follow that by zero or more space separated options that take one of three + # forms: name, name=value, or name="<quoted list>" + # + # <quoted list> is a space-separated list of numbers + SYNTAX = %r!^([a-zA-Z0-9.+#_-]+)((\s+\w+(=(\w+|"([0-9]+\s)*[0-9]+"))?)*)$!.freeze + + def initialize(tag_name, markup, tokens) + super + if markup.strip =~ SYNTAX + @lang = Regexp.last_match(1).downcase + @highlight_options = parse_options(Regexp.last_match(2)) + else + raise SyntaxError, <<~MSG + Syntax Error in tag 'highlight' while parsing the following markup: + + #{markup} + + Valid syntax: highlight <lang> [linenos] + MSG + end + end + + LEADING_OR_TRAILING_LINE_TERMINATORS = %r!\A(\n|\r)+|(\n|\r)+\z!.freeze + + def render(context) + prefix = context["highlighter_prefix"] || "" + suffix = context["highlighter_suffix"] || "" + code = super.to_s.gsub(LEADING_OR_TRAILING_LINE_TERMINATORS, "") + + output = + case context.registers[:site].highlighter + when "rouge" + render_rouge(code) + when "pygments" + render_pygments(code, context) + else + render_codehighlighter(code) + end + + rendered_output = add_code_tag(output) + prefix + rendered_output + suffix + end + + private + + OPTIONS_REGEX = %r!(?:\w="[^"]*"|\w=\w|\w)+!.freeze + + def parse_options(input) + options = {} + return options if input.empty? + + # Split along 3 possible forms -- key="<quoted list>", key=value, or key + input.scan(OPTIONS_REGEX) do |opt| + key, value = opt.split("=") + # If a quoted list, convert to array + if value&.include?('"') + value.delete!('"') + value = value.split + end + options[key.to_sym] = value || true + end + + options[:linenos] = "inline" if options[:linenos] == true + options + end + + def render_pygments(code, _context) + Jekyll.logger.warn "Warning:", "Highlight Tag no longer supports rendering with Pygments." + Jekyll.logger.warn "", "Using the default highlighter, Rouge, instead." + render_rouge(code) + end + + def render_rouge(code) + require "rouge" + formatter = ::Rouge::Formatters::HTML.new + if @highlight_options[:linenos] + formatter = ::Rouge::Formatters::HTMLTable.new( + formatter, + { + :css_class => "highlight", + :gutter_class => "gutter", + :code_class => "code", + } + ) + end + lexer = ::Rouge::Lexer.find_fancy(@lang, code) || Rouge::Lexers::PlainText + formatter.format(lexer.lex(code)) + end + + def render_codehighlighter(code) + h(code).strip + end + + def add_code_tag(code) + code_attributes = [ + "class=\"language-#{@lang.to_s.tr("+", "-")}\"", + "data-lang=\"#{@lang}\"", + ].join(" ") + "<figure class=\"highlight\"><pre><code #{code_attributes}>" \ + "#{code.chomp}</code></pre></figure>" + end + end + end +end + +Liquid::Template.register_tag("highlight", Jekyll::Tags::HighlightBlock) diff --git a/lib/jekyll/tags/include.rb b/lib/jekyll/tags/include.rb new file mode 100644 index 0000000..6960952 --- /dev/null +++ b/lib/jekyll/tags/include.rb @@ -0,0 +1,275 @@ +# frozen_string_literal: true + +module Jekyll + module Tags + class IncludeTag < Liquid::Tag + VALID_SYNTAX = %r! + ([\w-]+)\s*=\s* + (?:"([^"\\]*(?:\\.[^"\\]*)*)"|'([^'\\]*(?:\\.[^'\\]*)*)'|([\w.-]+)) + !x.freeze + VARIABLE_SYNTAX = %r! + (?<variable>[^{]*(\{\{\s*[\w\-.]+\s*(\|.*)?\}\}[^\s{}]*)+) + (?<params>.*) + !mx.freeze + + FULL_VALID_SYNTAX = %r!\A\s*(?:#{VALID_SYNTAX}(?=\s|\z)\s*)*\z!.freeze + VALID_FILENAME_CHARS = %r!^[\w/.\-()+~\#@]+$!.freeze + INVALID_SEQUENCES = %r![./]{2,}!.freeze + + def initialize(tag_name, markup, tokens) + super + markup = markup.strip + matched = markup.match(VARIABLE_SYNTAX) + if matched + @file = matched["variable"].strip + @params = matched["params"].strip + else + @file, @params = markup.split(%r!\s+!, 2) + end + validate_params if @params + @tag_name = tag_name + end + + def syntax_example + "{% #{@tag_name} file.ext param='value' param2='value' %}" + end + + def parse_params(context) + params = {} + @params.scan(VALID_SYNTAX) do |key, d_quoted, s_quoted, variable| + value = if d_quoted + d_quoted.include?('\\"') ? d_quoted.gsub('\\"', '"') : d_quoted + elsif s_quoted + s_quoted.include?("\\'") ? s_quoted.gsub("\\'", "'") : s_quoted + elsif variable + context[variable] + end + + params[key] = value + end + params + end + + def validate_file_name(file) + if INVALID_SEQUENCES.match?(file) || !VALID_FILENAME_CHARS.match?(file) + raise ArgumentError, <<~MSG + Invalid syntax for include tag. File contains invalid characters or sequences: + + #{file} + + Valid syntax: + + #{syntax_example} + + MSG + end + end + + def validate_params + unless FULL_VALID_SYNTAX.match?(@params) + raise ArgumentError, <<~MSG + Invalid syntax for include tag: + + #{@params} + + Valid syntax: + + #{syntax_example} + + MSG + end + end + + # Grab file read opts in the context + def file_read_opts(context) + context.registers[:site].file_read_opts + end + + # Render the variable if required + def render_variable(context) + Liquid::Template.parse(@file).render(context) if VARIABLE_SYNTAX.match?(@file) + end + + def tag_includes_dirs(context) + context.registers[:site].includes_load_paths.freeze + end + + def locate_include_file(context, file, safe) + includes_dirs = tag_includes_dirs(context) + includes_dirs.each do |dir| + path = PathManager.join(dir, file) + return path if valid_include_file?(path, dir.to_s, safe) + end + raise IOError, could_not_locate_message(file, includes_dirs, safe) + end + + def render(context) + site = context.registers[:site] + + file = render_variable(context) || @file + validate_file_name(file) + + path = locate_include_file(context, file, site.safe) + return unless path + + add_include_to_dependency(site, path, context) + + partial = load_cached_partial(path, context) + + context.stack do + context["include"] = parse_params(context) if @params + begin + partial.render!(context) + rescue Liquid::Error => e + e.template_name = path + e.markup_context = "included " if e.markup_context.nil? + raise e + end + end + end + + def add_include_to_dependency(site, path, context) + if context.registers[:page]&.key?("path") + site.regenerator.add_dependency( + site.in_source_dir(context.registers[:page]["path"]), + path + ) + end + end + + def load_cached_partial(path, context) + context.registers[:cached_partials] ||= {} + cached_partial = context.registers[:cached_partials] + + if cached_partial.key?(path) + cached_partial[path] + else + unparsed_file = context.registers[:site] + .liquid_renderer + .file(path) + begin + cached_partial[path] = unparsed_file.parse(read_file(path, context)) + rescue Liquid::Error => e + e.template_name = path + e.markup_context = "included " if e.markup_context.nil? + raise e + end + end + end + + def valid_include_file?(path, dir, safe) + !outside_site_source?(path, dir, safe) && File.file?(path) + end + + def outside_site_source?(path, dir, safe) + safe && !realpath_prefixed_with?(path, dir) + end + + def realpath_prefixed_with?(path, dir) + File.exist?(path) && File.realpath(path).start_with?(dir) + rescue StandardError + false + end + + # This method allows to modify the file content by inheriting from the class. + def read_file(file, context) + File.read(file, **file_read_opts(context)) + end + + private + + def could_not_locate_message(file, includes_dirs, safe) + message = "Could not locate the included file '#{file}' in any of #{includes_dirs}. " \ + "Ensure it exists in one of those directories and" + message + if safe + " is not a symlink as those are not allowed in safe mode." + else + ", if it is a symlink, does not point outside your site source." + end + end + end + + # Do not inherit from this class. + # TODO: Merge into the `Jekyll::Tags::IncludeTag` in v5.0 + class OptimizedIncludeTag < IncludeTag + def render(context) + @site ||= context.registers[:site] + + file = render_variable(context) || @file + validate_file_name(file) + + @site.inclusions[file] ||= locate_include_file(file) + inclusion = @site.inclusions[file] + + add_include_to_dependency(inclusion, context) if @site.config["incremental"] + + context.stack do + context["include"] = parse_params(context) if @params + inclusion.render(context) + end + end + + private + + def locate_include_file(file) + @site.includes_load_paths.each do |dir| + path = PathManager.join(dir, file) + return Inclusion.new(@site, dir, file) if valid_include_file?(path, dir) + end + raise IOError, could_not_locate_message(file, @site.includes_load_paths, @site.safe) + end + + def valid_include_file?(path, dir) + File.file?(path) && !outside_scope?(path, dir) + end + + def outside_scope?(path, dir) + @site.safe && !realpath_prefixed_with?(path, dir) + end + + def realpath_prefixed_with?(path, dir) + File.realpath(path).start_with?(dir) + rescue StandardError + false + end + + def add_include_to_dependency(inclusion, context) + page = context.registers[:page] + return unless page&.key?("path") + + absolute_path = \ + if page["collection"] + @site.in_source_dir(@site.config["collections_dir"], page["path"]) + else + @site.in_source_dir(page["path"]) + end + + @site.regenerator.add_dependency(absolute_path, inclusion.path) + end + end + + class IncludeRelativeTag < IncludeTag + def tag_includes_dirs(context) + Array(page_path(context)).freeze + end + + def page_path(context) + page, site = context.registers.values_at(:page, :site) + return site.source unless page + + site.in_source_dir File.dirname(resource_path(page, site)) + end + + private + + def resource_path(page, site) + path = page["path"] + path = File.join(site.config["collections_dir"], path) if page["collection"] + path.delete_suffix("/#excerpt") + end + end + end +end + +Liquid::Template.register_tag("include", Jekyll::Tags::OptimizedIncludeTag) +Liquid::Template.register_tag("include_relative", Jekyll::Tags::IncludeRelativeTag) diff --git a/lib/jekyll/tags/link.rb b/lib/jekyll/tags/link.rb new file mode 100644 index 0000000..c986b5a --- /dev/null +++ b/lib/jekyll/tags/link.rb @@ -0,0 +1,42 @@ +# frozen_string_literal: true + +module Jekyll + module Tags + class Link < Liquid::Tag + include Jekyll::Filters::URLFilters + + class << self + def tag_name + name.split("::").last.downcase + end + end + + def initialize(tag_name, relative_path, tokens) + super + + @relative_path = relative_path.strip + end + + def render(context) + @context = context + site = context.registers[:site] + relative_path = Liquid::Template.parse(@relative_path).render(context) + relative_path_with_leading_slash = PathManager.join("", relative_path) + + site.each_site_file do |item| + return relative_url(item) if item.relative_path == relative_path + # This takes care of the case for static files that have a leading / + return relative_url(item) if item.relative_path == relative_path_with_leading_slash + end + + raise ArgumentError, <<~MSG + Could not find document '#{relative_path}' in tag '#{self.class.tag_name}'. + + Make sure the document exists and the path is correct. + MSG + end + end + end +end + +Liquid::Template.register_tag(Jekyll::Tags::Link.tag_name, Jekyll::Tags::Link) diff --git a/lib/jekyll/tags/post_url.rb b/lib/jekyll/tags/post_url.rb new file mode 100644 index 0000000..7774d37 --- /dev/null +++ b/lib/jekyll/tags/post_url.rb @@ -0,0 +1,106 @@ +# frozen_string_literal: true + +module Jekyll + module Tags + class PostComparer + MATCHER = %r!^(.+/)*(\d+-\d+-\d+)-(.*)$!.freeze + + attr_reader :path, :date, :slug, :name + + def initialize(name) + @name = name + + all, @path, @date, @slug = *name.sub(%r!^/!, "").match(MATCHER) + unless all + raise Jekyll::Errors::InvalidPostNameError, + "'#{name}' does not contain valid date and/or title." + end + + basename_pattern = "#{date}-#{Regexp.escape(slug)}\\.[^.]+" + @name_regex = %r!^_posts/#{path}#{basename_pattern}|^#{path}_posts/?#{basename_pattern}! + end + + def post_date + @post_date ||= Utils.parse_date( + date, + "'#{date}' does not contain valid date and/or title." + ) + end + + def ==(other) + other.relative_path.match(@name_regex) + end + + def deprecated_equality(other) + slug == post_slug(other) && + post_date.year == other.date.year && + post_date.month == other.date.month && + post_date.day == other.date.day + end + + private + + # Construct the directory-aware post slug for a Jekyll::Post + # + # other - the Jekyll::Post + # + # Returns the post slug with the subdirectory (relative to _posts) + def post_slug(other) + path = other.basename.split("/")[0...-1].join("/") + if path.nil? || path == "" + other.data["slug"] + else + "#{path}/#{other.data["slug"]}" + end + end + end + + class PostUrl < Liquid::Tag + include Jekyll::Filters::URLFilters + + def initialize(tag_name, post, tokens) + super + @orig_post = post.strip + begin + @post = PostComparer.new(@orig_post) + rescue StandardError => e + raise Jekyll::Errors::PostURLError, <<~MSG + Could not parse name of post "#{@orig_post}" in tag 'post_url'. + Make sure the post exists and the name is correct. + #{e.class}: #{e.message} + MSG + end + end + + def render(context) + @context = context + site = context.registers[:site] + + site.posts.docs.each do |document| + return relative_url(document) if @post == document + end + + # New matching method did not match, fall back to old method + # with deprecation warning if this matches + + site.posts.docs.each do |document| + next unless @post.deprecated_equality document + + Jekyll::Deprecator.deprecation_message( + "A call to '{% post_url #{@post.name} %}' did not match a post using the new " \ + "matching method of checking name (path-date-slug) equality. Please make sure " \ + "that you change this tag to match the post's name exactly." + ) + return relative_url(document) + end + + raise Jekyll::Errors::PostURLError, <<~MSG + Could not find post "#{@orig_post}" in tag 'post_url'. + Make sure the post exists and the name is correct. + MSG + end + end + end +end + +Liquid::Template.register_tag("post_url", Jekyll::Tags::PostUrl) diff --git a/lib/jekyll/theme.rb b/lib/jekyll/theme.rb new file mode 100644 index 0000000..a3f2a94 --- /dev/null +++ b/lib/jekyll/theme.rb @@ -0,0 +1,90 @@ +# frozen_string_literal: true + +module Jekyll + class Theme + extend Forwardable + attr_reader :name + + def_delegator :gemspec, :version, :version + + def initialize(name) + @name = name.downcase.strip + Jekyll.logger.debug "Theme:", name + Jekyll.logger.debug "Theme source:", root + end + + def root + # Must use File.realpath to resolve symlinks created by rbenv + # Otherwise, Jekyll.sanitized path with prepend the unresolved root + @root ||= File.realpath(gemspec.full_gem_path) + rescue Errno::ENOENT, Errno::EACCES, Errno::ELOOP + raise "Path #{gemspec.full_gem_path} does not exist, is not accessible or includes " \ + "a symbolic link loop" + end + + # The name of theme directory + def basename + @basename ||= File.basename(root) + end + + def includes_path + @includes_path ||= path_for "_includes" + end + + def layouts_path + @layouts_path ||= path_for "_layouts" + end + + def sass_path + @sass_path ||= path_for "_sass" + end + + def assets_path + @assets_path ||= path_for "assets" + end + + def data_path + @data_path ||= path_for "_data" + end + + def runtime_dependencies + gemspec.runtime_dependencies + end + + private + + def path_for(folder) + path = realpath_for(folder) + path if path && File.directory?(path) + end + + def realpath_for(folder) + # This resolves all symlinks for the theme subfolder and then ensures that the directory + # remains inside the theme root. This prevents the use of symlinks for theme subfolders to + # escape the theme root. + # However, symlinks are allowed to point to other directories within the theme. + Jekyll.sanitized_path(root, File.realpath(Jekyll.sanitized_path(root, folder.to_s))) + rescue Errno::ENOENT, Errno::EACCES, Errno::ELOOP => e + log_realpath_exception(e, folder) + nil + end + + def log_realpath_exception(err, folder) + return if err.is_a?(Errno::ENOENT) + + case err + when Errno::EACCES + Jekyll.logger.error "Theme error:", "Directory '#{folder}' is not accessible." + when Errno::ELOOP + Jekyll.logger.error "Theme error:", "Directory '#{folder}' includes a symbolic link loop." + end + end + + def gemspec + @gemspec ||= Gem::Specification.find_by_name(name) + rescue Gem::LoadError + raise Jekyll::Errors::MissingDependencyException, + "The #{name} theme could not be found." + end + end +end diff --git a/lib/jekyll/theme_builder.rb b/lib/jekyll/theme_builder.rb new file mode 100644 index 0000000..b03abbf --- /dev/null +++ b/lib/jekyll/theme_builder.rb @@ -0,0 +1,121 @@ +# frozen_string_literal: true + +module Jekyll + class ThemeBuilder + SCAFFOLD_DIRECTORIES = %w( + assets _data _layouts _includes _sass + ).freeze + + attr_reader :name, :path, :code_of_conduct + + def initialize(theme_name, opts) + @name = theme_name.to_s.tr(" ", "_").squeeze("_") + @path = Pathname.new(File.expand_path(name, Dir.pwd)) + @code_of_conduct = !!opts["code_of_conduct"] + end + + def create! + create_directories + create_starter_files + create_gemspec + create_accessories + 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 + @root ||= Pathname.new(File.expand_path("../", __dir__)) + end + + def template_file(filename) + [ + root.join("theme_template", "#{filename}.erb"), + root.join("theme_template", filename.to_s), + ].find(&:exist?) + end + + def template(filename) + erb.render(template_file(filename).read) + end + + def erb + @erb ||= ERBRenderer.new(self) + end + + def mkdir_p(directories) + Array(directories).each do |directory| + full_path = path.join(directory) + Jekyll.logger.info "create", full_path.to_s + FileUtils.mkdir_p(full_path) + end + end + + def write_file(filename, contents) + full_path = path.join(filename) + Jekyll.logger.info "create", full_path.to_s + File.write(full_path, contents) + end + + def create_directories + mkdir_p(SCAFFOLD_DIRECTORIES) + end + + def create_starter_files + %w(page post default).each do |layout| + write_file("_layouts/#{layout}.html", template("_layouts/#{layout}.html")) + end + end + + def create_gemspec + write_file("Gemfile", template("Gemfile")) + write_file("#{name}.gemspec", template("theme.gemspec")) + end + + def create_accessories + accessories = %w(README.md LICENSE.txt) + accessories << "CODE_OF_CONDUCT.md" if code_of_conduct + accessories.each do |filename| + write_file(filename, template(filename)) + end + end + + def initialize_git_repo + Jekyll.logger.info "initialize", path.join(".git").to_s + Dir.chdir(path.to_s) { `git init` } + write_file(".gitignore", template("gitignore")) + end + + class ERBRenderer + extend Forwardable + + def_delegator :@theme_builder, :name, :theme_name + def_delegator :@theme_builder, :user_name, :user_name + def_delegator :@theme_builder, :user_email, :user_email + + def initialize(theme_builder) + @theme_builder = theme_builder + end + + def jekyll_version_with_minor + Jekyll::VERSION.split(".").take(2).join(".") + end + + def theme_directories + SCAFFOLD_DIRECTORIES + end + + def render(contents) + ERB.new(contents).result binding + end + end + end +end diff --git a/lib/jekyll/url.rb b/lib/jekyll/url.rb new file mode 100644 index 0000000..aeb2ac0 --- /dev/null +++ b/lib/jekyll/url.rb @@ -0,0 +1,167 @@ +# frozen_string_literal: true + +# Public: Methods that generate a URL for a resource such as a Post or a Page. +# +# Examples +# +# URL.new({ +# :template => /:categories/:title.html", +# :placeholders => {:categories => "ruby", :title => "something"} +# }).to_s +# +module Jekyll + class URL + # options - One of :permalink or :template must be supplied. + # :template - The String used as template for URL generation, + # for example "/:path/:basename:output_ext", where + # a placeholder is prefixed with a colon. + # :placeholders - A hash containing the placeholders which will be + # replaced when used inside the template. E.g. + # { "year" => Time.now.strftime("%Y") } would replace + # the placeholder ":year" with the current year. + # :permalink - If supplied, no URL will be generated from the + # template. Instead, the given permalink will be + # used as URL. + def initialize(options) + @template = options[:template] + @placeholders = options[:placeholders] || {} + @permalink = options[:permalink] + + if (@template || @permalink).nil? + raise ArgumentError, "One of :template or :permalink must be supplied." + end + end + + # The generated relative URL of the resource + # + # Returns the String URL + def to_s + sanitize_url(generated_permalink || generated_url) + end + + # Generates a URL from the permalink + # + # Returns the _unsanitized String URL + def generated_permalink + (@generated_permalink ||= generate_url(@permalink)) if @permalink + end + + # Generates a URL from the template + # + # Returns the unsanitized String URL + def generated_url + @generated_url ||= generate_url(@template) + end + + # Internal: Generate the URL by replacing all placeholders with their + # respective values in the given template + # + # Returns the unsanitized String URL + def generate_url(template) + if @placeholders.is_a? Drops::UrlDrop + generate_url_from_drop(template) + else + generate_url_from_hash(template) + end + end + + def generate_url_from_hash(template) + @placeholders.inject(template) do |result, token| + break result if result.index(":").nil? + + if token.last.nil? + # Remove leading "/" to avoid generating urls with `//` + result.gsub("/:#{token.first}", "") + else + result.gsub(":#{token.first}", self.class.escape_path(token.last)) + end + end + end + + # We include underscores in keys to allow for 'i_month' and so forth. + # This poses a problem for keys which are followed by an underscore + # but the underscore is not part of the key, e.g. '/:month_:day'. + # That should be :month and :day, but our key extraction regexp isn't + # smart enough to know that so we have to make it an explicit + # possibility. + def possible_keys(key) + if key.end_with?("_") + [key, key.chomp("_")] + else + [key] + end + end + + def generate_url_from_drop(template) + template.gsub(%r!:([a-z_]+)!) do |match| + name = Regexp.last_match(1) + pool = name.end_with?("_") ? [name, name.chomp!("_")] : [name] + + winner = pool.find { |key| @placeholders.key?(key) } + if winner.nil? + raise NoMethodError, + "The URL template doesn't have #{pool.join(" or ")} keys. " \ + "Check your permalink template!" + end + + value = @placeholders[winner] + value = "" if value.nil? + replacement = self.class.escape_path(value) + + match.sub!(":#{winner}", replacement) + end + end + + # Returns a sanitized String URL, stripping "../../" and multiples of "/", + # as well as the beginning "/" so we can enforce and ensure it. + def sanitize_url(str) + "/#{str}".gsub("..", "/").tap do |result| + result.gsub!("./", "") + result.squeeze!("/") + end + end + + # Escapes a path to be a valid URL path segment + # + # path - The path to be escaped. + # + # Examples: + # + # URL.escape_path("/a b") + # # => "/a%20b" + # + # Returns the escaped path. + def self.escape_path(path) + return path if path.empty? || %r!^[a-zA-Z0-9./-]+$!.match?(path) + + # Because URI.escape doesn't escape "?", "[" and "]" by default, + # specify unsafe string (except unreserved, sub-delims, ":", "@" and "/"). + # + # URI path segment is defined in RFC 3986 as follows: + # segment = *pchar + # pchar = unreserved / pct-encoded / sub-delims / ":" / "@" + # unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" + # pct-encoded = "%" HEXDIG HEXDIG + # sub-delims = "!" / "$" / "&" / "'" / "(" / ")" + # / "*" / "+" / "," / ";" / "=" + Addressable::URI.encode(path).encode("utf-8").sub("#", "%23") + end + + # Unescapes a URL path segment + # + # path - The path to be unescaped. + # + # Examples: + # + # URL.unescape_path("/a%20b") + # # => "/a b" + # + # Returns the unescaped path. + def self.unescape_path(path) + path = path.encode("utf-8") + return path unless path.include?("%") + + Addressable::URI.unencode(path) + end + end +end diff --git a/lib/jekyll/utils.rb b/lib/jekyll/utils.rb new file mode 100644 index 0000000..2a96527 --- /dev/null +++ b/lib/jekyll/utils.rb @@ -0,0 +1,371 @@ +# frozen_string_literal: true + +module Jekyll + module Utils + extend self + autoload :Ansi, "jekyll/utils/ansi" + autoload :Exec, "jekyll/utils/exec" + autoload :Internet, "jekyll/utils/internet" + autoload :Platforms, "jekyll/utils/platforms" + autoload :ThreadEvent, "jekyll/utils/thread_event" + autoload :WinTZ, "jekyll/utils/win_tz" + + # Constants for use in #slugify + SLUGIFY_MODES = %w(raw default pretty ascii latin).freeze + SLUGIFY_RAW_REGEXP = Regexp.new('\\s+').freeze + SLUGIFY_DEFAULT_REGEXP = Regexp.new("[^\\p{M}\\p{L}\\p{Nd}]+").freeze + SLUGIFY_PRETTY_REGEXP = Regexp.new("[^\\p{M}\\p{L}\\p{Nd}._~!$&'()+,;=@]+").freeze + SLUGIFY_ASCII_REGEXP = Regexp.new("[^[A-Za-z0-9]]+").freeze + + # Takes a slug and turns it into a simple title. + def titleize_slug(slug) + slug.split("-").map!(&:capitalize).join(" ") + end + + # Non-destructive version of deep_merge_hashes! See that method. + # + # Returns the merged hashes. + def deep_merge_hashes(master_hash, other_hash) + deep_merge_hashes!(master_hash.dup, other_hash) + end + + # Merges a master hash with another hash, recursively. + # + # master_hash - the "parent" hash whose values will be overridden + # other_hash - the other hash whose values will be persisted after the merge + # + # This code was lovingly stolen from some random gem: + # http://gemjack.com/gems/tartan-0.1.1/classes/Hash.html + # + # Thanks to whoever made it. + def deep_merge_hashes!(target, overwrite) + merge_values(target, overwrite) + merge_default_proc(target, overwrite) + duplicate_frozen_values(target) + + target + end + + def mergable?(value) + value.is_a?(Hash) || value.is_a?(Drops::Drop) + end + + def duplicable?(obj) + case obj + when nil, false, true, Symbol, Numeric + false + else + true + end + end + + # Read array from the supplied hash favouring the singular key + # and then the plural key, and handling any nil entries. + # + # hash - the hash to read from + # singular_key - the singular key + # plural_key - the plural key + # + # Returns an array + def pluralized_array_from_hash(hash, singular_key, plural_key) + array = [] + value = value_from_singular_key(hash, singular_key) + value ||= value_from_plural_key(hash, plural_key) + + array << value + array.flatten! + array.compact! + array + end + + def value_from_singular_key(hash, key) + hash[key] if hash.key?(key) || (hash.default_proc && hash[key]) + end + + def value_from_plural_key(hash, key) + if hash.key?(key) || (hash.default_proc && hash[key]) + val = hash[key] + case val + when String + val.split + when Array + val.compact + end + end + end + + def transform_keys(hash) + result = {} + hash.each_key do |key| + result[yield(key)] = hash[key] + end + result + end + + # Apply #to_sym to all keys in the hash + # + # hash - the hash to which to apply this transformation + # + # Returns a new hash with symbolized keys + def symbolize_hash_keys(hash) + transform_keys(hash) { |key| key.to_sym rescue key } + end + + # Apply #to_s to all keys in the Hash + # + # hash - the hash to which to apply this transformation + # + # Returns a new hash with stringified keys + def stringify_hash_keys(hash) + transform_keys(hash) { |key| key.to_s rescue key } + end + + # Parse a date/time and throw an error if invalid + # + # input - the date/time to parse + # msg - (optional) the error message to show the user + # + # Returns the parsed date if successful, throws a FatalException + # if not + def parse_date(input, msg = "Input could not be parsed.") + @parse_date_cache ||= {} + @parse_date_cache[input] ||= Time.parse(input).localtime + rescue ArgumentError + raise Errors::InvalidDateError, "Invalid date '#{input}': #{msg}" + end + + # Determines whether a given file has + # + # Returns true if the YAML front matter is present. + # rubocop: disable Naming/PredicateName + def has_yaml_header?(file) + File.open(file, "rb", &:readline).match? %r!\A---\s*\r?\n! + rescue EOFError + false + end + + # Determine whether the given content string contains Liquid Tags or Variables + # + # Returns true is the string contains sequences of `{%` or `{{` + def has_liquid_construct?(content) + return false if content.nil? || content.empty? + + content.include?("{%") || content.include?("{{") + end + # rubocop: enable Naming/PredicateName + + # Slugify a filename or title. + # + # string - the filename or title to slugify + # mode - how string is slugified + # cased - whether to replace all uppercase letters with their + # lowercase counterparts + # + # When mode is "none", return the given string. + # + # When mode is "raw", return the given string, + # with every sequence of spaces characters replaced with a hyphen. + # + # When mode is "default" or nil, non-alphabetic characters are + # replaced with a hyphen too. + # + # When mode is "pretty", some non-alphabetic characters (._~!$&'()+,;=@) + # are not replaced with hyphen. + # + # When mode is "ascii", some everything else except ASCII characters + # a-z (lowercase), A-Z (uppercase) and 0-9 (numbers) are not replaced with hyphen. + # + # When mode is "latin", the input string is first preprocessed so that + # any letters with accents are replaced with the plain letter. Afterwards, + # it follows the "default" mode of operation. + # + # If cased is true, all uppercase letters in the result string are + # replaced with their lowercase counterparts. + # + # Examples: + # slugify("The _config.yml file") + # # => "the-config-yml-file" + # + # slugify("The _config.yml file", "pretty") + # # => "the-_config.yml-file" + # + # slugify("The _config.yml file", "pretty", true) + # # => "The-_config.yml file" + # + # slugify("The _config.yml file", "ascii") + # # => "the-config-yml-file" + # + # slugify("The _config.yml file", "latin") + # # => "the-config-yml-file" + # + # Returns the slugified string. + def slugify(string, mode: nil, cased: false) + mode ||= "default" + return nil if string.nil? + + unless SLUGIFY_MODES.include?(mode) + return cased ? string : string.downcase + end + + # Drop accent marks from latin characters. Everything else turns to ? + if mode == "latin" + I18n.config.available_locales = :en if I18n.config.available_locales.empty? + string = I18n.transliterate(string) + end + + slug = replace_character_sequence_with_hyphen(string, :mode => mode) + + # Remove leading/trailing hyphen + slug.gsub!(%r!^-|-$!i, "") + + slug.downcase! unless cased + Jekyll.logger.warn("Warning:", "Empty `slug` generated for '#{string}'.") if slug.empty? + slug + end + + # Add an appropriate suffix to template so that it matches the specified + # permalink style. + # + # template - permalink template without trailing slash or file extension + # permalink_style - permalink style, either built-in or custom + # + # The returned permalink template will use the same ending style as + # specified in permalink_style. For example, if permalink_style contains a + # trailing slash (or is :pretty, which indirectly has a trailing slash), + # then so will the returned template. If permalink_style has a trailing + # ":output_ext" (or is :none, :date, or :ordinal) then so will the returned + # template. Otherwise, template will be returned without modification. + # + # Examples: + # add_permalink_suffix("/:basename", :pretty) + # # => "/:basename/" + # + # add_permalink_suffix("/:basename", :date) + # # => "/:basename:output_ext" + # + # add_permalink_suffix("/:basename", "/:year/:month/:title/") + # # => "/:basename/" + # + # add_permalink_suffix("/:basename", "/:year/:month/:title") + # # => "/:basename" + # + # Returns the updated permalink template + def add_permalink_suffix(template, permalink_style) + template = template.dup + + case permalink_style + when :pretty + template << "/" + when :date, :ordinal, :none + template << ":output_ext" + else + template << "/" if permalink_style.to_s.end_with?("/") + template << ":output_ext" if permalink_style.to_s.end_with?(":output_ext") + end + + template + end + + # Work the same way as Dir.glob but separating the input into two parts + # ('dir' + '/' + 'pattern') to make sure the first part('dir') does not act + # as a pattern. + # + # For example, Dir.glob("path[/*") always returns an empty array, + # because the method fails to find the closing pattern to '[' which is ']' + # + # Examples: + # safe_glob("path[", "*") + # # => ["path[/file1", "path[/file2"] + # + # safe_glob("path", "*", File::FNM_DOTMATCH) + # # => ["path/.", "path/..", "path/file1"] + # + # safe_glob("path", ["**", "*"]) + # # => ["path[/file1", "path[/folder/file2"] + # + # dir - the dir where glob will be executed under + # (the dir will be included to each result) + # patterns - the patterns (or the pattern) which will be applied under the dir + # flags - the flags which will be applied to the pattern + # + # Returns matched paths + def safe_glob(dir, patterns, flags = 0) + return [] unless Dir.exist?(dir) + + pattern = File.join(Array(patterns)) + return [dir] if pattern.empty? + + Dir.chdir(dir) do + Dir.glob(pattern, flags).map { |f| File.join(dir, f) } + end + end + + # Returns merged option hash for File.read of self.site (if exists) + # and a given param + def merged_file_read_opts(site, opts) + merged = (site ? site.file_read_opts : {}).merge(opts) + + # always use BOM when reading UTF-encoded files + if merged[:encoding]&.downcase&.start_with?("utf-") + merged[:encoding] = "bom|#{merged[:encoding]}" + end + if merged["encoding"]&.downcase&.start_with?("utf-") + merged["encoding"] = "bom|#{merged["encoding"]}" + end + + merged + end + + private + + def merge_values(target, overwrite) + target.merge!(overwrite) do |_key, old_val, new_val| + if new_val.nil? + old_val + elsif mergable?(old_val) && mergable?(new_val) + deep_merge_hashes(old_val, new_val) + else + new_val + end + end + end + + def merge_default_proc(target, overwrite) + if target.is_a?(Hash) && overwrite.is_a?(Hash) && target.default_proc.nil? + target.default_proc = overwrite.default_proc + end + end + + def duplicate_frozen_values(target) + target.each do |key, val| + target[key] = val.dup if val.frozen? && duplicable?(val) + end + end + + # Replace each character sequence with a hyphen. + # + # See Utils#slugify for a description of the character sequence specified + # by each mode. + def replace_character_sequence_with_hyphen(string, mode: "default") + replaceable_char = + case mode + when "raw" + SLUGIFY_RAW_REGEXP + when "pretty" + # "._~!$&'()+,;=@" is human readable (not URI-escaped) in URL + # and is allowed in both extN and NTFS. + SLUGIFY_PRETTY_REGEXP + when "ascii" + # For web servers not being able to handle Unicode, the safe + # method is to ditch anything else but latin letters and numeric + # digits. + SLUGIFY_ASCII_REGEXP + else + SLUGIFY_DEFAULT_REGEXP + end + + # Strip according to the mode + string.gsub(replaceable_char, "-") + end + end +end diff --git a/lib/jekyll/utils/ansi.rb b/lib/jekyll/utils/ansi.rb new file mode 100644 index 0000000..00d1b1d --- /dev/null +++ b/lib/jekyll/utils/ansi.rb @@ -0,0 +1,57 @@ +# frozen_string_literal: true + +module Jekyll + module Utils + module Ansi + extend self + + ESCAPE = format("%c", 27) + MATCH = %r!#{ESCAPE}\[(?:\d+)(?:;\d+)*(j|k|m|s|u|A|B|G)|\e\(B\e\[m!ix.freeze + COLORS = { + :red => 31, + :green => 32, + :black => 30, + :magenta => 35, + :yellow => 33, + :white => 37, + :blue => 34, + :cyan => 36, + }.freeze + + # Strip ANSI from the current string. It also strips cursor stuff, + # well some of it, and it also strips some other stuff that a lot of + # the other ANSI strippers don't. + + def strip(str) + str.gsub MATCH, "" + end + + # + + def has?(str) + !!(str =~ MATCH) + end + + # Reset the color back to the default color so that you do not leak any + # colors when you move onto the next line. This is probably normally + # used as part of a wrapper so that we don't leak colors. + + def reset(str = "") + @ansi_reset ||= format("%c[0m", 27) + "#{@ansi_reset}#{str}" + end + + # SEE: `self::COLORS` for a list of methods. They are mostly + # standard base colors supported by pretty much any xterm-color, we do + # not need more than the base colors so we do not include them. + # Actually... if I'm honest we don't even need most of the + # base colors. + + COLORS.each do |color, num| + define_method color do |str| + "#{format("%c", 27)}[#{num}m#{str}#{reset}" + end + end + end + end +end diff --git a/lib/jekyll/utils/exec.rb b/lib/jekyll/utils/exec.rb new file mode 100644 index 0000000..5838ecb --- /dev/null +++ b/lib/jekyll/utils/exec.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +require "open3" + +module Jekyll + module Utils + module Exec + extend self + + # Runs a program in a sub-shell. + # + # *args - a list of strings containing the program name and arguments + # + # Returns a Process::Status and a String of output in an array in + # that order. + def run(*args) + stdin, stdout, stderr, process = Open3.popen3(*args) + out = stdout.read.strip + err = stderr.read.strip + + [stdin, stdout, stderr].each(&:close) + [process.value, out + err] + end + end + end +end diff --git a/lib/jekyll/utils/internet.rb b/lib/jekyll/utils/internet.rb new file mode 100644 index 0000000..945267f --- /dev/null +++ b/lib/jekyll/utils/internet.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +module Jekyll + module Utils + module Internet + # Public: Determine whether the present device has a connection to + # the Internet. This allows plugin writers which require the outside + # world to have a neat fallback mechanism for offline building. + # + # Example: + # if Internet.connected? + # Typhoeus.get("https://pages.github.com/versions.json") + # else + # Jekyll.logger.warn "Warning:", "Version check has been disabled." + # Jekyll.logger.warn "", "Connect to the Internet to enable it." + # nil + # end + # + # Returns true if a DNS call can successfully be made, or false if not. + + module_function + + def connected? + !dns("example.com").nil? + end + + def dns(domain) + require "resolv" + Resolv::DNS.open do |resolver| + resolver.getaddress(domain) + end + rescue Resolv::ResolvError, Resolv::ResolvTimeout + nil + end + end + end +end diff --git a/lib/jekyll/utils/platforms.rb b/lib/jekyll/utils/platforms.rb new file mode 100644 index 0000000..a6c6da9 --- /dev/null +++ b/lib/jekyll/utils/platforms.rb @@ -0,0 +1,67 @@ +# frozen_string_literal: true + +module Jekyll + module Utils + module Platforms + extend self + + def jruby? + RUBY_ENGINE == "jruby" + end + + def mri? + RUBY_ENGINE == "ruby" + end + + def windows? + vanilla_windows? || bash_on_windows? + end + + # Not a Windows Subsystem for Linux (WSL) + def vanilla_windows? + rbconfig_host.match?(%r!mswin|mingw|cygwin!) && proc_version.empty? + end + alias_method :really_windows?, :vanilla_windows? + + # Determine if Windows Subsystem for Linux (WSL) + def bash_on_windows? + linux_os? && microsoft_proc_version? + end + + def linux? + linux_os? && !microsoft_proc_version? + end + + def osx? + rbconfig_host.match?(%r!darwin|mac os!) + end + + def unix? + rbconfig_host.match?(%r!solaris|bsd!) + end + + private + + def proc_version + @proc_version ||= \ + begin + File.read("/proc/version").downcase + rescue Errno::ENOENT, Errno::EACCES + "" + end + end + + def rbconfig_host + @rbconfig_host ||= RbConfig::CONFIG["host_os"].downcase + end + + def linux_os? + rbconfig_host.include?("linux") + end + + def microsoft_proc_version? + proc_version.include?("microsoft") + end + end + end +end diff --git a/lib/jekyll/utils/thread_event.rb b/lib/jekyll/utils/thread_event.rb new file mode 100644 index 0000000..801022e --- /dev/null +++ b/lib/jekyll/utils/thread_event.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +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 + @cond.wait(@lock) unless @flag + end + end + end + end +end diff --git a/lib/jekyll/utils/win_tz.rb b/lib/jekyll/utils/win_tz.rb new file mode 100644 index 0000000..ad277d3 --- /dev/null +++ b/lib/jekyll/utils/win_tz.rb @@ -0,0 +1,46 @@ +# frozen_string_literal: true + +module Jekyll + module Utils + module WinTZ + extend self + + # Public: Calculate the Timezone for Windows when the config file has a defined + # 'timezone' key. + # + # timezone - the IANA Time Zone specified in "_config.yml" + # + # Returns a string that ultimately re-defines ENV["TZ"] in Windows + def calculate(timezone, now = Time.now) + External.require_with_graceful_fail("tzinfo") unless defined?(TZInfo) + tz = TZInfo::Timezone.get(timezone) + + # + # Use period_for_utc and utc_total_offset instead of + # period_for and observed_utc_offset for compatibility with tzinfo v1. + offset = tz.period_for_utc(now.getutc).utc_total_offset + + # + # POSIX style definition reverses the offset sign. + # e.g. Eastern Standard Time (EST) that is 5Hrs. to the 'west' of Prime Meridian + # is denoted as: + # EST+5 (or) EST+05:00 + # Reference: https://www.gnu.org/software/libc/manual/html_node/TZ-Variable.html + sign = offset.positive? ? "-" : "+" + + rational_hours = offset.abs.to_r / 3600 + hours = rational_hours.to_i + minutes = ((rational_hours - hours) * 60).to_i + + # + # Format the hours and minutes as two-digit numbers. + time = format("%<hours>02d:%<minutes>02d", :hours => hours, :minutes => minutes) + + Jekyll.logger.debug "Timezone:", "#{timezone} #{sign}#{time}" + # + # Note: The 3-letter-word below doesn't have a particular significance. + "WTZ#{sign}#{time}" + end + end + end +end diff --git a/lib/jekyll/version.rb b/lib/jekyll/version.rb new file mode 100644 index 0000000..525f4c6 --- /dev/null +++ b/lib/jekyll/version.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +module Jekyll + VERSION = "4.3.1" +end diff --git a/lib/site_template/.gitignore b/lib/site_template/.gitignore new file mode 100644 index 0000000..f40fbd8 --- /dev/null +++ b/lib/site_template/.gitignore @@ -0,0 +1,5 @@ +_site +.sass-cache +.jekyll-cache +.jekyll-metadata +vendor diff --git a/lib/site_template/404.html b/lib/site_template/404.html new file mode 100644 index 0000000..086a5c9 --- /dev/null +++ b/lib/site_template/404.html @@ -0,0 +1,25 @@ +--- +permalink: /404.html +layout: default +--- + +<style type="text/css" media="screen"> + .container { + margin: 10px auto; + max-width: 600px; + text-align: center; + } + h1 { + margin: 30px 0; + font-size: 4em; + line-height: 1; + letter-spacing: -1px; + } +</style> + +<div class="container"> + <h1>404</h1> + + <p><strong>Page not found :(</strong></p> + <p>The requested page could not be found.</p> +</div> diff --git a/lib/site_template/_config.yml b/lib/site_template/_config.yml new file mode 100644 index 0000000..ef7ba7c --- /dev/null +++ b/lib/site_template/_config.yml @@ -0,0 +1,55 @@ +# Welcome to Jekyll! +# +# This config file is meant for settings that affect your whole blog, values +# which you are expected to set up once and rarely edit after that. If you find +# yourself editing this file very often, consider using Jekyll's data files +# feature for the data you need to update frequently. +# +# For technical reasons, this file is *NOT* reloaded automatically when you use +# 'bundle exec jekyll serve'. If you change this file, please restart the server process. +# +# If you need help with YAML syntax, here are some quick references for you: +# https://learn-the-web.algonquindesign.ca/topics/markdown-yaml-cheat-sheet/#yaml +# https://learnxinyminutes.com/docs/yaml/ +# +# Site settings +# These are used to personalize your new site. If you look in the HTML files, +# you will see them accessed via {{ site.title }}, {{ site.email }}, and so on. +# You can create any custom variable you would like, and they will be accessible +# in the templates via {{ site.myvariable }}. + +title: Your awesome title +email: your-email@example.com +description: >- # this means to ignore newlines until "baseurl:" + Write an awesome description for your new site here. You can edit this + line in _config.yml. It will appear in your document head meta (for + Google search results) and in your feed.xml site description. +baseurl: "" # the subpath of your site, e.g. /blog +url: "" # the base hostname & protocol for your site, e.g. http://example.com +twitter_username: jekyllrb +github_username: jekyll + +# Build settings +theme: minima +plugins: + - jekyll-feed + +# Exclude from processing. +# The following items will not be processed, by default. +# Any item listed under the `exclude:` key here will be automatically added to +# the internal "default list". +# +# Excluded items can be processed by explicitly listing the directories or +# their entries' file path in the `include:` list. +# +# exclude: +# - .sass-cache/ +# - .jekyll-cache/ +# - gemfiles/ +# - Gemfile +# - Gemfile.lock +# - node_modules/ +# - vendor/bundle/ +# - vendor/cache/ +# - vendor/gems/ +# - vendor/ruby/ diff --git a/lib/site_template/_posts/0000-00-00-welcome-to-jekyll.markdown.erb b/lib/site_template/_posts/0000-00-00-welcome-to-jekyll.markdown.erb new file mode 100644 index 0000000..187a4d9 --- /dev/null +++ b/lib/site_template/_posts/0000-00-00-welcome-to-jekyll.markdown.erb @@ -0,0 +1,29 @@ +--- +layout: post +title: "Welcome to Jekyll!" +date: <%= Time.now.strftime('%Y-%m-%d %H:%M:%S %z') %> +categories: jekyll update +--- +You’ll find this post in your `_posts` directory. Go ahead and edit it and re-build the site to see your changes. You can rebuild the site in many different ways, but the most common way is to run `jekyll serve`, which launches a web server and auto-regenerates your site when a file is updated. + +Jekyll requires blog post files to be named according to the following format: + +`YEAR-MONTH-DAY-title.MARKUP` + +Where `YEAR` is a four-digit number, `MONTH` and `DAY` are both two-digit numbers, and `MARKUP` is the file extension representing the format used in the file. After that, include the necessary front matter. Take a look at the source for this post to get an idea about how it works. + +Jekyll also offers powerful support for code snippets: + +{% highlight ruby %} +def print_hi(name) + puts "Hi, #{name}" +end +print_hi('Tom') +#=> prints 'Hi, Tom' to STDOUT. +{% endhighlight %} + +Check out the [Jekyll docs][jekyll-docs] for more info on how to get the most out of Jekyll. File all bugs/feature requests at [Jekyll’s GitHub repo][jekyll-gh]. If you have questions, you can ask them on [Jekyll Talk][jekyll-talk]. + +[jekyll-docs]: https://jekyllrb.com/docs/home +[jekyll-gh]: https://github.com/jekyll/jekyll +[jekyll-talk]: https://talk.jekyllrb.com/ diff --git a/lib/site_template/about.markdown b/lib/site_template/about.markdown new file mode 100644 index 0000000..8b4e0b2 --- /dev/null +++ b/lib/site_template/about.markdown @@ -0,0 +1,18 @@ +--- +layout: page +title: About +permalink: /about/ +--- + +This is the base Jekyll theme. You can find out more info about customizing your Jekyll theme, as well as basic Jekyll usage documentation at [jekyllrb.com](https://jekyllrb.com/) + +You can find the source code for Minima at GitHub: +[jekyll][jekyll-organization] / +[minima](https://github.com/jekyll/minima) + +You can find the source code for Jekyll at GitHub: +[jekyll][jekyll-organization] / +[jekyll](https://github.com/jekyll/jekyll) + + +[jekyll-organization]: https://github.com/jekyll diff --git a/lib/site_template/index.markdown b/lib/site_template/index.markdown new file mode 100644 index 0000000..0671507 --- /dev/null +++ b/lib/site_template/index.markdown @@ -0,0 +1,6 @@ +--- +# Feel free to add content and custom Front Matter to this file. +# To modify the layout, see https://jekyllrb.com/docs/themes/#overriding-theme-defaults + +layout: home +--- diff --git a/lib/theme_template/CODE_OF_CONDUCT.md.erb b/lib/theme_template/CODE_OF_CONDUCT.md.erb new file mode 100644 index 0000000..2b2c773 --- /dev/null +++ b/lib/theme_template/CODE_OF_CONDUCT.md.erb @@ -0,0 +1,74 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, gender identity and expression, level of experience, +nationality, personal appearance, race, religion, or sexual identity and +orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or +advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at <%= user_email %>. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at [http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ diff --git a/lib/theme_template/Gemfile b/lib/theme_template/Gemfile new file mode 100644 index 0000000..bb94df8 --- /dev/null +++ b/lib/theme_template/Gemfile @@ -0,0 +1,4 @@ +# frozen_string_literal: true + +source "https://rubygems.org" +gemspec diff --git a/lib/theme_template/LICENSE.txt.erb b/lib/theme_template/LICENSE.txt.erb new file mode 100644 index 0000000..38a0eb4 --- /dev/null +++ b/lib/theme_template/LICENSE.txt.erb @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) <%= Time.now.year %> <%= user_name %> + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/lib/theme_template/README.md.erb b/lib/theme_template/README.md.erb new file mode 100644 index 0000000..9e18034 --- /dev/null +++ b/lib/theme_template/README.md.erb @@ -0,0 +1,50 @@ +# <%= theme_name %> + +Welcome to your new Jekyll theme! In this directory, you'll find the files you need to be able to package up your theme into a gem. Put your layouts in `_layouts`, your includes in `_includes`, your sass files in `_sass` and any other assets in `assets`. + +To experiment with this code, add some sample content and run `bundle exec jekyll serve` – this directory is setup just like a Jekyll site! + +TODO: Delete this and the text above, and describe your gem + +## Installation + +Add this line to your Jekyll site's `Gemfile`: + +```ruby +gem <%= theme_name.inspect %> +``` + +And add this line to your Jekyll site's `_config.yml`: + +```yaml +theme: <%= theme_name %> +``` + +And then execute: + + $ bundle + +Or install it yourself as: + + $ gem install <%= theme_name %> + +## Usage + +TODO: Write usage instructions here. Describe your available layouts, includes, sass and/or assets. + +## Contributing + +Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/<%= theme_name %>. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](https://www.contributor-covenant.org/) code of conduct. + +## Development + +To set up your environment to develop this theme, run `bundle install`. + +Your theme is setup just like a normal Jekyll site! To test your theme, run `bundle exec jekyll serve` and open your browser at `http://localhost:4000`. This starts a Jekyll server using your theme. Add pages, documents, data, etc. like normal to test your theme's contents. As you make modifications to your theme and to your content, your site will regenerate and you should see the changes in the browser after a refresh, just like normal. + +When your theme is released, only the files in `_layouts`, `_includes`, `_sass` and `assets` tracked with Git will be bundled. +To add a custom directory to your theme-gem, please edit the regexp in `<%= theme_name %>.gemspec` accordingly. + +## License + +The theme is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT). diff --git a/lib/theme_template/_layouts/default.html b/lib/theme_template/_layouts/default.html new file mode 100644 index 0000000..cddd070 --- /dev/null +++ b/lib/theme_template/_layouts/default.html @@ -0,0 +1 @@ +{{ content }} diff --git a/lib/theme_template/_layouts/page.html b/lib/theme_template/_layouts/page.html new file mode 100644 index 0000000..5e71126 --- /dev/null +++ b/lib/theme_template/_layouts/page.html @@ -0,0 +1,5 @@ +--- +layout: default +--- + +{{ content }} diff --git a/lib/theme_template/_layouts/post.html b/lib/theme_template/_layouts/post.html new file mode 100644 index 0000000..5e71126 --- /dev/null +++ b/lib/theme_template/_layouts/post.html @@ -0,0 +1,5 @@ +--- +layout: default +--- + +{{ content }} diff --git a/lib/theme_template/example/_config.yml.erb b/lib/theme_template/example/_config.yml.erb new file mode 100644 index 0000000..82e8d42 --- /dev/null +++ b/lib/theme_template/example/_config.yml.erb @@ -0,0 +1 @@ +theme: <%= theme_name %> diff --git a/lib/theme_template/example/_post.md b/lib/theme_template/example/_post.md new file mode 100644 index 0000000..145b21d --- /dev/null +++ b/lib/theme_template/example/_post.md @@ -0,0 +1,12 @@ +--- +layout: post +--- + +Eos eu docendi tractatos sapientem, brute option menandri in vix, quando vivendo accommodare te ius. Nec melius fastidii constituam id, viderer theophrastus ad sit, hinc semper periculis cum id. Noluisse postulant assentior est in, no choro sadipscing repudiandae vix. Vis in euismod delenit dignissim. Ex quod nostrum sit, suas decore animal id ius, nobis solet detracto quo te. + +{% comment %} +Might you have an include in your theme? Why not try it here! +{% include my-themes-great-include.html %} +{% endcomment %} + +No laudem altera adolescens has, volumus lucilius eum no. Eam ei nulla audiam efficiantur. Suas affert per no, ei tale nibh sea. Sea ne magna harum, in denique scriptorem sea, cetero alienum tibique ei eos. Labores persequeris referrentur eos ei. diff --git a/lib/theme_template/example/index.html b/lib/theme_template/example/index.html new file mode 100644 index 0000000..b688538 --- /dev/null +++ b/lib/theme_template/example/index.html @@ -0,0 +1,14 @@ +--- +layout: page +--- + +Lorem ipsum dolor sit amet, quo id prima corrumpit pertinacia, id ius dolor dolores, an veri pertinax explicari mea. Agam solum et qui, his id ludus graeco adipiscing. Duis theophrastus nam in, at his vidisse atomorum. Tantas gloriatur scripserit ne eos. Est wisi tempor habemus at, ei graeco dissentiet eos. Ne usu aliquip sanctus conceptam, te vis ignota animal, modus latine contentiones ius te. + +{% for post in site.posts %} +<h2><a href="{{ post.url }}">{{ post.title }}</a></h2> +<blockquote>{{ post.excerpt }}</blockquote> +{% endfor %} + +Te falli veritus sea, at molestiae scribentur deterruisset vix, et mea zril phaedrum vulputate. No cum dicit consulatu. Ut has nostro noluisse expetendis, te pro quaeque disputando, eu sed summo omnes. Eos at tale aperiam, usu cu propriae quaestio constituto, sed aperiam erroribus temporibus an. + +Quo eu liber mediocritatem, vix an delectus eleifend, iuvaret suscipit ei vel. Partem invenire per an, mea postulant dissentias eu, ius tantas audire nominavi eu. Dicunt tritani veritus ex vis, mei in case sententiae. At exerci democritum nam, cu lobortis iracundia mei. Alia eligendi consectetuer eu sed, paulo docendi noluisse sit ex. diff --git a/lib/theme_template/example/style.scss b/lib/theme_template/example/style.scss new file mode 100644 index 0000000..7388f52 --- /dev/null +++ b/lib/theme_template/example/style.scss @@ -0,0 +1,7 @@ +--- +--- + +// Here, you can test out the Sass/SCSS that you include in your theme. +// Simply `@import` the necessary file(s) to get the proper styles on the site. +// E.g.: +// @import "a-file-from-my-theme"; diff --git a/lib/theme_template/gitignore.erb b/lib/theme_template/gitignore.erb new file mode 100644 index 0000000..736d740 --- /dev/null +++ b/lib/theme_template/gitignore.erb @@ -0,0 +1,6 @@ +*.gem +.bundle +.jekyll-cache +.sass-cache +_site +Gemfile.lock diff --git a/lib/theme_template/theme.gemspec.erb b/lib/theme_template/theme.gemspec.erb new file mode 100644 index 0000000..7c13cd3 --- /dev/null +++ b/lib/theme_template/theme.gemspec.erb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +Gem::Specification.new do |spec| + spec.name = <%= theme_name.inspect %> + spec.version = "0.1.0" + spec.authors = [<%= user_name.inspect %>] + spec.email = [<%= user_email.inspect %>] + + spec.summary = "TODO: Write a short summary, because Rubygems requires one." + spec.homepage = "TODO: Put your gem's website or public repo URL here." + spec.license = "MIT" + + spec.files = `git ls-files -z`.split("\x0").select { |f| f.match(%r!^(<%= theme_directories.join("|") %>|LICENSE|README|_config\.yml)!i) } + + spec.add_runtime_dependency "jekyll", "~> <%= jekyll_version_with_minor %>" +end diff --git a/rake/profile.rake b/rake/profile.rake new file mode 100644 index 0000000..d8c0f4e --- /dev/null +++ b/rake/profile.rake @@ -0,0 +1,57 @@ +# frozen_string_literal: true + +require "jekyll" + +namespace :profile do + desc "Profile allocations from a build session" + task :memory, [:file, :mode] do |_t, args| + args.with_defaults(file: "memprof.txt", mode: "lite") + + build_phases = [:reset, :read, :generate, :render, :cleanup, :write] + safe_mode = false + + if args.mode == "lite" + build_phases -= [:render, :generate] + safe_mode = true + end + + require "memory_profiler" + + report = MemoryProfiler.report do + site = Jekyll::Site.new( + Jekyll.configuration( + "source" => File.expand_path("../docs", __dir__), + "destination" => File.expand_path("../docs/_site", __dir__), + "safe" => safe_mode + ) + ) + + Jekyll.logger.info "Source:", site.source + Jekyll.logger.info "Destination:", site.dest + Jekyll.logger.info "Plugins and Cache:", site.safe ? "disabled" : "enabled" + Jekyll.logger.info "Profiling phases:", build_phases.join(", ").cyan + Jekyll.logger.info "Profiling..." + + build_phases.each { |phase| site.send phase } + + Jekyll.logger.info "", "and done. Generating results.." + Jekyll.logger.info "" + end + + if ENV["CI"] + report.pretty_print(scale_bytes: true, color_output: false, normalize_paths: true) + else + FileUtils.mkdir_p("tmp") + report_file = File.join("tmp", args.file) + + total_allocated_output = report.scale_bytes(report.total_allocated_memsize) + total_retained_output = report.scale_bytes(report.total_retained_memsize) + + Jekyll.logger.info "Total allocated: #{total_allocated_output} (#{report.total_allocated} objects)".cyan + Jekyll.logger.info "Total retained: #{total_retained_output} (#{report.total_retained} objects)".cyan + + report.pretty_print(to_file: report_file, scale_bytes: true, normalize_paths: true) + Jekyll.logger.info "\nDetailed Report saved into:", report_file.cyan + end + end +end diff --git a/rake/release.rake b/rake/release.rake new file mode 100644 index 0000000..7064f67 --- /dev/null +++ b/rake/release.rake @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +############################################################################# +# +# Packaging tasks +# +############################################################################# + +desc "Release #{name} v#{version}" +task :release => :build do + current_branch = `git branch`.to_s.strip.match(%r!^\* (.+)$!)[1] + unless current_branch == "master" || current_branch.end_with?("-stable") + puts "You must be on the master branch to release!" + exit! + end + sh "git commit --allow-empty -m 'Release :gem: #{version}'" + sh "git tag v#{version}" + sh "git push origin #{current_branch}" + sh "git push origin v#{version}" + sh "gem push pkg/#{name}-#{version}.gem" + puts "Do not forget to build and release the docs gem as well." + puts "https://github.com/jekyll/jekyll-docs#releasing" +end + +desc "Build #{name} v#{version} into pkg/" +task :build do + mkdir_p "pkg" + sh "gem build #{gemspec_file}" + sh "mv #{gem_file} pkg" +end diff --git a/rake/site.rake b/rake/site.rake new file mode 100644 index 0000000..9e89847 --- /dev/null +++ b/rake/site.rake @@ -0,0 +1,126 @@ +# frozen_string_literal: true + +############################################################################# +# +# Site tasks - https://jekyllrb.com +# +############################################################################# + +namespace :site do + task :generated_pages => [:history, :latest_version, :conduct, :contributing, :security, :support] + + desc "Generate and view the site locally" + task :preview => :generated_pages do + require "launchy" + require "jekyll" + + browser_launched = false + Jekyll::Hooks.register :site, :post_write do |_site| + next if browser_launched + browser_launched = true + Jekyll.logger.info "Opening in browser..." + Launchy.open("http://localhost:4000") + end + + # Generate the site in server mode. + puts "Running Jekyll..." + options = { + "source" => File.expand_path(docs_folder), + "destination" => File.expand_path("#{docs_folder}/_site"), + "incremental" => true, + "profile" => true, + "watch" => true, + "serving" => true, + } + Jekyll::Commands::Build.process(options) + Jekyll::Commands::Serve.process(options) + end + + desc "Generate the site" + task :generate => :generated_pages do + require "jekyll" + Jekyll::Commands::Build.process({ + "profile" => true, + "source" => File.expand_path(docs_folder), + "destination" => File.expand_path("#{docs_folder}/_site"), + }) + end + task :build => :generate + + desc "Update normalize.css library to the latest version" + task :update_normalize_css do + Dir.chdir("#{docs_folder}/_sass") do + sh 'curl "https://necolas.github.io/normalize.css/latest/normalize.css" -o "_normalize.scss"' + end + end + + desc "Generate generated pages and publish to GitHub Pages" + task :publish => :generated_pages do + puts "GitHub Pages now compiles our docs site on every push to the `master` branch. Cool, huh?" + exit 1 + end + + desc "Create a nicely formatted history page for the jekyll site based on the repo history." + task :history do + siteify_file("History.markdown", { "title" => "History" }) + end + + desc "Copy the Code of Conduct" + task :conduct do + front_matter = { + "redirect_from" => "/conduct/index.html", + "editable" => false, + } + siteify_file(".github/CODE_OF_CONDUCT.markdown", front_matter) + end + + desc "Copy the contributing file" + task :contributing do + siteify_file(".github/CONTRIBUTING.markdown", "title" => "Contributing") + end + + desc "Copy the support file" + task :support do + siteify_file(".github/SUPPORT.markdown", "title" => "Support") + end + + desc "Copy the security policy" + task :security do + siteify_file(".github/SECURITY.markdown", "title" => "Security Policy") + end + + desc "Write the latest Jekyll version" + task :latest_version do + next if version =~ %r!(beta|rc|alpha)!i + require "safe_yaml/load" + config_file = File.join(docs_folder, "_config.yml") + config = SafeYAML.load_file(config_file) + config["version"] = version + File.write(config_file, YAML.dump(config)) + File.open("#{docs_folder}/latest_version.txt", "wb") { |f| f.puts(version) } + end + + namespace :releases do + desc "Create new release post" + task :new, :version do |_t, args| + raise "Specify a version: rake site:releases:new['1.2.3']" unless args.version + today = Time.new.strftime("%Y-%m-%d") + release = args.version.to_s + filename = "#{docs_folder}/_posts/#{today}-jekyll-#{release.split(".").join("-")}-released.markdown" + + File.open(filename, "wb") do |post| + post.puts("---") + post.puts("title: 'Jekyll #{release} Released'") + post.puts("date: #{Time.new.strftime("%Y-%m-%d %H:%M:%S %z")}") + post.puts("author: ") + post.puts("version: #{release}") + post.puts("category: release") + post.puts("---") + post.puts + post.puts + end + + puts "Created #{filename}" + end + end +end diff --git a/rubocop/jekyll.rb b/rubocop/jekyll.rb new file mode 100644 index 0000000..31236b8 --- /dev/null +++ b/rubocop/jekyll.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +Dir[File.join(File.expand_path("jekyll", __dir__), "*.rb")].each do |ruby_file| + require ruby_file +end diff --git a/rubocop/jekyll/assert_equal_literal_actual.rb b/rubocop/jekyll/assert_equal_literal_actual.rb new file mode 100644 index 0000000..5b66569 --- /dev/null +++ b/rubocop/jekyll/assert_equal_literal_actual.rb @@ -0,0 +1,149 @@ +# frozen_string_literal: true + +module RuboCop + module Cop + module Jekyll + # Checks for `assert_equal(exp, act, msg = nil)` calls containing literal values as + # second argument. The second argument should ideally be a method called on the tested + # instance. + # + # @example + # # bad + # assert_equal @foo.bar, "foobar" + # assert_equal @alpha.beta, { "foo" => "bar", "lorem" => "ipsum" } + # assert_equal @alpha.omega, ["foobar", "lipsum"] + # + # # good + # assert_equal "foobar", @foo.bar + # + # assert_equal( + # { "foo" => "bar", "lorem" => "ipsum" }, + # @alpha.beta + # ) + # + # assert_equal( + # ["foobar", "lipsum"], + # @alpha.omega + # ) + # + class AssertEqualLiteralActual < Cop + MSG = "Provide the 'expected value' as the first argument to `assert_equal`.".freeze + + SIMPLE_LITERALS = %i( + true + false + nil + int + float + str + sym + complex + rational + regopt + ).freeze + + COMPLEX_LITERALS = %i( + array + hash + pair + irange + erange + regexp + ).freeze + + def_node_matcher :literal_actual?, <<-PATTERN + (send nil? :assert_equal $(send ...) $#literal?) + PATTERN + + def_node_matcher :literal_actual_with_msg?, <<-PATTERN + (send nil? :assert_equal $(send ...) $#literal? $#opt_msg?) + PATTERN + + def on_send(node) + return unless literal_actual?(node) || literal_actual_with_msg?(node) + add_offense(node, location: :expression) + end + + def autocorrect(node) + lambda do |corrector| + corrector.replace(node.loc.expression, replacement(node)) + end + end + + private + + def opt_msg?(node) + node&.source + end + + # This is not implement using a NodePattern because it seems + # to not be able to match against an explicit (nil) sexp + def literal?(node) + node && (simple_literal?(node) || complex_literal?(node)) + end + + def simple_literal?(node) + SIMPLE_LITERALS.include?(node.type) + end + + def complex_literal?(node) + COMPLEX_LITERALS.include?(node.type) && + node.each_child_node.all?(&method(:literal?)) + end + + def replacement(node) + _, _, first_param, second_param, optional_param = *node + + replaced_text = \ + if second_param.type == :hash + replace_hash_with_variable(first_param.source, second_param.source) + elsif second_param.type == :array && second_param.source != "[]" + replace_array_with_variable(first_param.source, second_param.source) + else + replace_based_on_line_length(first_param.source, second_param.source) + end + + return "#{replaced_text}, #{optional_param.source}" if optional_param + replaced_text + end + + def replace_based_on_line_length(first_expression, second_expression) + result = "assert_equal #{second_expression}, #{first_expression}" + return result if result.length < 80 + + # fold long lines independent of Rubocop configuration for better readability + <<~TEXT + assert_equal( + #{second_expression}, + #{first_expression} + ) + TEXT + end + + def replace_hash_with_variable(first_expression, second_expression) + expect_expression = if second_expression.start_with?("{") + second_expression + else + "{#{second_expression}}" + end + <<~TEXT + expected = #{expect_expression} + assert_equal expected, #{first_expression} + TEXT + end + + def replace_array_with_variable(first_expression, second_expression) + expect_expression = if second_expression.start_with?("%") + second_expression + else + Array(second_expression) + end + <<~TEXT + expected = #{expect_expression} + assert_equal expected, #{first_expression} + TEXT + end + end + end + end +end diff --git a/rubocop/jekyll/no_p_allowed.rb b/rubocop/jekyll/no_p_allowed.rb new file mode 100644 index 0000000..cc7d997 --- /dev/null +++ b/rubocop/jekyll/no_p_allowed.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +require "rubocop" + +module RuboCop + module Cop + module Jekyll + class NoPAllowed < Cop + MSG = "Avoid using `p` to print things. Use `Jekyll.logger` instead.".freeze + + def_node_search :p_called?, <<-PATTERN + (send _ :p _) + PATTERN + + def on_send(node) + if p_called?(node) + add_offense(node, :location => :selector) + end + end + end + end + end +end diff --git a/rubocop/jekyll/no_puts_allowed.rb b/rubocop/jekyll/no_puts_allowed.rb new file mode 100644 index 0000000..a666aac --- /dev/null +++ b/rubocop/jekyll/no_puts_allowed.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +require "rubocop" + +module RuboCop + module Cop + module Jekyll + class NoPutsAllowed < Cop + MSG = "Avoid using `puts` to print things. Use `Jekyll.logger` instead.".freeze + + def_node_search :puts_called?, <<-PATTERN + (send nil? :puts _) + PATTERN + + def on_send(node) + if puts_called?(node) + add_offense(node, :location => :selector) + end + end + end + end + end +end diff --git a/script/backport-pr b/script/backport-pr new file mode 100644 index 0000000..a2b2c33 --- /dev/null +++ b/script/backport-pr @@ -0,0 +1,83 @@ +#!/usr/bin/env bash +# This script is pulled from https://github.com/git-lfs/git-lfs/blob/master/script/backport-pr +# Many thanks, git-lfs team! +# +# Backports a PR into a release branch: +# +# # backport PR #1023 into 1.1-stable-backport-1023 +# $ git checkout master +# $ git pull +# $ script/backport-pr 1.1 1023 + +usage() { + echo "usage: $0 <minor-release-version> <pull-number>" + echo "example: $0 3.4 5623" +} + +if test -z "$1"; then + echo "fatal: no minor release version, e.g. '3.4'" > /dev/stderr + usage + exit 1 +fi + +if test -z "$2"; then + echo "fatal: no pull request number, e.g. '5623'" > /dev/stderr + usage + exit 1 +fi + +relversion="v$1.x" +relbranch="$1-stable" +pr="$2" +prbranch="$relbranch-backport-$pr" +pullsurl="https://api.github.com/repos/jekyll/jekyll/pulls" +prurl="https://api.github.com/repos/jekyll/jekyll/pulls/$pr" +prjson="$(curl -n $pullsurl/$pr 2>/dev/null)" +headref="$(echo $prjson | jq -r -e ".head.ref")" +[ "$?" -ne 0 ] && { + echo "PR #$pr is invalid." + exit 1 +} +prtitle="$(echo $prjson | jq -r ".title" | sed "s/\"/'/g")" + +git checkout -q -f $relbranch +git clean -q -fdx +git pull -q +git checkout -q -f -B $prbranch + +commit=`git log -1 --pretty=%H "--grep=Merge pull request $pr" "--grep=Merge branch '.*$headref'" master` + +echo "Backporting:\n" + +git log -1 $commit + +conflicts="" + +# If we used regular merges, we'd use the `-m 1` flag for the cherry-pick +# command, but since we do squash-merges, we don't want this. +git cherry-pick -x --allow-empty $commit &> /dev/null || { + unmerged=$(git ls-files --unmerged --stage | cut -f 2 -d$'\t' | uniq) + conflicts="\n\nConflicting files:" + for file in $unmerged; do + git add "$file" + conflicts="$conflicts\n- $file" + done + git commit -q --no-edit +} + +commitmsg="Backport $headref from #$pr to $relbranch" +if [ "$conflicts" ]; then + commitmsg="$commitmsg [merge conflicts]" +fi + +git commit -q --allow-empty --amend -m "$commitmsg" +git push -q -f origin $prbranch +git checkout -q -f $relbranch +git branch -q -D $prbranch + +curl -in $pullsurl -d "{ + \"title\": \"Backport #$pr for $relversion: $prtitle\", + \"head\": \"$prbranch\", + \"base\": \"$relbranch\", + \"body\": \"This backports #$pr.$conflicts\" +}" 2>/dev/null diff --git a/script/bootstrap b/script/bootstrap new file mode 100644 index 0000000..054a2c2 --- /dev/null +++ b/script/bootstrap @@ -0,0 +1,4 @@ +#!/usr/bin/env bash + +script/branding +bundle install -j8 diff --git a/script/branding b/script/branding new file mode 100644 index 0000000..2708f4d --- /dev/null +++ b/script/branding @@ -0,0 +1,11 @@ +#!/usr/bin/env bash + +echo " ---------------------------------------------------------- " +echo " _ ______ _ __ __ __ _ _ " +echo " | | | ____| | |/ / \ \ / / | | | | " +echo " | | | |__ | ' / \ \_/ / | | | | " +echo " _ | | | __| | < \ / | | | | " +echo " | |__| | | |____ | . \ | | | |____ | |____ " +echo " \____/ |______| |_|\_\ |_| |______| |______| " +echo " " +echo " ---------------------------------------------------------- " diff --git a/script/cibuild b/script/cibuild new file mode 100644 index 0000000..4cdb6ba --- /dev/null +++ b/script/cibuild @@ -0,0 +1,19 @@ +#!/usr/bin/env bash + +script/branding + +set -e + +if [[ -z "$TEST_SUITE" ]] +then + script/fmt + script/test ci + script/cucumber + script/default-site +elif [[ -x "script/$TEST_SUITE" ]] +then + script/$TEST_SUITE +else + echo "Unknown test suite." + exit 1 +fi diff --git a/script/console b/script/console new file mode 100644 index 0000000..38ca96a --- /dev/null +++ b/script/console @@ -0,0 +1,38 @@ +#!/usr/bin/env ruby + +require 'pry' +$LOAD_PATH.unshift File.expand_path("../lib", __dir__) +require 'jekyll' + +TEST_DIR = File.expand_path("../test", __dir__) + +def fixture_site(overrides = {}) + Jekyll::Site.new(site_configuration(overrides)) +end + +def build_configs(overrides, base_hash = Jekyll::Configuration::DEFAULTS) + Jekyll::Utils.deep_merge_hashes(base_hash, overrides) +end + +def site_configuration(overrides = {}) + build_configs({ + "source" => source_dir, + "destination" => dest_dir + }, build_configs(overrides)) +end + +def dest_dir(*subdirs) + test_dir('dest', *subdirs) +end + +def source_dir(*subdirs) + test_dir('source', *subdirs) +end + +def test_dir(*subdirs) + File.join(TEST_DIR, *subdirs) +end + +module Jekyll + binding.pry +end diff --git a/script/cucumber b/script/cucumber new file mode 100644 index 0000000..91a3755 --- /dev/null +++ b/script/cucumber @@ -0,0 +1,4 @@ +#!/usr/bin/env bash + +time ruby -S bundle exec cucumber \ + --format progress --publish "$@" diff --git a/script/default-site b/script/default-site new file mode 100644 index 0000000..0504fd3 --- /dev/null +++ b/script/default-site @@ -0,0 +1,20 @@ +#!/usr/bin/env bash +# Runs the `jekyll new` command and builds the default site as a sanity check + +set -e + +echo "$0: setting up tmp directory" +mkdir -p ./tmp +rm -Rf ./tmp/default-site + +echo "$0: creating new default site" +bundle exec jekyll new tmp/default-site +pushd tmp/default-site + +echo "$0: respecifying the jekyll install location" +ruby -e "contents = File.read('Gemfile'); File.write('Gemfile', contents.sub(/gem \"jekyll\".*\\n/, 'gem \"jekyll\", path: \"../../\"'))" +echo "$0: installing default site dependencies" +BUNDLE_GEMFILE=Gemfile bundle install +echo "$0: building the default site" +BUNDLE_GEMFILE=Gemfile bundle exec jekyll build --verbose --profile +popd diff --git a/script/fmt b/script/fmt new file mode 100644 index 0000000..9a6cd08 --- /dev/null +++ b/script/fmt @@ -0,0 +1,8 @@ +#!/bin/bash +echo "RuboCop $(bundle exec rubocop --version)" +bundle exec rubocop -D --disable-pending-cops $@ +success=$? +if ((success != 0)); then + echo -e "\nTry running \`script/fmt -a\` to automatically fix errors" +fi +exit $success diff --git a/script/memprof b/script/memprof new file mode 100644 index 0000000..df645ea --- /dev/null +++ b/script/memprof @@ -0,0 +1,5 @@ +#!/bin/bash + +file="memprof.txt" +mode="core" +bundle exec rake profile:memory[$file,$mode] diff --git a/script/profile-docs b/script/profile-docs new file mode 100644 index 0000000..2347826 --- /dev/null +++ b/script/profile-docs @@ -0,0 +1,10 @@ +#!/usr/bin/env bash +# +# Build Jekyll's Documentation site in 'debug' mode and outputs the site's profile stats. +# Helps detecting *hard* breaking-changes (`jekyll build` aborts) and optimizations +# in the `build` process. +# +# Usage: bash script/profile-docs + +SOURCE_DIR=$PWD/docs +bundle exec jekyll build -s $SOURCE_DIR -d $SOURCE_DIR/_site --profile --trace --verbose diff --git a/script/proof b/script/proof new file mode 100644 index 0000000..e8b796b --- /dev/null +++ b/script/proof @@ -0,0 +1,35 @@ +#!/usr/bin/env bash +# +# Usage: +# script/proof + +set -e + +function msg { + printf "\e[0;37m==> $1\e[0m\n" +} + +IGNORE_HREFS=$(ruby -e 'puts %w{ + Chrononaut + twitter.com + nearlyfreespeech.net + eduardoboucas.com + github.com\/matrix9180 +}.map{|h| "/#{h}/"}.join(",")') +SOURCE="docs" +DESTINATION="_site" + +export PROOF=true +export NOKOGIRI_USE_SYSTEM_LIBRARIES=true + +# 1. +msg "Installing..." +bundle install -j8 > /dev/null || bundle install > /dev/null + +# 2. +msg "Building..." +bundle exec jekyll build -s $SOURCE -d $DESTINATION --trace + +# 3. +msg "Proofing..." +time bundle exec htmlproofer ./$DESTINATION --allow-hash-href --url-ignore $IGNORE_HREFS $@ diff --git a/script/rubies b/script/rubies new file mode 100644 index 0000000..ae6d486 --- /dev/null +++ b/script/rubies @@ -0,0 +1,14 @@ +#!/bin/bash +# ----------------------------------------------------------------------------- +# If you send us a ruby then we use that, if you do not then we test with +# whatever we can detect, this way you can run both suites when you test out +# your source, we expect full coverage now, not just MRI. +# ----------------------------------------------------------------------------- + +rubies=() +for r in ruby jruby; do + if which "$r" > /dev/null 2>&1 + then + echo $r + fi +done diff --git a/script/rubyprof b/script/rubyprof new file mode 100644 index 0000000..51a89d9 --- /dev/null +++ b/script/rubyprof @@ -0,0 +1,23 @@ +#!/usr/bin/env ruby + +require "ruby-prof" +require File.expand_path("../lib/jekyll", __dir__) + +result = RubyProf.profile do + Jekyll::Commands::Build.process({ + "source" => File.expand_path("../docs", __dir__), + "destination" => File.expand_path("../docs/_site", __dir__), + }) +end + +puts "\nProcessing result.." + +dir_path = File.expand_path("../tmp", __dir__) +file_path = File.join(dir_path, "rubyprof-#{Time.now.strftime('%Y%m%d%H%M%S')}") + +FileUtils.mkdir_p(dir_path) unless Dir.exist?(dir_path) +File.open(file_path, "wb") do |file| + RubyProf::FlatPrinter.new(result).print(file) +end + +puts "Profile result printed to #{file_path.cyan}" diff --git a/script/stackprof b/script/stackprof new file mode 100644 index 0000000..12cbdd2 --- /dev/null +++ b/script/stackprof @@ -0,0 +1,49 @@ +#!/usr/bin/env ruby + +require "bundler/setup" +require "json" +require "stackprof" +require File.expand_path("../lib/jekyll", __dir__) + +MODE = ARGV.first || "cpu" +PROF_OUTPUT_FILE = File.expand_path("../tmp/stackprof-#{MODE}-#{Time.now.strftime("%Y%m%d%H%M")}.dump", __dir__) + +puts "Stackprof Mode: #{MODE}" + +unless File.exist?(PROF_OUTPUT_FILE) + StackProf.run( + mode: MODE.to_sym, + interval: 100, + raw: true, + out: PROF_OUTPUT_FILE + ) do + puts "GC Stats:", JSON.pretty_generate(GC.stat) + GC.disable + + Jekyll::Commands::Build.process({'source' => 'docs'}) + + puts 'GC Stats:' + GC.start(full_mark: true, immediate_sweep: false) + puts JSON.pretty_generate(GC.stat) + end +end + +options = { + sort: true, + limit: 30, + format: :text, +} + +# You can also do other things. Examples: +# https://github.com/tmm1/stackprof/blob/master/bin/stackprof +report = StackProf::Report.new(Marshal.load(IO.binread(PROF_OUTPUT_FILE))) +report.print_text( + options[:sort], + options[:limit], + options[:select_files], + options[:reject_files], + options[:select_names], + options[:reject_names] +) + +puts "Results stored in #{PROF_OUTPUT_FILE}" diff --git a/script/test b/script/test new file mode 100644 index 0000000..09f7eaa --- /dev/null +++ b/script/test @@ -0,0 +1,56 @@ +#!/usr/bin/env bash +set -e + +# Usage: +# script/test <test_file> +# script/test [ruby|jruby] +# script/test + +if [ -d test/dest ] + then rm -r test/dest +fi + +# ----------------------------------------------------------------------------- +# If you send us a ruby then we use that, if you do not then we test with +# whatever we can detect, this way you can run both suites when you test out +# your source, we expect full coverage now, not just MRI. +# ----------------------------------------------------------------------------- + +if [[ "$1" == "ci" ]] +then + rubies=( + ruby + ) + + shift +elif [[ "$1" == "ruby" ]] || [[ "$1" == "jruby" ]] +then + rubies=( + $1 + ) + + shift +else + rubies=($(script/rubies)) +fi + + +for ruby in $rubies; do + if [[ "$ruby" == "jruby" ]] + then + testopts="" + else + testopts="--profile" + fi + + if [[ $# -lt 1 ]] + then + set -x + time $ruby -S bundle exec \ + rake TESTOPTS=$testopts test + else + set -x + time $ruby -S bundle exec ruby -I test \ + "$@" $testops + fi +done diff --git a/script/travis b/script/travis new file mode 100644 index 0000000..e7f1a13 --- /dev/null +++ b/script/travis @@ -0,0 +1,40 @@ +#!/bin/bash +# Usage: script/travis [ruby-version [file]] +# Example: script/travis 2.0 test/failing_test.rb +# Example: script/travis 2.3.0 +set -e + +mkdir -p vendor/docker +docker rm -fv docker-travis > /dev/null 2>&1 || true +docker run --volume=$(pwd):/home/travis/builds/jekyll/jekyll \ + --workdir=/home/travis/builds/jekyll/jekyll \ + --volume=$(pwd)/vendor/docker:/home/travis/builds/jekyll/jekyll/vendor/bundle \ + --user=travis --name=docker-travis -dit quay.io/travisci/travis-ruby \ + bash > /dev/null + +status=0 +if [ $# -eq 2 ]; then + docker exec -it docker-travis bash -ilc " \ + rvm use --install --binary --fuzzy $1 + bundle install --path vendor/bundle -j 12 \\ + --without benchmark:site:development + bundle clean + script/test $2 + " || status=$? + +elif [ $# -eq 1 ]; then + docker exec -it docker-travis bash -ilc " \ + rvm use --install --binary --fuzzy $1 + bundle install --path vendor/bundle -j 12 \\ + --without benchmark:site:development + bundle clean + bundle exec rake + " || status=$? + +else + docker exec -it docker-travis \ + bash -il || status=$? +fi + +docker rm -fv docker-travis > /dev/null +exit $status diff --git a/script/vendor-mimes b/script/vendor-mimes new file mode 100644 index 0000000..0242d53 --- /dev/null +++ b/script/vendor-mimes @@ -0,0 +1,77 @@ +#!/usr/bin/env ruby +# Vendors the MIME type config from the mime-db list +# usage: script/vendor-mimes + +require 'colorator' +require 'json' +require 'open-uri' + +# ---- Helpers ---- + +{ + :info => :cyan, + :success => :green, + :error => :red, +}.each do |type, color| + define_method("log_#{type}") do |msg| + puts " #{msg}".send(color) + end +end + +# ---- + +json = begin + log_info "Reading remote data.." + URI.open("https://raw.githubusercontent.com/jshttp/mime-db/master/db.json").read +rescue StandardError => e + log_error "Error reading remote data!" + log_error e.message + log_error "Aborting." + exit 1 +end + +log_info "Parsing remote data.." +data = JSON.parse(json) +data.reject! { |mime, meta| meta["extensions"].nil? || meta["extensions"].empty? } + +log_info "Generating interim mime data-hashes.." +mimes = {} +charset_data = {} +data.each do |mime, meta| + # Normalize extensions and mime-types + mime = mime.downcase.strip + extensions = meta["extensions"].map { |e| e.downcase.strip }.compact + + # If a given extension is listed multiple times, prefer the first one listed + extensions.reject! { |extension| mimes.values.flatten.include?(extension) } + + next if extensions.empty? + mimes[mime] = [] if mimes[mime].nil? + mimes[mime].concat extensions + + # Extract mime-types with "charset" metadata + charset_data[mime] = meta["charset"] if meta.key?("charset") + + # Assign `UTF-8` charset for mime-types under the `text` domain if not already assigned upstream + charset_data[mime] ||= "UTF-8" if mime.start_with?("text/") +end + +log_info "Formatting primary hash and writing to file.." +strlen = mimes.keys.max_by(&:length).length +output = "" +output << "# Woah there. Do not edit this file directly.\n" +output << "# This file is generated automatically by script/vendor-mimes.\n\n" +mimes = mimes.sort_by { |k,v| k } +output << mimes.map { |mime,extensions| "#{mime.ljust(strlen)} #{extensions.sort.join(" ")}" }.join("\n") + +config = File.expand_path "../lib/jekyll/mime.types", __dir__ +File.write(config, output) +log_info "Done! See: #{config.inspect.white}" + +# --- Generate JSON file from charset_data ---- +puts + +log_info "Dumping mimetype-charset mapping as JSON.." +json_file = File.expand_path "../lib/jekyll/commands/serve/mime_types_charset.json", __dir__ +File.write(json_file, JSON.pretty_generate(charset_data) + "\n") +log_success "and done! See: #{json_file.inspect.white}" diff --git a/test/fixtures/broken_front_matter1.erb b/test/fixtures/broken_front_matter1.erb new file mode 100644 index 0000000..c18befb --- /dev/null +++ b/test/fixtures/broken_front_matter1.erb @@ -0,0 +1,5 @@ +# Some stuff on the first line +--- +test: good +--- +Real content starts here diff --git a/test/fixtures/broken_front_matter2.erb b/test/fixtures/broken_front_matter2.erb new file mode 100644 index 0000000..f895dd2 --- /dev/null +++ b/test/fixtures/broken_front_matter2.erb @@ -0,0 +1,4 @@ +--- +bad yaml: [ +--- +Real content starts here diff --git a/test/fixtures/broken_front_matter3.erb b/test/fixtures/broken_front_matter3.erb new file mode 100644 index 0000000..0672ae4 --- /dev/null +++ b/test/fixtures/broken_front_matter3.erb @@ -0,0 +1,6 @@ +--- +test: good +--- +Real content starts here + +������� ����� diff --git a/test/fixtures/empty_permalink.erb b/test/fixtures/empty_permalink.erb new file mode 100644 index 0000000..6dabf63 --- /dev/null +++ b/test/fixtures/empty_permalink.erb @@ -0,0 +1,4 @@ +--- +permalink: '' +--- +Empty Permalink diff --git a/test/fixtures/exploit_front_matter.erb b/test/fixtures/exploit_front_matter.erb new file mode 100644 index 0000000..604a7ae --- /dev/null +++ b/test/fixtures/exploit_front_matter.erb @@ -0,0 +1,4 @@ +--- +test: !ruby/hash:DoesNotExist {} +--- +Real content starts here diff --git a/test/fixtures/front_matter.erb b/test/fixtures/front_matter.erb new file mode 100644 index 0000000..96e46a0 --- /dev/null +++ b/test/fixtures/front_matter.erb @@ -0,0 +1,4 @@ +--- +test: good +--- +Real content starts here diff --git a/test/fixtures/no_liquid.erb b/test/fixtures/no_liquid.erb new file mode 100644 index 0000000..bfb7fc4 --- /dev/null +++ b/test/fixtures/no_liquid.erb @@ -0,0 +1,4 @@ +--- +render_with_liquid: false +--- +{% raw %}{% endraw %} diff --git a/test/fixtures/physical.html b/test/fixtures/physical.html new file mode 100644 index 0000000..8913980 --- /dev/null +++ b/test/fixtures/physical.html @@ -0,0 +1,6 @@ +--- +title: Physical file +permalink: /physical/ +--- + +A physical file entity diff --git a/test/fixtures/sample.csv b/test/fixtures/sample.csv new file mode 100644 index 0000000..b4b36e6 --- /dev/null +++ b/test/fixtures/sample.csv @@ -0,0 +1,3 @@ +id,field_a +1,"foo" +2,"bar" diff --git a/test/fixtures/sample.tsv b/test/fixtures/sample.tsv new file mode 100644 index 0000000..c467a88 --- /dev/null +++ b/test/fixtures/sample.tsv @@ -0,0 +1,3 @@ +id field_a +1 "foo" +2 "bar" diff --git a/test/fixtures/test-dependency-theme/test-dependency-theme.gemspec b/test/fixtures/test-dependency-theme/test-dependency-theme.gemspec new file mode 100644 index 0000000..ebc8a83 --- /dev/null +++ b/test/fixtures/test-dependency-theme/test-dependency-theme.gemspec @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +Gem::Specification.new do |s| + s.name = "test-dependency-theme" + s.version = "0.1.0" + s.licenses = ["MIT"] + s.summary = "This is another theme used to test Jekyll" + s.authors = ["Jekyll"] + s.files = ["lib/example.rb"] + s.homepage = "https://github.com/jekyll/jekyll" + + s.add_runtime_dependency "jekyll_test_plugin" +end diff --git a/test/fixtures/test-theme-skinny/_layouts/default.html b/test/fixtures/test-theme-skinny/_layouts/default.html new file mode 100644 index 0000000..837a4dc --- /dev/null +++ b/test/fixtures/test-theme-skinny/_layouts/default.html @@ -0,0 +1,11 @@ +<!DOCTYPE html> +<html> +<head> + <meta charset="UTF-8"> + <title>Skinny</title> +</head> +<body> + <h1>Hello World</h1> + {{ content }} +</body> +</html> diff --git a/test/fixtures/test-theme-skinny/_layouts/home.html b/test/fixtures/test-theme-skinny/_layouts/home.html new file mode 100644 index 0000000..6d9ce5c --- /dev/null +++ b/test/fixtures/test-theme-skinny/_layouts/home.html @@ -0,0 +1,5 @@ +--- +layout: default +--- + +Message: {{ content }} diff --git a/test/fixtures/test-theme-skinny/test-theme-skinny.gemspec b/test/fixtures/test-theme-skinny/test-theme-skinny.gemspec new file mode 100644 index 0000000..84f59b9 --- /dev/null +++ b/test/fixtures/test-theme-skinny/test-theme-skinny.gemspec @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +Gem::Specification.new do |s| + s.name = "test-theme-skinny" + s.version = "0.1.0" + s.licenses = ["MIT"] + s.summary = "This is a theme with just layouts used to test Jekyll" + s.authors = ["Jekyll"] + s.files = ["lib/example.rb"] + s.homepage = "https://github.com/jekyll/jekyll" +end diff --git a/test/fixtures/test-theme-symlink/test-theme-symlink.gemspec b/test/fixtures/test-theme-symlink/test-theme-symlink.gemspec new file mode 100644 index 0000000..b0a915e --- /dev/null +++ b/test/fixtures/test-theme-symlink/test-theme-symlink.gemspec @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +Gem::Specification.new do |s| + s.name = "test-theme-symlink" + s.version = "0.1.0" + s.licenses = ["MIT"] + s.summary = "This is a theme with a symlink used to test Jekyll" + s.authors = ["Jekyll"] + s.files = ["lib/example.rb"] + s.homepage = "https://github.com/jekyll/jekyll" +end diff --git a/test/fixtures/test-theme-w-empty-data/_data/.gitkeep b/test/fixtures/test-theme-w-empty-data/_data/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/test/fixtures/test-theme-w-empty-data/_layouts/default.html b/test/fixtures/test-theme-w-empty-data/_layouts/default.html new file mode 100644 index 0000000..837a4dc --- /dev/null +++ b/test/fixtures/test-theme-w-empty-data/_layouts/default.html @@ -0,0 +1,11 @@ +<!DOCTYPE html> +<html> +<head> + <meta charset="UTF-8"> + <title>Skinny</title> +</head> +<body> + <h1>Hello World</h1> + {{ content }} +</body> +</html> diff --git a/test/fixtures/test-theme-w-empty-data/test-theme-w-empty-data.gemspec b/test/fixtures/test-theme-w-empty-data/test-theme-w-empty-data.gemspec new file mode 100644 index 0000000..013f1b1 --- /dev/null +++ b/test/fixtures/test-theme-w-empty-data/test-theme-w-empty-data.gemspec @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +Gem::Specification.new do |s| + s.name = "test-theme-w-empty-data" + s.version = "0.1.0" + s.licenses = ["MIT"] + s.summary = "This is a theme with just one layout and an empty _data folder used to test Jekyll" + s.authors = ["Jekyll"] + s.files = ["lib/example.rb"] + s.homepage = "https://github.com/jekyll/jekyll" +end diff --git a/test/fixtures/test-theme/_config.yml b/test/fixtures/test-theme/_config.yml new file mode 100644 index 0000000..27c6182 --- /dev/null +++ b/test/fixtures/test-theme/_config.yml @@ -0,0 +1,14 @@ +title: Hello World +baseurl: "/test-theme" +include: ["_extras/banner.md"] +exclude: + - README.md + - CHANGELOG.md + - Rakefile + - test/**/* + +# theme-specific settings +test_theme: + skin: aero # aero / chrome / dark / neon + date_format: "%b -d %Y" # any format supported by strftime + header_links: true # generate header links automatically diff --git a/test/fixtures/test-theme/_data/cars.yml b/test/fixtures/test-theme/_data/cars.yml new file mode 100644 index 0000000..86adb89 --- /dev/null +++ b/test/fixtures/test-theme/_data/cars.yml @@ -0,0 +1,6 @@ +manufacturer: Mercedes +models: +- model: A-Klasse + price: 32,000.00 +- model: B-Klasse + price: 35,000.00 diff --git a/test/fixtures/test-theme/_data/categories/dairy.yaml b/test/fixtures/test-theme/_data/categories/dairy.yaml new file mode 100644 index 0000000..54497fa --- /dev/null +++ b/test/fixtures/test-theme/_data/categories/dairy.yaml @@ -0,0 +1,6 @@ +name: Cheese Dairy +products: +- name: spread cheese + price: 1.2 +- name: cheddar cheese + price: 4.5 diff --git a/test/fixtures/test-theme/_data/greetings.yml b/test/fixtures/test-theme/_data/greetings.yml new file mode 100644 index 0000000..250f1e6 --- /dev/null +++ b/test/fixtures/test-theme/_data/greetings.yml @@ -0,0 +1 @@ +foo: "Hello! I’m bar. What’s up so far?" diff --git a/test/fixtures/test-theme/_data/i18n/testimonials.yml b/test/fixtures/test-theme/_data/i18n/testimonials.yml new file mode 100644 index 0000000..19e0cd1 --- /dev/null +++ b/test/fixtures/test-theme/_data/i18n/testimonials.yml @@ -0,0 +1,2 @@ +header: Testimonials +footer: Design by FTC diff --git a/test/fixtures/test-theme/_includes/include.html b/test/fixtures/test-theme/_includes/include.html new file mode 100644 index 0000000..9860839 --- /dev/null +++ b/test/fixtures/test-theme/_includes/include.html @@ -0,0 +1 @@ +<span class="sample">include.html from test-theme</span> diff --git a/test/fixtures/test-theme/_includes/testimonials.html b/test/fixtures/test-theme/_includes/testimonials.html new file mode 100644 index 0000000..44b054e --- /dev/null +++ b/test/fixtures/test-theme/_includes/testimonials.html @@ -0,0 +1,9 @@ +<section class="testimonials"> + <h3>{{ site.data.i18n.testimonials.header }}</h3> + <!-- for testimonial in site.data.testimonial }} --> + … + <!-- endfor --> + <footer class="testimonials-footer"> + {{ site.data.i18n.testimonials.footer }} + </footer> +</section> diff --git a/test/fixtures/test-theme/_layouts/default.html b/test/fixtures/test-theme/_layouts/default.html new file mode 100644 index 0000000..902c61c --- /dev/null +++ b/test/fixtures/test-theme/_layouts/default.html @@ -0,0 +1 @@ +default.html from test-theme: {{ content }} diff --git a/test/fixtures/test-theme/_sass/test-theme-black.scss b/test/fixtures/test-theme/_sass/test-theme-black.scss new file mode 100644 index 0000000..a1e07da --- /dev/null +++ b/test/fixtures/test-theme/_sass/test-theme-black.scss @@ -0,0 +1,3 @@ +.sample { + color: black; +} diff --git a/test/fixtures/test-theme/_sass/test-theme-red.scss b/test/fixtures/test-theme/_sass/test-theme-red.scss new file mode 100644 index 0000000..0307e17 --- /dev/null +++ b/test/fixtures/test-theme/_sass/test-theme-red.scss @@ -0,0 +1,3 @@ +.sample { + color: red; +} diff --git a/test/fixtures/test-theme/_symlink b/test/fixtures/test-theme/_symlink new file mode 100644 index 0000000..d2d6aac --- /dev/null +++ b/test/fixtures/test-theme/_symlink @@ -0,0 +1 @@ +_layouts \ No newline at end of file diff --git a/test/fixtures/test-theme/assets/application.coffee b/test/fixtures/test-theme/assets/application.coffee new file mode 100644 index 0000000..02f3351 --- /dev/null +++ b/test/fixtures/test-theme/assets/application.coffee @@ -0,0 +1,3 @@ +--- +--- +alert "From your theme." diff --git a/test/fixtures/test-theme/assets/base.js b/test/fixtures/test-theme/assets/base.js new file mode 100644 index 0000000..00c238e --- /dev/null +++ b/test/fixtures/test-theme/assets/base.js @@ -0,0 +1 @@ +alert("From your theme."); diff --git a/test/fixtures/test-theme/assets/img/another-logo.png b/test/fixtures/test-theme/assets/img/another-logo.png new file mode 100644 index 0000000..bd36e71 --- /dev/null +++ b/test/fixtures/test-theme/assets/img/another-logo.png @@ -0,0 +1 @@ +logo.png \ No newline at end of file diff --git a/test/fixtures/test-theme/assets/img/logo.png b/test/fixtures/test-theme/assets/img/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..95f0a3bb8f6534972d3af06950484dfcf98ccbf9 GIT binary patch literal 308 zcmeAS@N?(olHy`uVBq!ia0y~yU;weXIG7k17?=VgUNJB*{PuKl45?t`IkuJYfPx6C z!?$?7ckfisc{l%A-KV*9$&_<a7QBCsSh6b|`(eYs`0V9NQ;yzvcK^HVan%+c#U+kQ z|Nr}Y{pJO|QwI|AGUDPRmTJk@y!m0dKqlnZ`T6#}OPBKS@T~ITxm-L;LW1SnJNt$S z|Nj2|K8sUUTdOEtG(~^C*X)S6idT13TfVH1-`}SfT~P4piD_x+<wtL>`M#+7^-R0# zUH9wj`FXFSWA9t;R>*bQa&hbF6TANZySFjv-NIe>dRuctOF94j<Ox4N&-v2d@BW`Z zEn5?G{oS;t6)P|DRPro8H_xT$=Y4xs<7~-Qk~vW^PdA^H*=wPv?8(gV!0?v-1imwC R85kHCJYD@<);T3K0RY{%ky8Kw literal 0 HcmV?d00001 diff --git a/test/fixtures/test-theme/assets/style.scss b/test/fixtures/test-theme/assets/style.scss new file mode 100644 index 0000000..0009652 --- /dev/null +++ b/test/fixtures/test-theme/assets/style.scss @@ -0,0 +1,3 @@ +--- +--- +@import "test-theme-{{ site.theme-color | default: 'red' }}"; diff --git a/test/fixtures/test-theme/test-theme.gemspec b/test/fixtures/test-theme/test-theme.gemspec new file mode 100644 index 0000000..970e1b8 --- /dev/null +++ b/test/fixtures/test-theme/test-theme.gemspec @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +Gem::Specification.new do |s| + s.name = "test-theme" + s.version = "0.1.0" + s.licenses = ["MIT"] + s.summary = "This is a theme used to test Jekyll" + s.authors = ["Jekyll"] + s.files = ["lib/example.rb"] + s.homepage = "https://github.com/jekyll/jekyll" +end diff --git a/test/fixtures/webrick/bar.html b/test/fixtures/webrick/bar.html new file mode 100644 index 0000000..76870bf --- /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 0000000..794d5f2 --- /dev/null +++ b/test/fixtures/webrick/bar/baz.html @@ -0,0 +1 @@ +Content of baz.html diff --git a/test/fixtures/webrick/bar/foo.xhtml b/test/fixtures/webrick/bar/foo.xhtml new file mode 100644 index 0000000..6890fdc --- /dev/null +++ b/test/fixtures/webrick/bar/foo.xhtml @@ -0,0 +1 @@ +<html xmlns="http://www.w3.org/1999/xhtml">Content of foo.xhtml</html> diff --git a/test/helper.rb b/test/helper.rb new file mode 100644 index 0000000..7b1a2b4 --- /dev/null +++ b/test/helper.rb @@ -0,0 +1,264 @@ +# frozen_string_literal: true + +$stdout.puts "# -------------------------------------------------------------" +$stdout.puts "# SPECS AND TESTS ARE RUNNING WITH WARNINGS OFF." +$stdout.puts "# SEE: https://github.com/Shopify/liquid/issues/730" +$stdout.puts "# SEE: https://github.com/jekyll/jekyll/issues/4719" +$stdout.puts "# -------------------------------------------------------------" +$VERBOSE = nil + +def jruby? + defined?(RUBY_ENGINE) && RUBY_ENGINE == "jruby" +end + +if ENV["CI"] + require "simplecov" + SimpleCov.start +else + require File.expand_path("simplecov_custom_profile", __dir__) + SimpleCov.start "gem" do + add_filter "/vendor/gem" + add_filter "/vendor/bundle" + add_filter ".bundle" + end +end + +require "nokogiri" +require "rubygems" +require "ostruct" +require "minitest/autorun" +require "minitest/reporters" +require "minitest/profile" +require "rspec/mocks" +require_relative "../lib/jekyll" + +Jekyll.logger = Logger.new(StringIO.new, :error) + +require "kramdown" +require "shoulda" + +include Jekyll + +require "jekyll/commands/serve/servlet" + +# Report with color. +Minitest::Reporters.use! [ + Minitest::Reporters::DefaultReporter.new( + :color => true + ), +] + +module Minitest::Assertions + def assert_exist(filename, msg = nil) + msg = message(msg) { "Expected '#{filename}' to exist" } + assert_path_exists(filename, msg) + end + + def refute_exist(filename, msg = nil) + msg = message(msg) { "Expected '#{filename}' not to exist" } + refute_path_exists(filename, msg) + end +end + +module DirectoryHelpers + def root_dir(*subdirs) + File.expand_path(File.join("..", *subdirs), __dir__) + end + + def dest_dir(*subdirs) + test_dir("dest", *subdirs) + end + + def source_dir(*subdirs) + test_dir("source", *subdirs) + end + + def theme_dir(*subdirs) + test_dir("fixtures", "test-theme", *subdirs) + end + + def test_dir(*subdirs) + root_dir("test", *subdirs) + end + + def temp_dir(*subdirs) + if Utils::Platforms.vanilla_windows? + drive = Dir.pwd.sub(%r!^([^/]+).*!, '\1') + temp_root = File.join(drive, "tmp") + else + temp_root = "/tmp" + end + + File.join(temp_root, *subdirs) + end +end + +class JekyllUnitTest < Minitest::Test + include ::RSpec::Mocks::ExampleMethods + include DirectoryHelpers + extend DirectoryHelpers + + def mu_pp(obj) + s = obj.is_a?(Hash) ? JSON.pretty_generate(obj) : obj.inspect + s = s.encode Encoding.default_external if defined? Encoding + s + end + + def mocks_expect(*args) + RSpec::Mocks::ExampleMethods::ExpectHost.instance_method(:expect).bind(self).call(*args) + end + + def before_setup + RSpec::Mocks.setup + super + end + + def after_teardown + super + RSpec::Mocks.verify + ensure + RSpec::Mocks.teardown + end + + def fixture_document(relative_path) + site = fixture_site( + "collections" => { + "methods" => { + "output" => true, + }, + } + ) + site.read + matching_doc = site.collections["methods"].docs.find do |doc| + doc.relative_path == relative_path + end + [site, matching_doc] + end + + def fixture_site(overrides = {}) + Jekyll::Site.new(site_configuration(overrides)) + end + + def default_configuration + Marshal.load(Marshal.dump(Jekyll::Configuration::DEFAULTS)) + end + + def build_configs(overrides, base_hash = default_configuration) + Utils.deep_merge_hashes(base_hash, overrides) + end + + def site_configuration(overrides = {}) + full_overrides = build_configs(overrides, build_configs( + "destination" => dest_dir, + "incremental" => false + )) + Configuration.from(full_overrides.merge( + "source" => source_dir + )) + end + + def clear_dest + FileUtils.rm_rf(dest_dir) + FileUtils.rm_rf(source_dir(".jekyll-metadata")) + end + + def directory_with_contents(path) + FileUtils.rm_rf(path) + FileUtils.mkdir(path) + File.write("#{path}/index.html", "I was previously generated.") + end + + def with_env(key, value) + old_value = ENV[key] + ENV[key] = value + yield + ENV[key] = old_value + end + + def capture_output(level = :debug) + buffer = StringIO.new + Jekyll.logger = Logger.new(buffer) + Jekyll.logger.log_level = level + yield + buffer.rewind + buffer.string.to_s + ensure + Jekyll.logger = Logger.new(StringIO.new, :error) + end + alias_method :capture_stdout, :capture_output + alias_method :capture_stderr, :capture_output + + def nokogiri_fragment(str) + Nokogiri::HTML.fragment( + str + ) + end + + def skip_if_windows(msg = nil) + if Utils::Platforms.really_windows? + msg ||= "Jekyll does not currently support this feature on Windows." + skip msg.to_s.magenta + end + end + + def symlink_if_allowed(target, sym_file) + FileUtils.ln_sf(target, sym_file) + rescue Errno::EACCES + skip "Permission denied for creating a symlink to #{target.inspect} " \ + "on this machine".magenta + rescue NotImplementedError => e + skip e.to_s.magenta + 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, ""]], + :MimeTypesCharset => Jekyll::Commands::Serve.send(:mime_types_charset), + :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/safe_glob_test[/find_me.txt b/test/safe_glob_test[/find_me.txt new file mode 100644 index 0000000..e69de29 diff --git a/test/simplecov_custom_profile.rb b/test/simplecov_custom_profile.rb new file mode 100644 index 0000000..555acec --- /dev/null +++ b/test/simplecov_custom_profile.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +require "simplecov" + +SimpleCov.profiles.define "gem" do + add_filter "/test/" + add_filter "/features/" + add_filter "/autotest/" + + add_group "Binaries", "/bin/" + add_group "Libraries", "/lib/" +end diff --git a/test/source/+/%# +.md b/test/source/+/%# +.md new file mode 100644 index 0000000..18e9ccc --- /dev/null +++ b/test/source/+/%# +.md @@ -0,0 +1,6 @@ +--- +layout: default +title : Page name with non-alphabetic character +--- +Line 1 +{{ page.title }} diff --git a/test/source/+/foo.md b/test/source/+/foo.md new file mode 100644 index 0000000..bd2d148 --- /dev/null +++ b/test/source/+/foo.md @@ -0,0 +1,7 @@ +--- +layout: default +title : Page inside + +permalink: /+/plus+in+url.html +--- +Line 1 +{{ page.title }} diff --git a/test/source/.gitattributes b/test/source/.gitattributes new file mode 100644 index 0000000..fa1385d --- /dev/null +++ b/test/source/.gitattributes @@ -0,0 +1 @@ +* -text diff --git a/test/source/.htaccess b/test/source/.htaccess new file mode 100644 index 0000000..294fb14 --- /dev/null +++ b/test/source/.htaccess @@ -0,0 +1,8 @@ +--- +layout: nil +--- +ErrorDocument 404 /404.html +ErrorDocument 500 /500.html +{% for post in site.posts %} + # {{ post.url }} +{% endfor %} \ No newline at end of file diff --git a/test/source/_broken/bad_post.md b/test/source/_broken/bad_post.md new file mode 100644 index 0000000..f895dd2 --- /dev/null +++ b/test/source/_broken/bad_post.md @@ -0,0 +1,4 @@ +--- +bad yaml: [ +--- +Real content starts here diff --git a/test/source/_config.dev.toml b/test/source/_config.dev.toml new file mode 100644 index 0000000..7ac863f --- /dev/null +++ b/test/source/_config.dev.toml @@ -0,0 +1,2 @@ +baseurl = "/you-beautiful-blog-you" +title = "My magnificent site, wut" diff --git a/test/source/_config_folded.yml b/test/source/_config_folded.yml new file mode 100644 index 0000000..9b0bfa4 --- /dev/null +++ b/test/source/_config_folded.yml @@ -0,0 +1,8 @@ +folded_string: > + This string of text will ignore + newlines till the next key. +foo: bar +clean_folded_string: >- + This string of text will ignore + newlines till the next key. +baz: foo diff --git a/test/source/_data/categories.01/dairy.yaml b/test/source/_data/categories.01/dairy.yaml new file mode 100644 index 0000000..30a09ba --- /dev/null +++ b/test/source/_data/categories.01/dairy.yaml @@ -0,0 +1,6 @@ +name: Dairy +products: +- name: cheese + price: 5.5 +- name: milk + price: 2.75 diff --git a/test/source/_data/categories/dairy.yaml b/test/source/_data/categories/dairy.yaml new file mode 100644 index 0000000..c091a68 --- /dev/null +++ b/test/source/_data/categories/dairy.yaml @@ -0,0 +1,6 @@ +name: Dairy +products: +- name: cheese + price: 5.3 +- name: milk + price: 2.5 \ No newline at end of file diff --git a/test/source/_data/greetings.yml b/test/source/_data/greetings.yml new file mode 100644 index 0000000..07d4750 --- /dev/null +++ b/test/source/_data/greetings.yml @@ -0,0 +1 @@ +foo: "Hello! I’m foo. And who are you?" diff --git a/test/source/_data/i18n.yml b/test/source/_data/i18n.yml new file mode 100644 index 0000000..2c76932 --- /dev/null +++ b/test/source/_data/i18n.yml @@ -0,0 +1,3 @@ +testimonials: + header: Kundenstimmen + # footer omitted by design diff --git a/test/source/_data/languages.yml b/test/source/_data/languages.yml new file mode 100644 index 0000000..6b0a250 --- /dev/null +++ b/test/source/_data/languages.yml @@ -0,0 +1,2 @@ +- java +- ruby diff --git a/test/source/_data/members.json b/test/source/_data/members.json new file mode 100644 index 0000000..5ca4821 --- /dev/null +++ b/test/source/_data/members.json @@ -0,0 +1,12 @@ +[ + { + "name": "Jack", + "age": 27, + "blog": "http://example.com/jack" + }, + { + "name": "John", + "age": 32, + "blog": "http://example.com/john" + } +] diff --git a/test/source/_data/members.yaml b/test/source/_data/members.yaml new file mode 100644 index 0000000..22e29a9 --- /dev/null +++ b/test/source/_data/members.yaml @@ -0,0 +1,7 @@ +- name: Jack + age: 27 + blog: http://example.com/jack + +- name: John + age: 32 + blog: http://example.com/john diff --git a/test/source/_data/products.yml b/test/source/_data/products.yml new file mode 100644 index 0000000..bc0c685 --- /dev/null +++ b/test/source/_data/products.yml @@ -0,0 +1 @@ +../products.yml \ No newline at end of file diff --git a/test/source/_dates/date_without_time.md b/test/source/_dates/date_without_time.md new file mode 100644 index 0000000..7e5ee00 --- /dev/null +++ b/test/source/_dates/date_without_time.md @@ -0,0 +1,4 @@ +--- +date: 2015-10-01 +--- +Here is the content. diff --git a/test/source/_dates/time_with_timezone.md b/test/source/_dates/time_with_timezone.md new file mode 100644 index 0000000..5a7c927 --- /dev/null +++ b/test/source/_dates/time_with_timezone.md @@ -0,0 +1,4 @@ +--- +date: 2015-10-01 01:00:00 +0800 +--- +Here is the content. diff --git a/test/source/_dates/time_without_timezone.md b/test/source/_dates/time_without_timezone.md new file mode 100644 index 0000000..ae0423e --- /dev/null +++ b/test/source/_dates/time_without_timezone.md @@ -0,0 +1,4 @@ +--- +date: 2015-10-01 01:00:00 +--- +Here is the content. diff --git a/test/source/_drafts/draft-properties.text b/test/source/_drafts/draft-properties.text new file mode 100644 index 0000000..da33d07 --- /dev/null +++ b/test/source/_drafts/draft-properties.text @@ -0,0 +1,11 @@ +--- +categories: foo bar baz +foo: bar +layout: default +tags: ay bee cee +title: Properties Draft +--- + +All the properties. + +Plus an excerpt. diff --git a/test/source/_encodings/UTF8CRLFandBOM.md b/test/source/_encodings/UTF8CRLFandBOM.md new file mode 100644 index 0000000..36390cc --- /dev/null +++ b/test/source/_encodings/UTF8CRLFandBOM.md @@ -0,0 +1,11 @@ +--- +layout: post +title: "UTF8CRLFandBOM" +date: 2017-04-05 16:16:01 -0800 +categories: bom +--- +This file was created with CR/LFs, and encoded as UTF8 with a BOM + +You’ll find this post in your `_posts` directory. Go ahead and edit it and re-build the site to see your changes. You can rebuild the site in many different ways, but the most common way is to run `bundle exec jekyll serve`, which launches a web server and auto-regenerates your site when a file is updated. + +To add new posts, simply add a file in the `_posts` directory that follows the convention `YYYY-MM-DD-name-of-post.ext` and includes the necessary front matter. Take a look at the source for this post to get an idea about how it works. diff --git a/test/source/_encodings/Unicode16LECRLFandBOM.md b/test/source/_encodings/Unicode16LECRLFandBOM.md new file mode 100644 index 0000000000000000000000000000000000000000..8941716a1d06a4095648425f505cda26421ffa09 GIT binary patch literal 1556 zcmezWPnQ7%c^SAEau^aBDjD(_N*PKRtQZs+3K;SkiWy26KyoDvnG7WiISi>_83hI< zhERq)hD?TJhJ1z;hExVa1~Ud{h9CwX1~-O8hCGH81}6r8244mxu<jIwM1~T$UIhjt z1_K5|26F~o1_K5YFlox505-#l!GOVn0Yn-yD1c=w7z`K;z@{WaO-X0SXDDLGWJqNw z2D>JSA)g@^tODc{T`((zA%h{4p_oB|A&nsu>}~~ya)v~(NHRkaLn_p)6b6tu$Zr`8 z3Sb}TgZ)s<pu?a5^@##QDnlOJryv~=b5KJ-fgzJ2k3oUKk0BrI*8+w_h7_<V<zTlY zGk|P>xCvb+DD*(~`G7+i63z+?uHbOY1iJ#^7LchaV1FbsC_qD55A0`9JVk=zPf{U= zAqVPrP+TZ5lz`m;3J+MU!rTjq@gfEVh6ILqc$^nABrqs2q%dTH!z!7f1RUm-40;R- z4DJm1;IPR6hax0AAhx7}b(AoG%m$H=l%N1MLzf|mp%fe!ARQq0q%tTl6oY*XVu5S` z@lzQ<JcO%2;RZ4%9UR{v`yqZ*U;w2DP>6$K6TjJzn8^i)WF^#JX$)x$sbH7oF@W3( zN*$F9ka!1~1`>n0G8dW?Kz4%C8i)_F8KMW|TTsYCd{D$t3Qh+JVE=-`1(a(*X{drB zl_8lyfgy__l_8s<k^vG&#b8&IF{CmiV5AmMjs%$o3IkAT2c-f~dQD|W0*4*Q{UDv7 z*aL+TC}b1C`8S_I7wk7ssO5oefaE}sI}u@v2z^i*1*LL`4TwAgNw1|01q`q}oWg)e zZ6VN<kir168Dvj6Qn>*MOOPG83<V5144{yQq+U#0K%of>ZDP_PBt?V#1j^z0;IPbR zC<o^Ym^q-bDUSgZKA;%RWXOlcOC$pX>N5B;_%i4+xG=ad=rZInBr@bOq%!C-<TIo( z=)!Y?9z!Za1w#oqKfuBml%A3qau`a%WgjR^VRnMTAQ_xeiy0CbiokwOV<-aK4N6g< zG*|*IABw<vDTE=BAsd|HLAg1HA)g_iAsd{#K&oM;7K3wj5d+9%h<`yb4w8k$4<b)P v(jX|-LHa@N1*J|<2@NtokpWazg35OVh71N!c!6X=D$Bw7JDZ`HL5~3d$kGZu literal 0 HcmV?d00001 diff --git a/test/source/_glob_include_test/_is_dir/include_me.txt b/test/source/_glob_include_test/_is_dir/include_me.txt new file mode 100644 index 0000000..e69de29 diff --git a/test/source/_glob_include_test/_not_dir b/test/source/_glob_include_test/_not_dir new file mode 100644 index 0000000..e69de29 diff --git a/test/source/_includes/include.html b/test/source/_includes/include.html new file mode 100644 index 0000000..201a102 --- /dev/null +++ b/test/source/_includes/include.html @@ -0,0 +1 @@ +included diff --git a/test/source/_includes/params.html b/test/source/_includes/params.html new file mode 100644 index 0000000..65a0001 --- /dev/null +++ b/test/source/_includes/params.html @@ -0,0 +1,7 @@ +<span id='include-param'>{{include.param}}</span> + +<ul id='param-list'> + {% for param in include %} + <li>{{param[0]}} = {{param[1]}}</li> + {% endfor %} +</ul> \ No newline at end of file diff --git a/test/source/_includes/params@2.0.html b/test/source/_includes/params@2.0.html new file mode 100644 index 0000000..65a0001 --- /dev/null +++ b/test/source/_includes/params@2.0.html @@ -0,0 +1,7 @@ +<span id='include-param'>{{include.param}}</span> + +<ul id='param-list'> + {% for param in include %} + <li>{{param[0]}} = {{param[1]}}</li> + {% endfor %} +</ul> \ No newline at end of file diff --git a/test/source/_includes/sig.markdown b/test/source/_includes/sig.markdown new file mode 100644 index 0000000..fbbf563 --- /dev/null +++ b/test/source/_includes/sig.markdown @@ -0,0 +1,3 @@ +--- +Tom Preston-Werner +github.com/mojombo diff --git a/test/source/_includes/tmp b/test/source/_includes/tmp new file mode 100644 index 0000000..a846f50 --- /dev/null +++ b/test/source/_includes/tmp @@ -0,0 +1 @@ +../../../tmp/ \ No newline at end of file diff --git a/test/source/_includes_custom/custom.html b/test/source/_includes_custom/custom.html new file mode 100644 index 0000000..cbf553c --- /dev/null +++ b/test/source/_includes_custom/custom.html @@ -0,0 +1 @@ +custom_included \ No newline at end of file diff --git a/test/source/_layouts/default.html b/test/source/_layouts/default.html new file mode 100644 index 0000000..ef2d438 --- /dev/null +++ b/test/source/_layouts/default.html @@ -0,0 +1,30 @@ +--- +front_matter_var: variable from layout +--- +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> + +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en-us"> +<head> + <meta http-equiv="content-type" content="text/html; charset=utf-8" /> + <title>{{ page.title }}</title> + <meta name="author" content="<%= @page.author %>" /> + + <!-- CodeRay syntax highlighting CSS --> + <link rel="stylesheet" href="/css/coderay.css" type="text/css" /> + + <!-- Homepage CSS --> + <link rel="stylesheet" href="/css/screen.css" type="text/css" media="screen, projection" /> +</head> +<body> + +<div class="site"> + <div class="title"> + Tom Preston-Werner + </div> + + {{ content }} +</div> + +</body> +</html> diff --git a/test/source/_layouts/post/simple.html b/test/source/_layouts/post/simple.html new file mode 100644 index 0000000..cd4f599 --- /dev/null +++ b/test/source/_layouts/post/simple.html @@ -0,0 +1 @@ +<<< {{ content }} >>> diff --git a/test/source/_layouts/simple.html b/test/source/_layouts/simple.html new file mode 100644 index 0000000..16e9ac2 --- /dev/null +++ b/test/source/_layouts/simple.html @@ -0,0 +1 @@ +<<< {{ content }} >>> \ No newline at end of file diff --git a/test/source/_methods/3940394-21-9393050-fifif1323-test.md b/test/source/_methods/3940394-21-9393050-fifif1323-test.md new file mode 100644 index 0000000..848d020 --- /dev/null +++ b/test/source/_methods/3940394-21-9393050-fifif1323-test.md @@ -0,0 +1,5 @@ +--- +title: "this is a test!" +--- + +wheee diff --git a/test/source/_methods/_do_not_read_me.md b/test/source/_methods/_do_not_read_me.md new file mode 100644 index 0000000..1b5ad07 --- /dev/null +++ b/test/source/_methods/_do_not_read_me.md @@ -0,0 +1,5 @@ +--- +title: The unreadable wonder +--- + +Don't read me, you fool! FILTER ME diff --git a/test/source/_methods/collection/entries b/test/source/_methods/collection/entries new file mode 100644 index 0000000..7622ac9 --- /dev/null +++ b/test/source/_methods/collection/entries @@ -0,0 +1,5 @@ +--- +title: "Collection#entries" +--- + +I have no file extension but I should still be a part of the collection. diff --git a/test/source/_methods/configuration.md b/test/source/_methods/configuration.md new file mode 100644 index 0000000..fd17980 --- /dev/null +++ b/test/source/_methods/configuration.md @@ -0,0 +1,8 @@ +--- +title: "Jekyll.configuration" +whatever: foo.bar +--- + +Use `{{ page.title }}` to build a full configuration for use w/Jekyll. + +Whatever: {{ page.whatever }} diff --git a/test/source/_methods/escape-+ #%20[].md b/test/source/_methods/escape-+ #%20[].md new file mode 100644 index 0000000..528317a --- /dev/null +++ b/test/source/_methods/escape-+ #%20[].md @@ -0,0 +1,5 @@ +--- +title: "Jekyll.escape" +--- + +Signs are nice diff --git a/test/source/_methods/extensionless_static_file b/test/source/_methods/extensionless_static_file new file mode 100644 index 0000000..b050c59 --- /dev/null +++ b/test/source/_methods/extensionless_static_file @@ -0,0 +1 @@ +I have no front matter. diff --git a/test/source/_methods/sanitized_path.md b/test/source/_methods/sanitized_path.md new file mode 100644 index 0000000..8b4d767 --- /dev/null +++ b/test/source/_methods/sanitized_path.md @@ -0,0 +1,5 @@ +--- +title: "Jekyll.sanitized_path" +--- + +`{{ page.title }}` is used to make sure your path is in your source. diff --git a/test/source/_methods/site/_dont_include_me_either.md b/test/source/_methods/site/_dont_include_me_either.md new file mode 100644 index 0000000..6607961 --- /dev/null +++ b/test/source/_methods/site/_dont_include_me_either.md @@ -0,0 +1,5 @@ +--- +title: Don't Include Me Either +--- + +Don't include me either. FILTER ME PLZ diff --git a/test/source/_methods/site/generate.md b/test/source/_methods/site/generate.md new file mode 100644 index 0000000..1cab376 --- /dev/null +++ b/test/source/_methods/site/generate.md @@ -0,0 +1,6 @@ +--- +title: "Site#generate" +layout: default +--- + +Run your generators! {{ page.layout }} diff --git a/test/source/_methods/site/initialize.md b/test/source/_methods/site/initialize.md new file mode 100644 index 0000000..af5641c --- /dev/null +++ b/test/source/_methods/site/initialize.md @@ -0,0 +1,4 @@ +--- +--- + +Page without title. diff --git a/test/source/_methods/trailing-dots...md b/test/source/_methods/trailing-dots...md new file mode 100644 index 0000000..2ec72a7 --- /dev/null +++ b/test/source/_methods/trailing-dots...md @@ -0,0 +1,3 @@ +--- +title: Ellipsis Path +--- diff --git a/test/source/_methods/um_hi.md b/test/source/_methods/um_hi.md new file mode 100644 index 0000000..9ebb532 --- /dev/null +++ b/test/source/_methods/um_hi.md @@ -0,0 +1 @@ +./site/generate.md \ No newline at end of file diff --git a/test/source/_methods/with.dots/.gitignore b/test/source/_methods/with.dots/.gitignore new file mode 100644 index 0000000..87e42cf --- /dev/null +++ b/test/source/_methods/with.dots/.gitignore @@ -0,0 +1 @@ +I should be copied diff --git a/test/source/_methods/with.dots/.htaccess b/test/source/_methods/with.dots/.htaccess new file mode 100644 index 0000000..87e42cf --- /dev/null +++ b/test/source/_methods/with.dots/.htaccess @@ -0,0 +1 @@ +I should be copied diff --git a/test/source/_methods/yaml_with_dots.md b/test/source/_methods/yaml_with_dots.md new file mode 100644 index 0000000..eb0b667 --- /dev/null +++ b/test/source/_methods/yaml_with_dots.md @@ -0,0 +1,8 @@ +--- +title: "YAML with Dots" +whatever: foo.bar +... + +Use `{{ page.title }}` to build a full configuration for use w/Jekyll. + +Whatever: {{ page.whatever }} diff --git a/test/source/_plugins/custom_block.rb b/test/source/_plugins/custom_block.rb new file mode 100644 index 0000000..eee0fba --- /dev/null +++ b/test/source/_plugins/custom_block.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +# For testing excerpt handling of custom tags + +module Jekyll + class DoNothingBlock < Liquid::Block + end + + class DoNothingOther < Liquid::Tag + end +end + +Liquid::Template.register_tag("do_nothing", Jekyll::DoNothingBlock) +Liquid::Template.register_tag("do_nothing_other", Jekyll::DoNothingOther) diff --git a/test/source/_plugins/dummy.rb b/test/source/_plugins/dummy.rb new file mode 100644 index 0000000..25eef88 --- /dev/null +++ b/test/source/_plugins/dummy.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +module Jekyll + class Dummy < Generator + priority :high + + def generate(site) end + end +end diff --git a/test/source/_posts/2008-02-02-not-published.markdown b/test/source/_posts/2008-02-02-not-published.markdown new file mode 100644 index 0000000..5ff418d --- /dev/null +++ b/test/source/_posts/2008-02-02-not-published.markdown @@ -0,0 +1,8 @@ +--- +layout: default +title: Not published! +published: false +category: publish_test +--- + +This should *not* be published! diff --git a/test/source/_posts/2008-02-02-published.markdown b/test/source/_posts/2008-02-02-published.markdown new file mode 100644 index 0000000..5b2428e --- /dev/null +++ b/test/source/_posts/2008-02-02-published.markdown @@ -0,0 +1,7 @@ +--- +layout: default +title: Publish +category: publish_test +--- + +This should be published. diff --git a/test/source/_posts/2008-02-03-wrong-extension.yml b/test/source/_posts/2008-02-03-wrong-extension.yml new file mode 100644 index 0000000..bb9f7f4 --- /dev/null +++ b/test/source/_posts/2008-02-03-wrong-extension.yml @@ -0,0 +1,8 @@ +--- +layout: default +title: This file extension is skipped +category: test_post_reader +--- + +`jekyll serve` used to crash if there's a post (document) with a wrong file extension. +This file is used in `./test/test_post_reader.rb` to verify that this doesn't happen anymore. diff --git a/test/source/_posts/2008-10-18-foo-bar.markdown b/test/source/_posts/2008-10-18-foo-bar.markdown new file mode 100644 index 0000000..3bc219a --- /dev/null +++ b/test/source/_posts/2008-10-18-foo-bar.markdown @@ -0,0 +1,8 @@ +--- +layout: default +title: Foo Bar +--- + +# {{ page.title }} + +Best **post** ever \ No newline at end of file diff --git a/test/source/_posts/2008-11-21-complex.markdown b/test/source/_posts/2008-11-21-complex.markdown new file mode 100644 index 0000000..24b2828 --- /dev/null +++ b/test/source/_posts/2008-11-21-complex.markdown @@ -0,0 +1,8 @@ +--- +layout: default +title: Complex +--- + +url: {{ page.url }} +date: {{ page.date }} +id: {{ page.id }} \ No newline at end of file diff --git a/test/source/_posts/2008-12-03-permalinked-post.markdown b/test/source/_posts/2008-12-03-permalinked-post.markdown new file mode 100644 index 0000000..88a89ad --- /dev/null +++ b/test/source/_posts/2008-12-03-permalinked-post.markdown @@ -0,0 +1,9 @@ +--- +title: Post with Permalink +permalink: my_category/permalinked-post +--- + +h1. {{ page.title }} + + +<p>Best <strong>post</strong> ever</p> \ No newline at end of file diff --git a/test/source/_posts/2008-12-13-include.markdown b/test/source/_posts/2008-12-13-include.markdown new file mode 100644 index 0000000..28a9353 --- /dev/null +++ b/test/source/_posts/2008-12-13-include.markdown @@ -0,0 +1,8 @@ +--- +layout: default +title: Include +--- + +{% include sig.markdown %} + +This _is_ cool diff --git a/test/source/_posts/2009-01-27-array-categories.markdown b/test/source/_posts/2009-01-27-array-categories.markdown new file mode 100644 index 0000000..575843d --- /dev/null +++ b/test/source/_posts/2009-01-27-array-categories.markdown @@ -0,0 +1,10 @@ +--- +layout: default +title: Array categories in YAML +categories: +- foo +- bar +- baz +--- + +Best *post* ever diff --git a/test/source/_posts/2009-01-27-categories.markdown b/test/source/_posts/2009-01-27-categories.markdown new file mode 100644 index 0000000..189fe29 --- /dev/null +++ b/test/source/_posts/2009-01-27-categories.markdown @@ -0,0 +1,7 @@ +--- +layout: default +title: Categories in YAML +categories: foo bar baz +--- + +Best *post* ever diff --git a/test/source/_posts/2009-01-27-category.markdown b/test/source/_posts/2009-01-27-category.markdown new file mode 100644 index 0000000..2881e4c --- /dev/null +++ b/test/source/_posts/2009-01-27-category.markdown @@ -0,0 +1,7 @@ +--- +layout: default +title: Category in YAML +category: foo +--- + +Best *post* ever diff --git a/test/source/_posts/2009-01-27-empty-categories.markdown b/test/source/_posts/2009-01-27-empty-categories.markdown new file mode 100644 index 0000000..6e784cc --- /dev/null +++ b/test/source/_posts/2009-01-27-empty-categories.markdown @@ -0,0 +1,7 @@ +--- +layout: default +title: Category in YAML +categories: +--- + +Best *post* ever diff --git a/test/source/_posts/2009-01-27-empty-category.markdown b/test/source/_posts/2009-01-27-empty-category.markdown new file mode 100644 index 0000000..7e8b1b3 --- /dev/null +++ b/test/source/_posts/2009-01-27-empty-category.markdown @@ -0,0 +1,7 @@ +--- +layout: default +title: Category in YAML +category: +--- + +Best *post* ever diff --git a/test/source/_posts/2009-01-27-no-category.markdown b/test/source/_posts/2009-01-27-no-category.markdown new file mode 100644 index 0000000..af8382d --- /dev/null +++ b/test/source/_posts/2009-01-27-no-category.markdown @@ -0,0 +1,6 @@ +--- +layout: default +title: Category in YAML +--- + +Best *post* ever diff --git a/test/source/_posts/2009-03-12-hash-#1.markdown b/test/source/_posts/2009-03-12-hash-#1.markdown new file mode 100644 index 0000000..c6e8851 --- /dev/null +++ b/test/source/_posts/2009-03-12-hash-#1.markdown @@ -0,0 +1,6 @@ +--- +layout: default +title: Hash #1 +--- + +Hashes are nice diff --git a/test/source/_posts/2009-05-18-empty-tag.markdown b/test/source/_posts/2009-05-18-empty-tag.markdown new file mode 100644 index 0000000..1f103f6 --- /dev/null +++ b/test/source/_posts/2009-05-18-empty-tag.markdown @@ -0,0 +1,6 @@ +--- +title: A Tag +tag: +--- + +Whoa. diff --git a/test/source/_posts/2009-05-18-empty-tags.markdown b/test/source/_posts/2009-05-18-empty-tags.markdown new file mode 100644 index 0000000..5339d15 --- /dev/null +++ b/test/source/_posts/2009-05-18-empty-tags.markdown @@ -0,0 +1,6 @@ +--- +title: Some Tags +tags: +--- + +Awesome! diff --git a/test/source/_posts/2009-05-18-tag.markdown b/test/source/_posts/2009-05-18-tag.markdown new file mode 100644 index 0000000..349a009 --- /dev/null +++ b/test/source/_posts/2009-05-18-tag.markdown @@ -0,0 +1,6 @@ +--- +title: A Tag +tag: code +--- + +Whoa. diff --git a/test/source/_posts/2009-05-18-tags.markdown b/test/source/_posts/2009-05-18-tags.markdown new file mode 100644 index 0000000..d646ad2 --- /dev/null +++ b/test/source/_posts/2009-05-18-tags.markdown @@ -0,0 +1,9 @@ +--- +title: Some Tags +tags: +- food +- cooking +- pizza +--- + +Awesome! diff --git a/test/source/_posts/2009-06-22-empty-yaml.markdown b/test/source/_posts/2009-06-22-empty-yaml.markdown new file mode 100644 index 0000000..5b0e74b --- /dev/null +++ b/test/source/_posts/2009-06-22-empty-yaml.markdown @@ -0,0 +1,3 @@ +--- +--- +Empty YAML. \ No newline at end of file diff --git a/test/source/_posts/2009-06-22-no-yaml.markdown b/test/source/_posts/2009-06-22-no-yaml.markdown new file mode 100644 index 0000000..b0ce4d3 --- /dev/null +++ b/test/source/_posts/2009-06-22-no-yaml.markdown @@ -0,0 +1 @@ +No YAML. \ No newline at end of file diff --git a/test/source/_posts/2010-01-08-triple-dash.markdown b/test/source/_posts/2010-01-08-triple-dash.markdown new file mode 100644 index 0000000..cbb79e7 --- /dev/null +++ b/test/source/_posts/2010-01-08-triple-dash.markdown @@ -0,0 +1,5 @@ +--- +title: Foo --- Bar +--- + +Triple the fun! \ No newline at end of file diff --git a/test/source/_posts/2010-01-09-date-override.markdown b/test/source/_posts/2010-01-09-date-override.markdown new file mode 100644 index 0000000..45b3834 --- /dev/null +++ b/test/source/_posts/2010-01-09-date-override.markdown @@ -0,0 +1,7 @@ +--- +date: 2010-01-10 +--- + +Post with a front matter date + +{{ page.date | date_to_string }} diff --git a/test/source/_posts/2010-01-09-time-override.markdown b/test/source/_posts/2010-01-09-time-override.markdown new file mode 100644 index 0000000..e462c41 --- /dev/null +++ b/test/source/_posts/2010-01-09-time-override.markdown @@ -0,0 +1,7 @@ +--- +date: 2010-01-10 13:07:09 +--- + +Post with a front matter time + +{{ page.date | date_to_string }} diff --git a/test/source/_posts/2010-01-09-timezone-override.markdown b/test/source/_posts/2010-01-09-timezone-override.markdown new file mode 100644 index 0000000..3dc0e4f --- /dev/null +++ b/test/source/_posts/2010-01-09-timezone-override.markdown @@ -0,0 +1,7 @@ +--- +date: 2010-01-10 13:07:09 +00:00 +--- + +Post with a front matter time with timezone + +{{ page.date | date_to_string }} diff --git a/test/source/_posts/2010-01-16-override-data.markdown b/test/source/_posts/2010-01-16-override-data.markdown new file mode 100644 index 0000000..e510648 --- /dev/null +++ b/test/source/_posts/2010-01-16-override-data.markdown @@ -0,0 +1,6 @@ +--- +date: 2010-01-10 13:07:09 +tags: A string +--- + +Best **post** ever diff --git a/test/source/_posts/2011-04-12-md-extension.md b/test/source/_posts/2011-04-12-md-extension.md new file mode 100644 index 0000000..2d2ae93 --- /dev/null +++ b/test/source/_posts/2011-04-12-md-extension.md @@ -0,0 +1,7 @@ +--- +date: 2011-04-12 13:07:09 +--- + +under default configuration, this post should get processed by the identity converter. By changing +textile extension or markdown extension configuration parameters, you should be able to associate +it with either of those converters \ No newline at end of file diff --git a/test/source/_posts/2011-04-12-text-extension.text b/test/source/_posts/2011-04-12-text-extension.text new file mode 100644 index 0000000..6b4ef01 --- /dev/null +++ b/test/source/_posts/2011-04-12-text-extension.text @@ -0,0 +1 @@ +Best **post** ever diff --git a/test/source/_posts/2013-01-02-post-excerpt.markdown b/test/source/_posts/2013-01-02-post-excerpt.markdown new file mode 100644 index 0000000..17fc828 --- /dev/null +++ b/test/source/_posts/2013-01-02-post-excerpt.markdown @@ -0,0 +1,14 @@ +--- +layout: ~ +title: Post Excerpt +--- + +First paragraph with [link ref][link]. + +Second paragraph + +--- + +Third paragraph + +[link]: https://jekyllrb.com/ diff --git a/test/source/_posts/2013-01-12-nil-layout.markdown b/test/source/_posts/2013-01-12-nil-layout.markdown new file mode 100644 index 0000000..1e12fa9 --- /dev/null +++ b/test/source/_posts/2013-01-12-nil-layout.markdown @@ -0,0 +1,6 @@ +--- +layout: nil +title: No layout +--- + +This post has no layout. diff --git a/test/source/_posts/2013-01-12-no-layout.markdown b/test/source/_posts/2013-01-12-no-layout.markdown new file mode 100644 index 0000000..1c745ae --- /dev/null +++ b/test/source/_posts/2013-01-12-no-layout.markdown @@ -0,0 +1,5 @@ +--- +title: I have no layout +--- + +This post will be rendered with the "post" layout. diff --git a/test/source/_posts/2013-03-19-not-a-post.markdown/.gitkeep b/test/source/_posts/2013-03-19-not-a-post.markdown/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/test/source/_posts/2013-03-19-not-a-post/dubious.markdown b/test/source/_posts/2013-03-19-not-a-post/dubious.markdown new file mode 100644 index 0000000..cd2990d --- /dev/null +++ b/test/source/_posts/2013-03-19-not-a-post/dubious.markdown @@ -0,0 +1,6 @@ +--- +title: What Am I? +--- + +I am not a post. +Am I a document then..? diff --git a/test/source/_posts/2013-04-11-custom-excerpt.markdown b/test/source/_posts/2013-04-11-custom-excerpt.markdown new file mode 100644 index 0000000..023251f --- /dev/null +++ b/test/source/_posts/2013-04-11-custom-excerpt.markdown @@ -0,0 +1,10 @@ +--- +layout: ~ +excerpt: 'I can set a custom excerpt' +--- + +This is not my excerpt. + +Neither is this. + +I can use the excerpt: <quote>{{page.excerpt}}</quote> \ No newline at end of file diff --git a/test/source/_posts/2013-05-10-number-category.markdown b/test/source/_posts/2013-05-10-number-category.markdown new file mode 100644 index 0000000..1a490a8 --- /dev/null +++ b/test/source/_posts/2013-05-10-number-category.markdown @@ -0,0 +1,7 @@ +--- +layout: default +title: Number Category in YAML +category: 2013 +--- + +Please make me pass \ No newline at end of file diff --git a/test/source/_posts/2013-07-22-post-excerpt-with-layout.markdown b/test/source/_posts/2013-07-22-post-excerpt-with-layout.markdown new file mode 100644 index 0000000..930cab1 --- /dev/null +++ b/test/source/_posts/2013-07-22-post-excerpt-with-layout.markdown @@ -0,0 +1,24 @@ +--- +layout: post +title: Post Excerpt with Layout +categories: +- bar +- baz +- z_category +- MixedCase +tags: +- first +- second +- third +- jekyllrb.com +--- + +First paragraph with [link ref][link]. + +Second paragraph + +--- + +Third paragraph + +[link]: https://jekyllrb.com/ diff --git a/test/source/_posts/2013-08-01-mkdn-extension.mkdn b/test/source/_posts/2013-08-01-mkdn-extension.mkdn new file mode 100644 index 0000000..6b4ef01 --- /dev/null +++ b/test/source/_posts/2013-08-01-mkdn-extension.mkdn @@ -0,0 +1 @@ +Best **post** ever diff --git a/test/source/_posts/2013-12-17-include-variable-filters.markdown b/test/source/_posts/2013-12-17-include-variable-filters.markdown new file mode 100644 index 0000000..c3291c7 --- /dev/null +++ b/test/source/_posts/2013-12-17-include-variable-filters.markdown @@ -0,0 +1,25 @@ +--- +title: Post +layout: post +include1: include.html +include2: include +include3: INCLUDE +include4: params +include5: clude +--- + +Liquid tests +- 1 {% include {{ page.include1 }} %} +- 2 {% include {{ page.include2 | append: '.html' }} %} +- 3 {% include {{ page.include3 | downcase | append: '.html' }} %} + +Whitespace tests +- 4 {% include {{page.include1}} %} +- 5 {% include {{ page.include1}} %} +- 6 {% include {{ page.include3 | downcase | append: '.html'}} %} + +Parameters test +- 7 {% include {{ page.include4 | append: '.html' }} var1='foo' var2='bar' %} + +Partial variable test +- 8 {% include in{{ page.include5 }}.html %} diff --git a/test/source/_posts/2013-12-20-properties.text b/test/source/_posts/2013-12-20-properties.text new file mode 100644 index 0000000..c0d72ce --- /dev/null +++ b/test/source/_posts/2013-12-20-properties.text @@ -0,0 +1,11 @@ +--- +categories: foo bar baz MixedCase +foo: bar +layout: default +tags: ay bee cee +title: Properties Post +--- + +All the properties. + +Plus an excerpt. diff --git a/test/source/_posts/2014-01-06-permalink-traversal.md b/test/source/_posts/2014-01-06-permalink-traversal.md new file mode 100644 index 0000000..c3f77d1 --- /dev/null +++ b/test/source/_posts/2014-01-06-permalink-traversal.md @@ -0,0 +1,5 @@ +--- +permalink: /%2e%2e/%2e%2e/%2e%2e/baddie.html +--- + +# Test diff --git a/test/source/_posts/2014-03-03-yaml-with-dots.md b/test/source/_posts/2014-03-03-yaml-with-dots.md new file mode 100644 index 0000000..09639b2 --- /dev/null +++ b/test/source/_posts/2014-03-03-yaml-with-dots.md @@ -0,0 +1,5 @@ +--- +title: Test Post Where YAML Ends in Dots +... + +# Test diff --git a/test/source/_posts/2014-03-22-escape-+ %20[].markdown b/test/source/_posts/2014-03-22-escape-+ %20[].markdown new file mode 100644 index 0000000..abe120b --- /dev/null +++ b/test/source/_posts/2014-03-22-escape-+ %20[].markdown @@ -0,0 +1,6 @@ +--- +layout: default +title: Plus space percent +--- + +Signs are nice diff --git a/test/source/_posts/2014-07-05-another-mixed-case-category.markdown b/test/source/_posts/2014-07-05-another-mixed-case-category.markdown new file mode 100644 index 0000000..419ddab --- /dev/null +++ b/test/source/_posts/2014-07-05-another-mixed-case-category.markdown @@ -0,0 +1,7 @@ +--- +layout: default +title: Another Mixed Case Category in YAML +category: Mixedcase +--- + +Best *post* ever diff --git a/test/source/_posts/2014-07-05-mixed-case-category.markdown b/test/source/_posts/2014-07-05-mixed-case-category.markdown new file mode 100644 index 0000000..92a6790 --- /dev/null +++ b/test/source/_posts/2014-07-05-mixed-case-category.markdown @@ -0,0 +1,7 @@ +--- +layout: default +title: Mixed Case Category in YAML +category: MixedCase +--- + +Best *post* ever diff --git a/test/source/_posts/2014-09-02-relative-includes.markdown b/test/source/_posts/2014-09-02-relative-includes.markdown new file mode 100644 index 0000000..ab451b6 --- /dev/null +++ b/test/source/_posts/2014-09-02-relative-includes.markdown @@ -0,0 +1,29 @@ +--- +title: Post +layout: post +include1: rel_include.html +include2: include_relative/rel_include +include3: rel_INCLUDE +include4: params +include5: clude +--- + +Liquid tests +- 1 {% include_relative include_relative/{{ page.include1 }} %} +- 2 {% include_relative {{ page.include2 | append: '.html' }} %} +- 3 {% include_relative include_relative/{{ page.include3 | downcase | append: '.html' }} %} + +Whitespace tests +- 4 {% include_relative include_relative/{{page.include1}} %} +- 5 {% include_relative include_relative/{{ page.include1}} %} +- 6 {% include_relative include_relative/{{ page.include3 | downcase | append: '.html'}} %} + +Parameters test +- 7 {% include_relative include_relative/{{ page.include4 | append: '.html' }} var1='foo' var2='bar' %} + +Partial variable test +- 8 {% include_relative include_relative/rel_in{{ page.include5 }}.html %} + +Relative to self test: + +- 9 {% include_relative 2014-03-03-yaml-with-dots.md %} diff --git a/test/source/_posts/2014-11-24-Rmd-extension.Rmd b/test/source/_posts/2014-11-24-Rmd-extension.Rmd new file mode 100644 index 0000000..6b4ef01 --- /dev/null +++ b/test/source/_posts/2014-11-24-Rmd-extension.Rmd @@ -0,0 +1 @@ +Best **post** ever diff --git a/test/source/_posts/2015-01-08-post-excerpt-separator.markdown b/test/source/_posts/2015-01-08-post-excerpt-separator.markdown new file mode 100644 index 0000000..9b94c6d --- /dev/null +++ b/test/source/_posts/2015-01-08-post-excerpt-separator.markdown @@ -0,0 +1,15 @@ +--- +layout: ~ +title: Post Excerpt Separator +excerpt_separator: "\n---\n" +--- + +First paragraph with [link ref][link]. + +Second paragraph + +--- + +Third paragraph + +[link]: https://jekyllrb.com/ diff --git a/test/source/_posts/2015-02-20-extensionless-permalink.markdown b/test/source/_posts/2015-02-20-extensionless-permalink.markdown new file mode 100644 index 0000000..f92b21f --- /dev/null +++ b/test/source/_posts/2015-02-20-extensionless-permalink.markdown @@ -0,0 +1,7 @@ +--- +layout: ~ +title: Extensionless Permalink +permalink: /:title +--- + +{{ page.url }} diff --git a/test/source/_posts/2015-12-27-extra-spaces.markdown b/test/source/_posts/2015-12-27-extra-spaces.markdown new file mode 100644 index 0000000..d8f785b --- /dev/null +++ b/test/source/_posts/2015-12-27-extra-spaces.markdown @@ -0,0 +1,5 @@ +--- +extra: spaces +--- + +Best **post** ever diff --git a/test/source/_posts/2016-08-16-indented-link-references.markdown b/test/source/_posts/2016-08-16-indented-link-references.markdown new file mode 100644 index 0000000..f536616 --- /dev/null +++ b/test/source/_posts/2016-08-16-indented-link-references.markdown @@ -0,0 +1,16 @@ +--- +--- + +This is the first paragraph. It [has][link_0] [lots][link_1] [of][link_2] +[links][link_3]. + +This is the second paragraph. It has sample code that should not be extracted: + + [fakelink]: www.invalid.com + +And here are the real links: + +[link_0]: www.example.com/0 + [link_1]: www.example.com/1 + [link_2]: www.example.com/2 + [link_3]: www.example.com/3 diff --git a/test/source/_posts/2016-11-26-special-chars-(+).markdown b/test/source/_posts/2016-11-26-special-chars-(+).markdown new file mode 100644 index 0000000..21cc771 --- /dev/null +++ b/test/source/_posts/2016-11-26-special-chars-(+).markdown @@ -0,0 +1,8 @@ +--- +layout: default +title: Special Characters +--- + +url: {{ page.url }} +date: {{ page.date }} +id: {{ page.id }} \ No newline at end of file diff --git a/test/source/_posts/2017-2-5-i-dont-like-zeroes.md b/test/source/_posts/2017-2-5-i-dont-like-zeroes.md new file mode 100644 index 0000000..95e11f1 --- /dev/null +++ b/test/source/_posts/2017-2-5-i-dont-like-zeroes.md @@ -0,0 +1,5 @@ +--- +foo: bar +--- +I have an abbreviated date. Instead of "2017-02-05", I am instead "2017-2-5". +Zeros have always seemed superfluous. diff --git a/test/source/_posts/2018-01-28-closed-liquid-block-excerpt.markdown b/test/source/_posts/2018-01-28-closed-liquid-block-excerpt.markdown new file mode 100644 index 0000000..a2c9fe5 --- /dev/null +++ b/test/source/_posts/2018-01-28-closed-liquid-block-excerpt.markdown @@ -0,0 +1,24 @@ +--- +layout: post +--- + +{% + highlight + ruby +%} +{% assign foo = 'foobar' %} +{% raw +%} +def print_hi(name) + puts "Hi, #{name}" +end +print_hi('Tom') +#=> prints 'Hi, Tom' to STDOUT. +{% + endraw +%} +{% + endhighlight +%} + +So let's talk business. diff --git a/test/source/_posts/2018-01-28-open-liquid-block-excerpt.markdown b/test/source/_posts/2018-01-28-open-liquid-block-excerpt.markdown new file mode 100644 index 0000000..24c2e0e --- /dev/null +++ b/test/source/_posts/2018-01-28-open-liquid-block-excerpt.markdown @@ -0,0 +1,21 @@ +--- +layout: post +--- + +{% + highlight + ruby +%} +{% assign foo = 'foobar' %} +{% raw +%} +def print_hi(name) + puts "Hi, #{name}" +end + +print_hi('Tom') +#=> prints 'Hi, Tom' to STDOUT. +{% endraw %} +{% endhighlight %} + +So let's talk business. diff --git a/test/source/_posts/2018-05-15-closed-liquid-block-excerpt-whitespace-control.md b/test/source/_posts/2018-05-15-closed-liquid-block-excerpt-whitespace-control.md new file mode 100644 index 0000000..c435592 --- /dev/null +++ b/test/source/_posts/2018-05-15-closed-liquid-block-excerpt-whitespace-control.md @@ -0,0 +1,9 @@ +--- +title: LIQUID_TAG_REGEX excerpt whitespace control test +layout: post +--- + +{%- for post in site.posts -%} + You are in a maze of twisty little passages, all alike. + There's lots more to say about this, but that's enough for now. +{%- endfor -%} diff --git a/test/source/_posts/2018-05-15-excerpt-whitespace-control-variable.md b/test/source/_posts/2018-05-15-excerpt-whitespace-control-variable.md new file mode 100644 index 0000000..aad1207 --- /dev/null +++ b/test/source/_posts/2018-05-15-excerpt-whitespace-control-variable.md @@ -0,0 +1,7 @@ +--- +title: LIQUID_TAG_REGEX excerpt whitespace control test +layout: post +--- + +{%- assign xyzzy = 'You are in a maze of twisty little passages, all alike.' %} +{{- xyzzy -}} diff --git a/test/source/_posts/2018-05-15-open-liquid-block-excerpt-whitespace-control.md b/test/source/_posts/2018-05-15-open-liquid-block-excerpt-whitespace-control.md new file mode 100644 index 0000000..4ed7995 --- /dev/null +++ b/test/source/_posts/2018-05-15-open-liquid-block-excerpt-whitespace-control.md @@ -0,0 +1,10 @@ +--- +title: LIQUID_TAG_REGEX excerpt whitespace control test +layout: post +--- + +{%- for post in site.posts -%} + You are in a maze of twisty little passages, all alike. + + There's lots more to say about this, but that's enough for now. +{%- endfor -%} diff --git a/test/source/_posts/2018-10-12-trailing-dots...markdown b/test/source/_posts/2018-10-12-trailing-dots...markdown new file mode 100644 index 0000000..ceaaa33 --- /dev/null +++ b/test/source/_posts/2018-10-12-trailing-dots...markdown @@ -0,0 +1,5 @@ +--- +title: Ellipsis Path +--- + +Lorem ipsum dolor sit amet diff --git a/test/source/_posts/2018-11-15-excerpt-liquid-block.md b/test/source/_posts/2018-11-15-excerpt-liquid-block.md new file mode 100644 index 0000000..7995d9d --- /dev/null +++ b/test/source/_posts/2018-11-15-excerpt-liquid-block.md @@ -0,0 +1,29 @@ +--- +title: liquid_block excerpt test with open tags in excerpt +layout: post +--- + +{% assign company = "Yoyodyne" %} +{% do_nothing_other %} +{% do_nothing %} + {% unless false %} + {% for i in (1..10) %} + {% if true %} + {% raw %} + EVIL! PURE AND SIMPLE FROM THE EIGHTH DIMENSION! + {% endraw %} + {% elsif false %} + No matter where you go, there you are. + {% break %} + {% else %} + {% case company %} + {% when "Yoyodyne" %} + Buckaroo Banzai + {% else %} + {% continue %} + + {% endcase %} + {% endif %} + {% endfor %} + {% endunless %} +{% enddo_nothing %} diff --git a/test/source/_posts/es/2008-11-21-nested.markdown b/test/source/_posts/es/2008-11-21-nested.markdown new file mode 100644 index 0000000..a0209ab --- /dev/null +++ b/test/source/_posts/es/2008-11-21-nested.markdown @@ -0,0 +1,8 @@ +--- +layout: default +title: Nested +--- + +url: {{ page.url }} +date: {{ page.date }} +id: {{ page.id }} diff --git a/test/source/_posts/include_relative/params.html b/test/source/_posts/include_relative/params.html new file mode 100644 index 0000000..ae4c6cf --- /dev/null +++ b/test/source/_posts/include_relative/params.html @@ -0,0 +1,7 @@ +<span id='include-param'>{{include.param}}</span> + +<ul id='param-list'> + {% for param in include %} + <li>{{param[0]}} = {{param[1]}}</li> + {% endfor %} +</ul> diff --git a/test/source/_posts/include_relative/rel_include.html b/test/source/_posts/include_relative/rel_include.html new file mode 100644 index 0000000..5fb677b --- /dev/null +++ b/test/source/_posts/include_relative/rel_include.html @@ -0,0 +1 @@ +relative_included diff --git a/test/source/_roles/named.md b/test/source/_roles/named.md new file mode 100644 index 0000000..dfb6847 --- /dev/null +++ b/test/source/_roles/named.md @@ -0,0 +1,5 @@ +--- +name: launcher +--- + +`name` defined in front matter. diff --git a/test/source/_roles/unnamed.md b/test/source/_roles/unnamed.md new file mode 100644 index 0000000..c35bf6b --- /dev/null +++ b/test/source/_roles/unnamed.md @@ -0,0 +1,4 @@ +--- +--- + +No `name` in front matter. diff --git a/test/source/_sass/_grid.scss b/test/source/_sass/_grid.scss new file mode 100644 index 0000000..606aaf2 --- /dev/null +++ b/test/source/_sass/_grid.scss @@ -0,0 +1 @@ +.half { width: 50%; } \ No newline at end of file diff --git a/test/source/_slides/example-slide-1.html b/test/source/_slides/example-slide-1.html new file mode 100644 index 0000000..52dd1ab --- /dev/null +++ b/test/source/_slides/example-slide-1.html @@ -0,0 +1,6 @@ +--- + title: Example slide + layout: slide +--- + +Wooot diff --git a/test/source/_slides/example-slide-2.html b/test/source/_slides/example-slide-2.html new file mode 100644 index 0000000..1515899 --- /dev/null +++ b/test/source/_slides/example-slide-2.html @@ -0,0 +1,7 @@ +--- + title: Override title + layout: slide + nested: + test1: override1 + test2: override2 +--- diff --git a/test/source/_slides/example-slide-3.html b/test/source/_slides/example-slide-3.html new file mode 100644 index 0000000..ecdc79e --- /dev/null +++ b/test/source/_slides/example-slide-3.html @@ -0,0 +1,5 @@ +--- + title: Override permalink + layout: slide + permalink: /slide/3/ +--- diff --git a/test/source/_slides/example-slide-4.html b/test/source/_slides/example-slide-4.html new file mode 100644 index 0000000..9feabf6 --- /dev/null +++ b/test/source/_slides/example-slide-4.html @@ -0,0 +1,15 @@ +--- + slug: so-what-is-jekyll-exactly + layout: slide +--- + +Jekyll is a simple, blog-aware, static site generator. It takes a template +directory containing raw text files in various formats, runs it through +[Markdown](https://daringfireball.net/projects/markdown/) (or +[Textile](https://www.promptworks.com/textile)) and +[Liquid](https://help.shopify.com/themes/liquid/basics) +converters, and spits out a complete, ready-to-publish static website suitable +for serving with your favorite web server. Jekyll also happens to be the engine +behind [GitHub Pages](https://pages.github.com/), which means you can use Jekyll +to host your project’s page, blog, or website from GitHub’s servers **for +free**. diff --git a/test/source/_slides/example-slide-5.html b/test/source/_slides/example-slide-5.html new file mode 100644 index 0000000..622df12 --- /dev/null +++ b/test/source/_slides/example-slide-5.html @@ -0,0 +1,5 @@ +--- + layout: slide +--- + +Wooot diff --git a/test/source/_slides/example-slide-6.html b/test/source/_slides/example-slide-6.html new file mode 100644 index 0000000..26addaf --- /dev/null +++ b/test/source/_slides/example-slide-6.html @@ -0,0 +1,15 @@ +--- + slug: Well, so what is Jekyll, then? + layout: slide +--- + +Jekyll is a simple, blog-aware, static site generator. It takes a template +directory containing raw text files in various formats, runs it through +[Markdown](http://daringfireball.net/projects/markdown/) (or +[Textile](http://redcloth.org/textile)) and +[Liquid](https://help.shopify.com/themes/liquid/basics) +converters, and spits out a complete, ready-to-publish static website suitable +for serving with your favorite web server. Jekyll also happens to be the engine +behind [GitHub Pages](https://pages.github.com/), which means you can use Jekyll +to host your project’s page, blog, or website from GitHub’s servers **for +free**. diff --git a/test/source/_slides/example-slide-7.md b/test/source/_slides/example-slide-7.md new file mode 100644 index 0000000..a17c79f --- /dev/null +++ b/test/source/_slides/example-slide-7.md @@ -0,0 +1,6 @@ +--- +am_i_convertible: yes +permalink: /slides/example-slide-7.php +--- + +Am I convertible? {{ page.am_i_convertible }} diff --git a/test/source/_slides/example-slide-Upper-Cased.html b/test/source/_slides/example-slide-Upper-Cased.html new file mode 100644 index 0000000..24aee8e --- /dev/null +++ b/test/source/_slides/example-slide-Upper-Cased.html @@ -0,0 +1,6 @@ +--- + title: Example Slide + layout: slide +--- + +Cased! diff --git a/test/source/_slides/non-outputted-slide.html b/test/source/_slides/non-outputted-slide.html new file mode 100644 index 0000000..5eca156 --- /dev/null +++ b/test/source/_slides/non-outputted-slide.html @@ -0,0 +1,7 @@ +--- + title: Non outputted slide + layout: slide + published: false +--- + +This should not be output diff --git a/test/source/_slides/octojekyll.png b/test/source/_slides/octojekyll.png new file mode 100644 index 0000000000000000000000000000000000000000..7c6d96d7ed4be64ab1f067c2d3b98f517ed96759 GIT binary patch literal 22360 zcmeAS@N?(olHy`uVBq!ia0y~yV4A|fz@)*!#=yXE{dlzz14FpHr;B4q1>>8$xf5px zNHkp3PL!Qu6~XDz(c<C2#5bX##bbw%5+kR_j@tSQGw0r&>$Ay0?2x5#f77-vPwOUZ zG5zrU{Curfi)4BOcP_TM>Y5wk9?BH)z4EKv+(l-u7R$sgE^>FCvRI9=`!Msa&Qngk zfql1Li0oZDpP8rs`HKHWCg<MU>asVyT)TJ84S}z0J`eitPEy<Dk{lAY#{V=!!mU+$ z{_mtr!#EmVx^C`nTVb=&P?xzO^XY~^NzPYf^LDT}r`Xqv7L`3Wk!LWLdy&mIJ(P)O z;_Grx2Axk0`qFQu=Egrdqxz%ih`>}`v(J*u2TDHdeY3!B)nz?~%cm3iPVreSmuLQP zpuYRmIVrRKS)5_Y$5*^rF(-|2LsLN6tsN^jPkZ1k*#Exl#y7Tt-M7muDm3T*-&lD2 z9z$Mv{^~zd4hqRk%`~e!c`P@dVf*fM^N8GrP_~n0|GHP?<~K|?oNKSMe%e7Ho=L`5 zstnhDzRhb^l~Cain$J-2;<DV+2Hr4*0}7Rk74Nb)ymY9w%+q^+pf~!qa|mC|RECNN z*9)gC)?zL2%$HKRs=`qBVW+~^yz-(4{F9!qIPtuhLGCC26I-ocmn62bon|iV+tuH- z&t#b{LqXl8qp}4Lk6C&#*l%AUvv>K-1D2llhdCbnp8P7O`TV`8wfR?E-ZGm8)-Kkn zeR%$W<;nKfLIuLxcAq+&*>jHle|50`+(l)px<9qA3%r?dU88=*-+;gS#qZtI<6oZ5 z^x#?VIi>lMztUJg{y96rLhgr5ZlB(K_VbT(^0jOE8;*u)UvbN~-hDH^UP|IQuk@*4 z`Rxx^_wG@MS!85=H8YjLE@NF{_Wy@9QJH`CE)2AMwfNi2=4U21*6m*PCW((h+UoJM z#V<aGFZ5$OewS~SZ;1XLroVIQZ|gl{*l?>kJh>#Qu)@auuJ^qwS4+=L*!jv(?CBS7 zhCfbydKYgqZC`oYIle2fZ`TWv&@k1%Nqh~`_rkQ_N7e2zl<{!B;{JSv)rVC}X5VAT ztB77ZCrsgd;Qrmk{gY<jWAIs>;Od{-Bc(mz|Bc;;rH)M1=vy4_q^iKkIkkaBMS+ps zV+D(f!a|`50i5OeX73Lxze)3MPhB`CzR2c}^URr{2Nq0GvAn?eVMDUVq!>w-gaEHe z+RY3xvMQczstt~sp6?d0{II*8;W_D!Bd3Fnx~Gu_BafG-k;Zpbc{T^}Nh<4~FzmP! zGl|EGp*aYh1S^CnM<ER~R8KBo!6XkXnDXTgj~Bx>MNfNUjo*_PY}8=R06FF-o2QY+ z?nw+bPlP7rO$wZ;%K)}uy(;63Tn<n3lMD7tVM*AQ?6J#}?P-8e!_9Aso<%z?8^Vj` zO;>3y3Kwd)xobknWS&#Go(yR-ohChbrN9u*W^3=s_B2AM!QQG><)mjmgZduTmp_|7 z1qe0R-)d8NIlXz4K4$~7<)kMGd|-;xjlo~fYm#^|!=4Gsp8woA5BzUc$-K5=ju%tG zbD2p?BAY+O2sK!@Pf$54HRbmthC4Sw(hJ^rF%_g2t=y^d^9^r8h}Wd^_ZZxkLOGYh zdrmD>Vc2gvO;5#hzs%(5?}1EPdb~V8?fe}j#2`M;`_d#s%V{hR6rDXL)$yJ3_GA#d z*{)*xOn{G})?-q&I%9^xR9}xv(|b?udvck<piQOr;*2{x<Tw%x+e=hVcIGvR@xAmg zQu9}3j5zdPQ&n>E?nw+k?3b$iysvWLw{@G!&OLiX81DFMo}6Jbjpf19bxXNDe|<3A zsr&OhLjjj(k*ffMU9I=0mC_CU7GHHe?Mj&+T-mO&d>3QE!<{P0ppe>L%;)*5!SLqR z7*HTtP2vH0=e6wuWlz3G3>)}8qi#FOFzi{Nd~(JcZ>9}XvRhOnr(B=JP}8%rMA0+f zNbS37JUhewAg@oaRT%1)PEpzK$Mj=r(4;?BoCicEJ-P15aK~Ho<oe0~g&3|+Q2Fl1 zbRk20(vwgb2B{dQNpTix=BnZB4Za?iy4juv6qPbqw5se)@F`2x`dP%V!^vaP>L!K) z3-?KmmeXQ%8O|ws@;zp_u!7t3mv{50kfN^)7A-23lI#s^$sU*DB_`jVw4c4fT2uAp z^E`%{Z_}O>n=|-Tdw<#~&9Hp0>d!m84c_NePo_R&;8FC{GiG0K&{XB*?Yj&nx7t)p z`9Qw*-f8DC$<cBu%Ym<slb-BSV3;<;X_6G%(}gMw^AdSHKmGS)kb2`h$!0r;Lvygl zr6$9hQ&}2{I6b2bG=5J~WmHhfJh;GS8ViH?9PcPAjq|FE2`>-IfKuoudxioY&#Hm| zH3pm434JR1PZ$nJO!{(Q!JVlr4cjeW>Yn7xXRtY_adL7u!-aO0vocerPkQb0zj^QH z{E4ggJo-_~`CI5ycy!dtt|bahyb6tMQ*>9gE+})jv_jxU7E=;WPPf^mBYMj+bA>O3 zCQHpWyL-i9(IhiPi7O^nZpj9{r74Ooofc6s$w_B);~Nx<Z`|DZ`OMtv^TqF<GqZ>F zTHl<R{{E-k^XL5Qyr<u-s8x%u`5L+>ea5-QbB6=MW`1X$?XilJVZ&kR!ix8A&6o4v zE6v`Ocl1-qYvt8e-%efos=QgYf8Dk0lABNVmcM#`apUc2dYXYD3HN`0+rI1G|K0hK zx4!;d6Myx^uRmXA-a5at;dAmi?&mER9T^OaBqUv0JkEvrh4WRMKi2+q%HnO)>wkA^ zAMH6(;2z<jAguXTmB(7Vqf%e6D*o%guqQPy(x(VqdwTNt^G08Fh6#*Ep3LZP7n|tb z*`^-8-h_$K_@b_|Td#ttTH><>M-I4grJCJ3{rrw|%Bymx`T9(gBd0JtkPwaed_d=@ zNMi`srN#!A6=??Sf{s2DtNxyQQQn@;(Imvs#_HJFal~NJ^k1=3wNHey)fpIgOuq;6 zFBf^f;+4YvTRT@Kzkh#sTGa0s56+!2`+aHi=Q{qI*1OHh_fFH%6Ks;LO<QcYeP6oU zbBE`N62|v8f3C{6F*lubV(BR^x8O$&stgQE`AnB@bQ7GCICrVch9fc47ytiq;@y|- z`uz{ios+$HH8^C>oxdT|Cscm*-|zP1X+h_O-?v^fEj;f$zdQW${f}oFWn-TwpIEU} zFaN99G!8EYhX5rNQO8Nixo>Ar+dTF1+Sb~Cw|u4xTsoh>c(JJP;$0dge|?sJ%ei=7 z>vhe)O^@^i^z_tr*}N=w+%No;+dD^xkwGIYVA3tQC3ZoFJp6*s8P{EW;5H#DG3%_G zFT2ri+clbxmR&!m8`SZ#e6g|t`%WJXQ9ow>+eHitQjVROCOvb)!vt4mgaoh%9X9Cv z)_7#;uFtmT&0GVR7_!AXz0&;hwR5%II}{FVxZ^X?+qKVl=|R);lb(w;2{8yvdDk{k zTWhAGl&6*SVXhUsL#}L^zv0S)k}Pe(DGUt@N)n|4FZp|{n&-E*)}ne%#NvBFhc?}J z_-*t&TuFnG!LjqJZ=te^;hFXgTUHj#W(t36eBOjbm0^d_lq=q+Dkt7LW>CIaPh7@! zNe=UQa~4$w1r2WD$zGGcdgWivX-?dC+-v1D$-vG9EDS75-3?c+NO5_Y%cC5%l3QVs zkRvAphnId*(AG&GEkk*fqiz<@|FKK-bmcomkj0T5GkWZg_9jN0_bW&X1-m7(V@67^ zTw7?mhSke_uwC6Elf5Pf>USn~c(JH5Ff<t@oKr~>IxH}i!;67|MK!4?;IM(!0n<M= zHH-Pcfw3mo#V093_j}`cbuIDuTG1vU1_sBQ_qR0h8g?drmVB9iHDvWRDaV`A3=RS3 zuEhFi{aT>m5_;sxo!P%He||4J@nyD))62HAdutEOJNd%p>EzYmP!bcEGP$H={kFx8 ziv%af@2l3>Tk$NU>Sg`>CDZj|eX0w89*U`2S!=e}pxWs_zfyWgo2N<GLm#gYwlgbS z0+<+@CaKNwc;=&ZOSxUQU8d__yk(KnjEKDSPmBMTv7~U^KU4eeollnZG|{PA{f+@; z3=9qd_qLUEmWr)Ax@69`>+{bnt5jlCDD2vB#A5PIvsZ6!{wO$K1qu)ajg_-Qx=ycs zJgs@_#rb>Y{s>>Iro<`z!lzGY?#a)~`*;0_3=PhGttmK#fq}EB=(Y0dT{1^j+D6}T zYnZ~N!*zA9;Mya<uD%tkQoEh*Z9jR{O1ocYj;%{?Gm_!(Vrclq<t2Kp+_G(ZSzgi0 z9inpsr!33(GRx#$!snTbZY-U1lDjx{zUS1#H-1j>s#VrtWN48SG4(0k{r<(Y$9lrE znT{Kno|#gkcGdCSg3r6_R=!`t&A`C2RK{Vt^MzfeM^gL+rW(EJIwpPYiS=%?>+4=y zGcq^?+{(3xe61!pnJKU{Q)YsB2;ZY6^MYdUgIpuU>1F$4x7kAlwat&0E)#m}W3}RQ zXxie}MZX@zZU-fwR|;zH_oc3UT{*d^Z{9@a@Z*(hSwyAt|I9xrw0r;5hbz3B{g@ag zC}@0L@ILFv#c$mCySG1;nY%d9@T;WekprJH&MldKrF`n*DVz)rO$)9x@B4gVI`fBT zdrW^D3WfN$Klqe7=cMuSJxlhPx-RNEe<6*DVM1WQwk)B!Q<xJ(P8XE8ZBNKK@X)8M zymo!`#ywr<FE(>BIEZ~Yu)_K661hK1L_bR!`M%n{YvsFx3Sa-eoOpLudUiG_Vj32# z$w=QW_h;Rl?O~^$o=umpn=r4gF#p%9E5E*cNPfM`=l{DW_vc@qFX=f;Q2A=@vm38h zo||!Q_SWS3J5%Z^e#F1Lv$J1&`meCqZEKgh1TZ<svOW!AJ-VdqZJFFB^>X{rmwZu{ zub8vtJ<9L@eC)HWJ}Fh}VP)aFZWEDR`(C~hvvL-isQ>59xiioGnLgh+DIEIpS6Fl3 zqA8(sy)7p*ILu9)zkS=I8(+IZv#TZqPqWy<S1pjZazzlwt$9JRj;$-FBnE8aJ~{7D zkl^VTvyb^q30i6vS~bm2je$Xu^{I<!dD(44$Ixy+x%>Nk{Vc_Ut&Fm_Owv$gKBLN% z>tb_7#X~f4`w<~a^T1PDZ-2N2FfrUNdbjs)TC{cK%oj^q<||nj9jlzh;$$D_Ic3s| zC0PZzAx7)HeWxy(vg-;c${i)^jr~Nk)m4A^q^z(2DZ1sUt!CThrM`8_t}9_rD{px) zFc@*2+NjleQq_LPx!jT<!M%zGNtYT|^z3^qeR)e`f#J)bkgR>#FSRAhZFcR_I)6o# zlffZu)egz$Yv<0svo=D8&GJ^DRncV~!wWH|yt?(C`MqjNoVDLA>!ImXzH>|THk!Td zUBJ>{#d#_%lx3+BbJdLV`@ZB{>E%z;nNYe*x%|Vc2WL<HyzDNR@lUrTcyii|bJu;0 zPjx)K@HuFI&G&%F_p9|bhraq2H{}WsL&A=o+d@j*SSyR~-Fhb2V3oC7X8XQtI~4v_ zKW^bJcxCe3C6_ThD0yksRM(7YFFCyRlXA~pOx<R5_ns#&BSYKl>=`>Vk9Afpo!iS7 zIdQqkj_xblr4+T)cX*V0o6OQkarc?^KdxZPoqEkSy{%D4I~Mc^G9>Kyxl8o4s>Wqw z_Oc3Z-8MeG_eC4JIA*?B+FZ45)v~>w<=z**_C?*cpS<65jn@{p8Q-og4Lh%7%6aUM zPp(=i3xkC0mah{-)$YoEz4vZi<>X(xTGC{%W?6X{Cb6D(zM86<eIy|Lh~tz?*IAfi zv)=wt_*TK}Aj|U9R8(M!-sQWSY!*%a^?Xg{NufnkRvmX<!8+q|C$mt|%*Cy%Hh+;= zIweEzwe2+@FWdfW>$9$8_nR{~xIXjOG79KSeAZ=?wk9=1>yE4LQdy1OpC(h)d7`W} z8)vS)W?p~!C*RUD-+#`Hojuhw(c903fnjm8$s*^bpoxs(=b|&WPw2HUbBg$Oq4?^# zK=(-A%QIz4e5RgD`DQ%3{-&$Ot_xGY+HL>l`+8=Gz9T2Yf-i1wn9ts?$kY1v%Tu~A zWQj(?7Vm#$HNUsb$~n8R?vk7I*Yh`(UmcCl*fZ~BaeKL|*@HO?pD{Qr-<~yf%eOOg zKVSPX=|h^#>oeSI7j6kuYB$Y}XvjPh+~$$|jAQ4k>h7HvSQc={&V0X><>@Br_3i;o z4r^JSR=knDcW>L{6K-?gZTmX;sC(xOcge4RPV&6WE3HZ2!(i!Ay?d7X%C(k;`DY}( z85o#Yo-R5OuBUZWq}sXf()F+-Q=_aiXDMf#={&)7Zb{H}$(ESJ%>ipS^sY5@j})Hz z_)7K)O9qE0%FSOG`fKaA2UW-VUFkO7{_^_=HIDPg9Ev$TE<c=ay69!XiG8~npP!oL zqM{+0#mDgGiy5!w#j=Xijk8|_tUPOB78Ux?FW+iUXrAGLghJ_yB?0a)-%t8{@!;HB z4~_<W_F!Nrt6Cj)a_;AA-)1;&be^j!wV7|e-T@V>7hn2rTv=k|P~7XK6;c)$_jgI} zG`0mRa%M803%!5#gaX5g*$Z-xXldNPzgxHMqmH9yj%Co&-)m-Ue)*BfXOVKA<H-{X z7I1edP5c~_$2HSnLT$^m)5emk7tHZsU|1A$uw%iLh|@w#CqABDY|d+G_v*%*8Pk67 zI%c}g&C2|m{<c#0z#@?;=WVyOO>|@k@D9A$7Z|YWhi#c-X10>-tQMBd8jrUs9s6$~ zQSk2dq|Ki{wo6Ega5Fe0_jTP@p6{r+v2DQ-fhmlt;ydSgvZPuwGOT!gY>t2b8@B+l zgjGwO1FQ^KRGTL8<)3G0$VyidnR3*Eon@(u`;jIg?K%&;rcX<C`;ULKnd|k5fq}uX zbAe01HUEIVXJ-^N%5x&T+szpQ+(Cj$8mp7rnA{i?HO$xS?B{zg!@%%LLvRX*SC5nz zi>mdWzsw9*0_A?cet*~Uef~4M<)@?D6g32=Y>d!eY0PlI=2Wx4{lf}A)h40O^(R-Z zSa5|ageA3Xw&y1n1_ypImZkfBE#p{I|Ew0jIc4$0fOF^PU+eG73+8K@)F#7_u>L#u z{^~39Kj+O~J8416bo15X%sp4T%XM~lw#hK8*t#G_Wb%R#kpr_oPg9nj@FM(v><rt+ zg5n&n|KFUdZ`sbbJ$sVp;h&d_IlQEb7#5@i-gF6Qn|<W!f;aOcBuiUf{Hb;q$gA_6 zJ|{!@$$G!F{+oph)C8yGoM2=)#(uZ>_t|%Iw#(dHa3b@Vm66Apy|oMvpWP5y8_n?l z>u$y~dw1@?^8HWS(d(NO*%`9*HP&~!2550TI9ur*!+Ywb`1f6<FOF}|_x|0O@i`aN zP)jHY*du#yx_+$AU)%l_B^fr13=#`h<hXuN2oYJpb@*)P;uqHz&bRzlwM#(CvpM5) z{>Hv_LdtU;I~T}!GcdTdUQ)<8t(jLcA+}Nc@`-8d?PvDJ<QR4b1v>oyI{D(61y?LK zM;u8pW@J!!o4f6)%cW51!!{1DFTDKFcVFdC^6m>Om~C6uNBMqln)J()!R?jK%;RcT zPFy;??24;+#!H54a*s;YswFmSe9mv;=n(o@_F~m}=M6i}J9X}h&uVzF<GR{LdmlT2 z?gdwh<LuTfTra+1roern0IRD!HFH<8{(C2E`{&yt76q28oih$!dGVQBIL5id(8@!0 znbNb5j$IKa7qXk$Cu}tBUfH)@=;gc9;?KT(-lWRTy>Z$_M&sHenqQZT3U`Fb_(=y! z{BGpixJ4$pWWvhG?p?Q|1y|c;Xa~3~;+lNp)BU%y_wMFqSIu;coF&V%{YC9|(M7#N ztQt2$HhD^K=eCx)dH9vL*oKWA!9R_@GktZp-*fPP*`yo|r5DSl<-WUfoN;TGp>*jK zt-=rXzpwnAdBs?hjnQQ8#dh5;H}fysI(wdGoE38UzNNQL&0s@C<_!L=kq>$u&HP^M zvf91(yxcEupUwaG^c8Hpcybo6N&11Ey1PHf&E=8LICnxJi=(;a)ehaM(uE;gGhNab zmwlc8tatvYw`a{<cbhG-nPo1O6LMqW%GH+^Gl%yk<^+1}-PY-6RBsf;Z82G>xNh;} zf8GWrrA>v7Ea!YY=ZP*jJEuAAayZ+mBw6jvN3Q5@E9PSKmt0uk%XDT5PsW)}!?(G? z{e0nf4fQnsZP|W3%FC4f=$v=gu5?PUJau83w`EFi-Lt?)*S94FuYdZmUH1Ko*9EC} z%~n3|tT>{yU014@%cI)5;1&BdyMUT3!Q$=Nae`T{tM<NsyG@wAMD68=bp^YmFIpxl z&3&tAym#(_*msK**&p685_IG8(Qf+wY|i`o@7Dj!r&##czTTQs((%^f>zi+4Q9ECL zHG2Hc$LhrgwY$9!&MZ8$LG^FC(yW<rdlriGiEw><{>FKM!OULEpZrtK#hrhaq20MC zhGTPu?^CWwi+#1TSMR#Z!S?2Lk@qg?J=4|OGWKcCo!fWs@>!J-i%TDv9L?okX-sW1 z?iKj=N^|XuC6dQGEiER$`k`-RB-c>(e0N3s|IP1T-nex4fX@5Q2@e*%y!KZ1*_rnT z7?oRQxNGjNcow2%)8$p0zrUA%MzgVI;L9+ds;(LDZ)~{s_rOOM1?Tpv{hy9M=_>pB z((i?kf{xcon^R1Srfgg0%eTYFP;%cjzR$I4-Tw92_m+xIHgs1{74Eg(!+pw)N#KC! z#WR_DYC9RM0_Mrj=n@Fg^~&6p|M|tgFy@xRXD;fUL1N`AFV1^cSN$k?&GWs9AGgjs znlhVJ?jFN}cZ<&cSa6_Y!rOJc{*Bs)gr_83$q@TLQQ=JXlNcuhlc<%aC){lcjwqS* z>*Pz7e1?WT{p88VtnMb*uJOHP{r_rt<DsC7^Lzvq|3-E){*T=f!4u3;GR<%Qj_`}` z_Dz?#n($0R<?So~SL)8s1)?@<cU7;~N~<dgWpGJ6c6ZUHEBzC{P0O^sqY=Or?HBDA z7wA!|dzv}-k7G^fb&;3SOr59CRbIY%`KfFCYo9)jrLSZ6wx8;I@iREu_|cUgOWBgP zI5e!&GUkkb<|%RO;aA^Xd$~P~tsF9ct;#=Y`Te~8^3BH4R$=CjD?Q>_(v50LL_e$N zx@>wH(&0DDx2i{*h1+SPJIlrs@nO5w@BVufCEdOAar9T8d9pi#?mu;2_4|tPe$VfB z>#y%wdFy)JRyU`~SGF6g)UaDke)B$$wd8YD_6?H?&!qupD}z2i=J4;>Z+W!isNnx3 z)9Q{xZK6%A``91H$uD~S?*Gba_H`yjmKU!6y{TRA&2Lu{fB)9y`a7S6JJ0Rv@LH?? z#OIljUPqv#ElZ~0&Fy8+tEDeSMoqVFSQ$9SXyrYd#qF1EyXGIi@lkf)?dY?X7q6|e zTyX05uK0gHZJA5e>p%Q?cB{i{>EyYBai_g^?UtQ=-n8zzS;w|wz6(Bz7N6TRk`FIg zT)e{AB=Kk0)#e#&inEFjz1Fm~Dlp{>4Kt9v|HoTfVzY~u=+!fizgkXqjVtgd_rIp$ zY%w?d!f_W1b*p{S3iTH=4!<;86ro>o`tuKis}mAsDk2U$yUs39;Zo&mPSfz}cvY>S zB{;dTq}=#KO`}}fw+R=|&eZlkC$!?|qR(MJ8aFmgbQZq3;d6tADqoD|53TYN?NZqt zcU?0-t$W|P!o_FRnq@*4J>5R|&bW0hG0S}Um%mD!MvbC<jY-QqJ$8R#lU+3BSflra zhdb^r*}b;3R^LT6z3A|)-*?K&tObmx9N8&6tLLud{zuO)aQ#;h?5&e7=?J>@_mpdK z;<uLR^W_~+x-j*!alLq!R$jhv?%fPw>6<yNU!qE8H5JMK@O!cIVy6{bt-%!&iyx}l z!b@fDo!ejT!hE!Q#q=-6m-!uE)|O=nrUwQ7dKjj)DQn}@hW>fI3=1=x6HcCRu-R6* zxpwjSqYs(GR<6{z+}jnV6=mGTG2#CT71Jqk=Tl@CPrCLlqHMCaqhxQqOU5>Kor|;A z?R^(`MJvE&o7LQqz@@7k=SvrB#&UVP&iJyoEpyMp)6&5#fterfoLi!2ZaDWOsMS(7 zzl=%nT4vcXIa6k(nY^qowbmT6W?hqeWxM!`U)RjKwbmTJ+;f6aW~a=yS<+KBx$r!- zYoFAybhfc)$tK?iON)As&vaRK@`0b*^&iQz(w0n`-ErLFhkf;Pd70AM<saXNY`mg% zWb?eBsi*JQUkl1F3f}v1$%|FFu71@swY26&U3avOvA(e-Y$I3M@~fBqmZ#lv^j}@J z^^t!Is}RS}oj)DxCZ%vHD(*LLbnsxxaZ*?i_j{lE{5N%Z`>l7ses)~#{#~2%XR_Cv zd~wY8#rKoH|5naBzwYkNZL&9azghP-vaf%8UUAKipUKCo<!h#Fe7W~~@MPPsy(gV6 z*_<i-7HB)cu#kt_PW+jURHNY&U(3y3jc*3Y?Vs=Ir!LFpI$82w{!PbKq1UhbJ?On? zxI68-VQ}>+?cAeFQ)jli9-X)6kJHJUbJZiQi&Jj*>D+vDOHaObkL$hv50opP-~Imd zZTY>es>Kg2nXIb5`etZVPB-_Q^m57Z8K)PWEMa`l(kGPGu;nJl`4pdo+P91|H@|ON zS#{p-_PtwH>7k{)>-Q`Q@;dd+Be1aZiMZIwcXw)T?#$kvbN-fQ^!B$C&PK<4I_N(8 z>D}-46Le=pNL|+WJ9|p&jSrLCS@_wUrbOQPI+^|Otdpz$*Ez9q-+%nuMM&}H+Ov#D zR~_X{39C_SwmdEDv3I?BT5O1ScC^>$gVTDBG5f}y-^aFc?PrhDvvHNb*S|kue16UJ z-|u$E@B91q{R#8^KYb&2m8|^pZ2NicYxkPAZm-!P8GMT0ynFW!lUltw%a8os{Y%l& z<NmtsPbR!RFEwwv%EP>)2iuxgzf%z_)>UhsX*ILGc9+=%;lfSR1h3w`Jo(bir=?rA zmjB?}w&hggvb3K9!pFB}%TG={Jx!BYvhQlYjneJ%l+$5bzunWiouIc-YWva0?6<mB zzBik}VW+{V7`8{WeRbX`rrGKF7bMS2@%V4V{^5dy+O|H~*Z!78UsgQ6u<)>5e);y2 zl9E&N<LhJJ?MU8f)N^si>vg+XUgzGNxHmW^qPo6kYk6K=<CJOdHXeQ1euM9b*PY7K zm(JIA2snAXeHOlE)~jcmXU=kLUfFMYi|PLc{Wj5UN9#RS3ak6wVq;_5>;C)KlY;#9 zH7%V_K0UqV;r29LQZmb^bBp;Q%iSRs?CF|I*X(ifI{od&qaV$$eYR*RI&mD-pC4v2 zMUu_+==B6Il|xhfvbWF73*cP*@L}*wb^l)}1s`^ll$4$Fum5#<M$ePK{5A^ph5J-p z^ID3zx>VR7&dI5_P+z|wU;^9pO}0<XyzhTMyL%s}Vod37o~~IZjb}VtyDhqYlIEKu zS7%-fe7<s`;749wPk;aZH8DFs756@Qb+!M5_3V37yH06o-VN4!Hz`O{qhz({_jPk( zW}K4wy7N<)){IkJzj>z2tCqN=*ivxSR?}w9gT-kdF9}&U-|$@EZ~ym7<+nGHmbJgO z@bL4Wmd@WJdC{fr|L^<Lr^lU|a!6_Wm8;cD((>1agoSeRELNycZhe>JqZ+ENdhO2B zrYgl$t&f%0dHx1etrfd;@bKX;28t~PSLcR2SJUiT^zYyI{mW~=-TZ%QXYu?KGmX=q zTv-`h^rK+C&G$RSN%cPxD?go7fBJp@f7?rU<vkDo``g5&n3-j#@mrdoL#rhvKX<QD z?~5f<E-mo$lJKrs=XaG`^Khi$wr}e^<}NIjvYX}Q)FB$V_fwv>pZewAl^u&Tyj<SY z|Gs_y$z*@~sTVJ9HZm%TYHMqIdZbhMlD&aKV}W0Jd4EY+nP+zN?gf*BE_iDGmip$M zyO(|0MzP=%SqCjWHILoeeIj<vvgun(zh*V9<az#MiqYYQ3@u@vGS5}jy8^@V&R$ou zF+VB5=Nc$xFhz4l&@TJmZ;X93&!5cn(G)y4(ZOQ*`x}NYZHrv0_+%&Ptq;F-+gjy< z=gkfK0}G1#X1RogU0?CR*Yl;^S3@8DsVjMWG_|ip?YWelaWO>bHM4U1uBXTTUa3EO zBm42Z_Z9oi9_Cv3H80Z0vSM|9`RB=Gf0l<bfe&TwPmr<i(@g!}AY*w;y`4{9tR!gF zm-5w>Z`Z0#H0aIZ>MWl8N=?>Bf9l4N`O^03E3ZA85wuLTW95>SDv9g;kMx>ftIs`s zP=N2>8{><%<}QI()EytrS(vzHum3!ek|3}6`-W@w_Is3{S3G#u^3t_?eVVD=G9T9O zZ9Z)IVou2Gd>{S4X~DI67IPk(EZCW+bvOU7BBNYO%I{ad58pd#d-<}bl-DdzwOyKB zUK)vS-}1E-`~BMR&8{w{ptw)QvM8jWnD5%XyO-`=e98aZWVPnf`nPKjTdHzAuaJ`Z zlU6*x@@;J3OJ@spRY4!sYAwz)$q~ud#h<@6{am-t>Wk^rolj#QbTXY)aSb$?qPckS z;(dZ^_O`ce_{P^#yy2VMujtldt1}N}Y&%1iZQSJ5TAY=jB;9oFURG0NZ1Srmb6A64 zvL>mT{duBjSE0+J)|AMr$`gMlZu86Y`m3&Y*h#92wWm#5q`{S)uYT>`zXHB%_nMm* z9kz_9F7I8t_i#&b*0*af{k(h>uO4qcY#Dm0U{yii?T&Spp>=ilyYg4(S=qI$<ve6y zoc6)sCr_>H*@TDHogeB1XL;&&Ey~ci9AfeON`%Gnc96M$|8hK+5tTi6F+0E5f_+(_ zui{0|mDle6E9i@>W^XMHO1~JgtMNmO>dYM{yXUVw^7@7G?1c-gG#u~gH`yOInJnTO z$hdIwrQiSOPI>%L=C0?>%gqIT)3?0by4`Y4^X5rePMxCL=LNOBTvBr8Q>ee6LKw4c zX^lp-fW)p9%Y@RUm&+XLOi`Qo?b%JwFy@OsYHKvQf=}_Z6sP={@@`4U!#Qy+#TPtZ z@;@)};J$Xx_nDtps+8cq!0*+EGgdYDWvHv#z2AD^@4_;Lu*c`3r+MAJ;w-Q7&ry=s z&U_}zwy5Oke}AP5{@-gb(~7}m4evSU&FMkj-HSA&yjqHx=1yrW@Y~P#yyLpaIm7jP z|2~vS)%gDC{dZTs=S!jwgxqR4-=$sPr@fYSS<6MArF!!2l2tyhbA8N|eN|2OD5@L2 zdv?q+W0q&vq7@!ew$;~`9<proJ9Y4*->G}|J{I_;&nV~vr>BVg*@rAS-nG16GAA~8 zCYMdv+%UB(H@#+gP2RcY?_uMtSUCoH<JlgwJm*dC;V)}mq;bkA_|z8%^`-h3Ja4YI z);zk}TJ!9~IeWK$ZS?aJJ$JArC@`Jx`HHPO-YwbEw($&0lB?01?~xjue!(jz-!Ein zyZnpw*&MH^(xY#g%|NDQ9lB+$F~|6x=gPyDcR8NVu-2SfxMuD5118s2bt#ktX%)Y; zPnOi1Gda;g%xLP%;Hbu8-g67ii8KVJGN>~yo8+DUBYbsReh_chB8~30Vk7_l*5bBO z3-|J%zV_lhm)2dsx2&MgO1tx%XQ^zm^WwPb*@rBD>^Z(+$``IXyhpkoB&JP_F5_@A z{be>+bHSuL9#x-hpN14|vRFIs$uf<u?DtoU8w>jQAI^FF_~9I7j^{7@PM!11<h|_4 zd3nXM2}@I-{;2NPoD;lvyV;J(MrRvzGIV4eGITt3jtM<iooYDeM3!{gl-F;MtYF^s zD$+AvY3}qT8eMaz<h2y1Xb7B>l;D3Z<7Tluy1n?8;nJLm%!+fT81!a2b$%@mP+$5h zrock|@`fc}ruNQbF_?DEGTF0uC6j4H=V~v7H5*NT%-9uWSm>j2{Wiz3$gk)3u`iM2 zROwo@<J&g&Lze2y59cIX%~6hBR^YdJR?x5O_pIgwHx=;lEWMxej{EJBFOtE6%cm|g zp7v&gO3Ep>ON&Asq}nHDEYj$*D)%c5`sKFan;ch35Z?<>l)f}*FaDx(>AvU6Lzbon z6L}|EYcAih?cvQ+7eXvvw{dt{DZfnZn!RX+%lsg#jo;+>pZ~DdEVW?Iey?}eGn4Q6 zg7Tp9phZe2u6V9gn*lBePJ3{?lrAd~y7TJwhj7{b53FpgTGCb0Gq#>x2`;TNwdNFO zxs(RU@s|Y2*=kwM5neZW)4I5sZa#{k>Z*3j4qN`)T3%qGZrbRR+VyU&k3iV^J+sqw z{|YoMR(#db!FM}C-2A*(kjT`WhjSMAo%&eN_io9XmyQ<8y>ETFbzA1#!AMWjg-5!g z{Wa4!O`eopa5>U@tI++ApNfkwc?kF^e>{Cv=bvOs)9ghWewxMt=O#W3-ZeR>wYW;~ zoTK_u`#F~tbt9_HXIg7c&2;(UcK3bn%hV-)Uh5}LxK!R$-59~`r^k6v-~ZKgjo1Fl zrZeA6U814Y$*T&^ehwDP<J*gmWOF^AVFe1KwxG2KK^gi+&ylHC@iP}33%W1cXR5$# z+oD!fR&ciScAiGpBDeGNN>>*6Etp(XuxIk7?d3LeE=#J-c^qH;x`6N6?PUdgP1XnJ zPVsZ@K323X{PB#qRRR8>_FIk|N1*-UACXsIzhOCSd;QXF5!XVmnQ||dd||cH{C(V7 zv$*X2wD0%-JDBbLz3$J~x#dsRZohZzc+v60mS}m|&uh-skkxWh-mfKnLRP)tR11HS zGR@ia)e5#{6Mp5rJ<{klX_1Cj?&W2^`{Mt;%KUkdzfQq>x}Io_j+S_Q&Bhx!W<TeL z$A^kKzE~0xuK(+NL>u?>8@#$w-mBN2_~;NmS#jM-&pp;kA5Ztpa!|V=C4IpoNW}GK zgK$NI&(yEd^F&+nuax%i_Ex=>tt#%j?ct@FJk#!L$d0eO^*>$+=dYiW{CHQ*qJmEu zM*X|wjT9G7T%ytCby=-w*3BQEv?Hpguby4fbl7r6RMhIpM*mh8s886*)UzyZ=5O^` z#lL5H%&WX|OfWdF*4cN`qdBV+rMnhosMR_=I(XLneh+7AhWb*jcP#D2M!id>zuQ>S z65W2@bu(vjqS2hr%CN}~s+aAr{}J~pQ+eO<s#sh04@}2n*8X3p(dE^-cxH%B)D{n4 zMQ?#~jSIhsFL=)#TV362sk)c1oKbP2v!mm&;KqbDGyRYD$32@lu2lDZzh@F_GV|!p zUg;&$BCdhOF?<Ri*@8Vp&#i3i%;~t}IWx~Ea88tv#N320KhG5$&^g+qdQRe4-#no< zM}I!w7gtPX-Y^MTzg4qqk%s()3V)5ibEho0bj5RJ-qk?W7q6blOgCJfYL)$xIZ2X3 z(eA+$6HuJhEQ)?~Hn#lkS|8<Q6IM+YN(*W){*(RwL4jYF<IAZ(*05J{DC#Bj*O*OO zq|tSXK`8%U%4W}>tJkmGJW0ba{MjY@r99U?XC?+LJZ8jt<N1>(lLTgYbS=`DH$R50 z%R%l2Yte)s=jwk+*1j*F;pf%wc+_BLPy0@lme)DmGePR4PPrzSYp}5@u%|SxUGU&q zbn1I{c^9tdOC&?qJTv08Xkh11R7tZ@D1LSHv50Hn6j=|O9TI!EwH!1xSav3;-(cIc zeutCS`+d=;=GTV#YG!}C##$a^)w{B5^~VpLg-#Z&Tm18_W-rp{@?tc)Tf_UL!770D z8-w`?_T2~gHn57^uiGthI7H0;*NQ7|G_OCDS+lp6%{2D%X`Tm<s%rY2m|rqUHbnDx zEm~o5nlokBhGwY?9IqUCHP}@bFuY;Ad4RPnzPNYFfdH{y#^A`<ZR!?tlFe6CNc#90 zcp5#rDttoHe1eE;V2IX)l|{lo1WKC13K-rnZ(SfXg|)S~tm$pS?N;$sTenM#G(7q$ z-E`PeuX$Fd)7O#-oOc|ASe$AO@XGL93J`Hsb*#OxeVXp%HEVKS#&2}0`uIKW0^h8I zbph<F4$1}ad~)Qw!T4$aH`A4|o88v#<#v?Qxc{fHKs|Mpq4Xn>%YKtRb#ml_LA9gS z$ME~l=hyGkiP+$<ZD&l;uP>RBQgYKC>+jcb|FBc;0%Mee@`r}KiH9t2E-+Mfz7<#H zxqk0w$BX*&FFc$R9$$O*=*i|(pERoihH{zfPg{003R^Vjp8WSGSj2UsTgJaXKZ{<f zPCqfVTkPfk&;KoX9>RJtm!41F*UJ5LL!yUx2D9%1#w$%)0c>|1=T32*5g>NIWbzc9 z_;uIs?Od>Gi_hA<+jH+PTxDA!ck0r0ms?ve_N?92A)sH->@$He=cIY*5{<4!L57Vb zF_R`KO^>ZwX*S{EkLj=1P5$?<PEt~G;=+ZBk`bU#*zIU-!SSuRDnM3k&ScgY1$|v# zf6dPhFOT)^np|{rZuzRI;_uJ<?$|ujY{J5`&tjtI3vZWimzS<LT%yr6YjO6uh*fF# zs^8mg{U#@8r>gGd{ZxbJ(L#fVbM`I}n$omrg5RmP++s5hS^ng=Td^*E&5AYC#N#Rq ze|(*!{PXNOpV{*5^5V<xH^nDC`E*)uvg`Etbw8J{UO$i3*yTg_lB)lQH~x6F+V<e; zOxsizHrK$1u7Pt*T;r4P?Jrt7M^g6at!kGVyZJJQuYB5IKj$*rEXU?|OL*kZ-CSj@ zS!&^~wP{nRg}VO!fN3$+xgWCABs`U0ofeOq(YC*$;m9HJeIHm$|0s#fxVWLk-1XKP zwWTkcHTi$X#dL(VUii*?yW?cb$C`vg(#&0pG`fUU)hBqAl$0dA?JYQWVFOPa=kqNO z*QD$|t5#aEBJ{`gd##Fdr|kasbH49GneV^8?wb6`o&TT47t^_JMjdIF-kGZ>l_u;< z6WNod^ngXgHBdw}?r7pNeyyj{iLC!#^PXQ-aPC8d#q!<Jzy4&u*So;QCc|gRW+h*7 z;EpF}S>xM;*Ly0LZ7r8mn-A(dyn4U-x~E*QXKc+y*Hxhk4;A$i{E~t}rZO3?G?;SF zL;FL)<x}~JD{ZZ|eRH!oZZ38%a_gxpo|Yf31YhEJe%ZQ^r=_^dk?{m$WP-NFj)!cQ zZ|#2b)x5;wdE4vt{OMY<Y*Pz_-*Z1ZTdY2HrIOs_m7tWQF(-G+^q$%KuDmEUe71S& z&B(}q1$<VTANTkFx_)o&0@>B?u3ZVqJfNlTqrQ~yc}jd?>y%&D@3Af@cpG)sb7$gV zw#_qrPAuR%`{a*)-Nnf@ZCwv^mc5tP-`%76t}sx<H88??x7RV>csCw4MPZH!?qTb% z&7Gn(XLH5IcT0Li&J{M-Y~X&w5bboxa_0gg`}p?a4^}2qIae8OZgl81uk;oDdFjtj zyD(-}mp~C$=a<@&>wgqVUKFj`7E~Jas^HwNQmr|cd94mOg36@cm6I>YoI4r2>4?ZV z&ANA%vgdYwpI5CU>1B69Cr|CTS_G$6rEzO&fpf?XmaSTU-49uXDJ{FCzw5=K7n(WU z%?m{T>ja6oo-IxMI%nQ<^QzE<rnyt+U5~5v^-K0zX{9MGrBoJlt?5F+Ilm3EInG-> zGt^VJ?w|arzy4S7mEU!hzOzlQ-^nt{^j*AyCuW7>o?oZ5bt8paW-0$Qef0V&+m)<i zpXK#-wk(^pNFz&+JNVF<1m{TZW=p}xALic9FQ3oZQe5MFX@63y=A6qs-<qugWUqL> zJb1PHEyvp}O;NjJz25COEOPGV=W}XXzv-=CA#&Wka#LdQLYplQT|Iwqujj9Qv5U7W zUG41Lsa2~ul$Yh#d4(mkZGCv8e*3TY$A23<es8<%l3m<#k3bPq_tq_2&2I!8`jILn zeC5T>&2lZpDH%F*gqKa;Bv|2~FekXX@06azNxlngQ4Q>ga~^ChhZKKnd1F7h-QMu* zxc)wwUjb)LbQH7H&Z?-DtlA-c?4n7k%CeWs=4Czl+qHXUe92K!xjbHtu0_vgY*V`0 zo6y4|?bW}IzbiKV{JgE~hb+}=?OsOjxFB*alZkhMSV*JWY~MY}vQrnxZfV%%E<bhB z)6-vXS&Oq+Y2FRps^-jjWsA`h#w_DWk#A;Rne2Xt%eeHj$7IRrMqj!0b~uQ|mx^v@ z12q;rJtERu45t)1TxCDHuK4-6r3)rsGH=@Y{X&HL(^;ljZ}0NnI_S7T{z`LJKtE^d z_QMO8Pg%P_%Qp7YrAuF?81YZZyR*Vt)7<p?Y@Vm`r}#eEEIpccptITXBHN@igS?F9 z&hOPx`|@YBZPK3xio=)5OLi$soa*uP(VQDm-QQm9<*nRS{H@8WfZ?*o`uhQ9H{MQa z%{<xXt<TH*++(W!oXwlpS#~YFvZLpw(Cfx!$*HTtZcKmpJmH$!ggFVTzRTZlUs<Dn zb${|2-a{UBpsX{S*ZGj-DK&|+ozka@%|y>xni}2nth`&A_4aM=CC^BnJ2QHo{Mg?& zSHp7suZR4;59f5P$q_xLxpqxwQD2c+_<f#-XRQC%ybtkFQHz@66eUs<f8~3+(f#m0 zK@;9YgG#?wwo7&@OP)$g@NrtaIDPM~$wpol%h^I2gaSCqnqwqRHz%iS{r};fS@A1V z_FUom`1RJB=I`GJrhPiOJ*MdGq<ohujw;@)V<rD8pQrjA@_6N`;v1dhtEXP{PDuMp z%6+K-5!b-?N@r!y25dWA;Ft7vZeu|oPk(FiuH8$|@Bj9WY3%|{FZI-iqTwqF{7!#9 z?=8%+X7A^)byptF(fKa7vG)DF2)PD6<#&q`^m2rKQdeh5&k&xqg=M|wp_|uctm-T< zGoBfKU&J-=irSK$izQF>d0t^xoHN-<>ctWn?&oWwmRf5bZY@6LXsE$pnQ*?>+)GmE zf$QmMGq2rCUB7?fVN3Iv`dS@++iwrgHYDljhhO$!v7dA$p)l)bWvNSi>=q44u;EjL zHS;9AI@2_)OKUH*SZl^UekfBCl$N@(z>oh0i|zvl5A{@dL(Fik&e!F8xg8$f+PZ7< zr;qVAa{m~QH=OI?QadE*e5*XKCEFt;x$|ndQQ6TT5!b+a$FAR>c~ZqKSRPw;7Wh>y z0Clj$^smjGGVzdQ-UR-#M%e^z2lb`%f}S;<`cor%?&h{y_ci=Gf2_T=LgYdBMGqk< z@2$W5HmzlJ4HWsR7IbH$gqOs@o|CP`Znai(Hs{UddS24ZS0G^0R29H{%8^rpVYj1M zLE{}&Zy)pLBKF_s@IAj%ecpDX-GfIPvKF7Ses81Rn<CO-G+|-7&iY@k!tc5TbuQBA zY70s-oU(V9UB;n@bIx3z5wyzhR9joe+$j$aSy~_bW?1mTe#_)dx9)rXJjDHKnoQ|> zb0@1;*XCG<+3J|z`*!!o`h6>}7?|H^*{wg@U{CU^%}36AZn{zhO5p1y+jPU6J5Mgk zFKYV~^J=Dp#c^|$zuI9l4q2W}uwTJ^)p72Wp9&W|cZ%-F-+2_R^xHMrXv+tgXGUKh zPD}Keu8|&aJwfNHeM{`Ls;JJBs>`Po2Z*>{4CpfSRGjnJ(Zap$*dfb6D^QBqbkJ;r zu;wL5vG$?8xI{hG<=^Lv$DP9OXC4c2UCO4uJ@UV^n$cPp3-`I}Wp+oNT%Z9Ok4uiJ zzRmn_&Sbm!Uvy(KxSpFdvITIja*!-&w%Nd)!@jQa^Aeqmw{y9f=1yrfowuT`D7iNO zh4WeCr=O=xVV<mZRn>E{i&~>q`tPIMdIeG<uBv-9q)tuYoU`zY)MODW&ER8)EtQQ- zu6gcE>y4@|Z!R<Va3#3tM&9+gQx-m)qw|2NGBdNvYbLvwdES+|Q)bWly?B<_ob^vC zPrY2r^~v|+8Wly)DL<BJbR}sVmtDMY&LtJbU@!G+_Z~l-b15j;B3+<ulXzRv{Q9@C zoL-KB9_ml|=3M0FeID}juw~@DYZ2;Gg7>?&mzUjMUU<~E<1om_LUy5v-QoPH9u~)! z9kN{RcFbCH{cgXtd-p<uu`7T7U6FGuPn-&SIOo!-SrO+0+LFxGw4}Vu1)P>~uAeBf zNTcgf?7eU2cUBcf`CNbTB69t^B|SptRx*k%U=e9633|8hnN+aF@x66>-!1v`!D0PL zkGvZ(E5h#T*v=0#p2^iRA@f)os08U;uR9~;P{1xHo~5ff<;|CQMpUOO%uzm^{BX{@ zyqBNX@7?X7ozZm%G<0@*f(Bn)_4BncD-T=d*Y9wZ^jJ`(ss6#B+xDlg^GwE;37KsV zWx5t=Xgw?NdzG<Za*@uQ%~?}dPX45DgTLvp<<SKUPa1SKh>F}RnVk7H_S-LMt2xDo zzh0j$7h*E?vUa^w9LLPG8aCHJ5m(;@lP{UdopV%A4V%XG{7Zx11tvb1mrL$QUh#~4 z6<;mhaA<3GsrpkXl@lKe7OmX!Y1eE4IopLAU5l1Uob%)_30hTf?$Oa*lZ}Ebj`Myx zsJDSRhf($P^w*lHdMnPH4}Ul(_jb8YWss4!qwKofoF`3^L_wCW^gHEXk$%@o^xR7} z*#`<4EMFHKy8;?rdcs~B^sK);=v6`A+h?<#<wIWiJnfIKiDWHhviUj9TlK)qe`&jB z8@%-m6meCZA#m=bO|RzCS4(m-3`&ExH8DGs2eq|;dSl5t5dt#j0>cf%ep_qG|3CV8 z8T(ad>A0S#s(&?py>}{ZmrIKLkU5&h#bIT2b?-Bcw_)Ys$N!ucnl{JZL&P;Orh552 z%yvw&`cmoV8mqrwTebCj8}Ip7>{n!0%bCqQuCXlWzj5OF%q26oFn01CeCybxt;w1{ zlkIz0``*`cd}@sy0!3U6F*5JA30uog3j2GUy%)9hTixfg`(Ln&gxxlj^jffdO5%sc zFP3>4vJQ(5pHx}5<cgcok&t5#O(W$&&BwyGx%{BXg_=v-3l?h5xy&Nvpu2)8^?>Xp z&zbF$J{$eNwfoW2@Ra=Axlz^s-<9v5o#wpq<eb%)PaO@975)21B``a0e`5)gbl5$! zKUeu|9BS*T;wL}X01Y*i1ug4oFLq1GSTniktkl+T@A__lq9vuVM&guh@@3C}*Lu@# z?W!_eTEp~Qb9wsHvzbp>KR@f0RMJ1sRA(o?s6_t#ROXh+!t<a17LnFG#sYS(*UKZf zs%_?MUhh~Q<i_=UO4FhRey5luPJP<=@=0=kC;R`J*uCz)n*WoJc5${Bt&9EC+iCaZ z&Vgx1YA5qOIJzp#_wKrVpS-+h@z=yo<J?!1`L9O)-s+CDjC((yS1<oxTFI;I8YptK zpwG9p*sphm->HA5*;j7emfi?zMHlKvDoxdXKe={k>ZbLH`>#}gJGpMo-B$}up59t; zqp<0Uis7V}2Ut}Y?AB~}RC$z@TWrPp+<$+5PWv%S@7Jg4v2G#{YnC3?TK&^?dVF50 z^GgFYHBe`3?v(Vsjee(u)&0EgGxN9bKEKky6~M%GFfX9+wC<%1Ka(?UE~xPSmcBZr z=<ej#X|EMuFl}QonV6{{Tya=++SbI)GiHAA_tX9Q_23FuQ>Pirq7DT-`g`fU^u5zd zIG%xuikRxvvy0n~gW8uy78Nml4|r0h*T22X@~tr{SY7o%^YWW-r#)-dEn2WL@|(Hb zgu6Q{r*Vij@JvYh$fc00q}-@EO_X=3nM;`QlminvJS+Fahpjg$0*yn7m<ApU*cGC# zddTv)|C|Z`{xMnx1uMt)+bHSA|KG*;{7OvC#*q7Ev#<Plw*Atbnt-U=x1J_=uQoB2 z5BWQJ``xP7TWqfsEDM^u_ej>Al53wf_ST2K{><s4`&Qv9Q}79%2TXPf8w*~lPS?2H zxMfr78Mb|5xoykxG%_`GqcWcuYjiEr$XayB^3;@xmzUcfJ{(a!-$s+Ux%txda-XW| zeh>fiiZ9=M@`|o*H|n3^wo0_zJ$?GG2=DCGUS;>~debg_+gqKQT$_JI;k?SZxGD!V z5B8OXCjFWR*ko=w_+8xdaQ!sS2MbJUs(4j-mrmVxT&j7IM%N-W<vELe4&J)mThPZN zXRmks*YBq%CN3^|aUt<z)$XNx7Yjez$gBOX*<eO&*S9{tmV%-uWj?ygi;X1j`eaS{ zIOmiI+YU#yf`*a=Y2`MSZD;#sEgFP(H0DP+tBJ=hIm4zlRm?#0*K5wBTMG^{@$){t z!n4%xlkrP%3DsO6x>z<lKmY%M2AQ|7Cm-Is_tM?F%5(p9rj+~hTd0Umow_OU@2|}I z2?te<#kBNItvD3A>43rN|85Jyz8gyLd#f}hmK~a@u-h!FGg0I1G!FjSr`mD#@yl44 z5BoWLg37?@Y;0_ofA`52f046%bnMpcyH}r<nw!ae_#<CkUNHNH;DJJkwY(X!$2VQu zIC-)64WSnaJD<K1I$=5e>^<e_4xD<8&Rh>>EcPzo<g4$T7&jr;mf42wbL}OKAQ49I zEsvJZG&i>Y_v@q0zi(^*@=so%Q@>6;pZC}A`SqWl->}ixU0?QTpLo0Il*Om)p2r^D zJY}*R!yB(Mwf7Zl`V+5eX`JSXf2(}HdR=jFfLZnxp()zsDd)}Oy~L&%%J3{ZyWq^t zdGa50yck~8-83k#Qr{5Qx!xvurnYSK{<uTN#x1ETQRWe=^iE&Ry}$cw&+GT~e|GLN zR7$>o{L~Y*IahC<y!gyfQ{%MSx;qbo_ubB^5}Lvw;~jEt*N;GN6SMs5Cnq}2eCvHD zcf*-E6YJ*}<chZJy!d?S^%Fi>oVRmz9zL(t+M~MsPDk*ulIq#9EUABL>g@8^s#U`y z?Y=N9Xx_bSzP23a-tCXpo+;TUI<@oUrpGmRm3uz@%C;AdVp_cE>APshoqx|;zWAO# zHTT@iuT~5VE9P90nKIKhCOK?_<*73%s|{)&Z%#?udNw=d+170fV(#9xy?wUOrm1M- z%gm5{&sqLlWKm^^sr_lR`itHpm#EtxUu92u`YCr#=ggbOT^?tjem|49_T{akJ+}Az z@A915xZuv+=YRhEEUuhp^nHiCZvfMQrv(NTULkcyBu|~u$+L3axoe}k_s7oDJvvj@ zw>`-UNLya6+CJ0%?Y9F3jz2HojO-KCYQHHT8o+eG;^O2LUwEE=b8Fhvl+7!=MOEW{ zx%8AKBkO&qKd<7fbmTm+@#nn9ey5CLpSE{SpLufI?mfoAKX2Wfm^%G?JMXDyfsRc@ z-{%(#e&A0{iwa;W=vj8xJyrC1K;4Z6Xa1jlR`mT`b5KB9v0vmju_+8PYa<tJnzJ}v zrX;}3oXxgGcuJGe>3ct|L-IU85w`gB(HZ&sRvtL<?v>CK(L48T1*hDtJy*BiUY~sq zw--a7rI!4uV@+SpUQ2ZD>}o2y5*xsE>S4hQ-hiL~SXCM3t<;&#U1YJ%!)D8Z92rRw zS=OZsbixg}PBAfjI{b4^`k_rN+9LPmr=+K+SO4beylHc&z`@b-7l-4_)xQ7hOC~H} zX*jdZW~#*|mVfIOoU#8dHAP)reY0P4P{6du&7b~qdog6(VfbWf9Q$<MqfOOUSJ-P9 zpT1KYz;&u+&jOYP@jmCsd)q$cZ2MHuw5h(z>ZP3q>(c^*8C<8XGdk3Lkw3k8>Q#CD zl(wQPAT3AB|HlT1F?_f+_o%vh>b6Tp6>}YDe!mwVFl(XA_8SvaH5d~FbvPUT6iihv zYDu+qj7;m-x90b9lvF;>wwIs5?CqVe{{Hf<sj-eTKRZ`ntPJ2fB_q4#USM>S5W~Au zpQk<z__Fu!3a(Q<wyaAR^zfRm<@91W6EUymmx+JC)U(#&;^OY5kNXygOqspEe*sIw zn-X6IAK@uypME$KRc2JJtTF$a)Rd-AfdP5^3^97s51(F>YI)j7+P%!YSW_dtn%B$G zQg{l(2I=kHhLOFeZSrTPevDtg-njawV3U#ki|_mI-m&5Ga@5pdO!#g*{qoU@DBd%l zKYu=xAa|(mW6R;cNj^fhzq`2{H8ogO8ESMJcN#TE@&%kXo-y<9X^)Jw64S*$xA3%N zHYG`#Z~yAvx<F(K!+{LRnLh>VBYGldo~=n#v6!uVK64sFZSkL?Qq%KBU(bI1`qyZK z+UW+*C+mCzxK90Q7Gltu*)Nqm=R-|$Px!yzx8t&#f&#csy<=nm(V(5WTwac#jin%G zGB|2#I0wi%g7#Pj1#n@JWnBv1nhJ7C!vc{hO`r|4O+f)n4D8MUXa=!@Hn*}a#Z(Hj zSk5`%9z@Tnt?v3PxJ(8sM{y#|<TVTo3K|F(ZDnR)!DSs*{VWXMe9>L~yeTLkj|pVZ z98OTcV6i>5l>rn`Fjs)K>cTw<3JS0m1_zMIH#H$Nasb2hiGa2fLxb>c&rXODcT&zE z0zwxNN&l|j`?=#)==H)s3QEa6rALoV+PH_oa2ap9lb&dG$I=ed`A05^y<A(AU{+~j zHc`>e`AGJ&btg<#ewaAHKt*ni%7P~fY8#{OZ7`5Lz$`d3%l51IoO?Al)%osJZ+m|4 z`SI_cZJ&Q_GGDN}YX9?^{(0Yj{`pm9G-YkAxwyU6*ZZ3_|HQo68n^TNuX)#u-q*Zm zmt<gIxN<CjFL`EHcX{kCoyV6aSp{uhvS-cT_I^IYDT`l!V;5U}ZM)7>HOccUzMC>L zFf>HXIQdh=XBN*TiT&)(FDurr<zZl8m}EXBmq)eDXpt4jM3sDv=>}8InocplS6F_f zG_LgSbG9gV|5skJD^=f~t6^kdV0fsi>lvHrd1`6u${T-A`n#HnT(A9^EpaNN&FEk4 z-O7{4&u`hcaLRO^=S^28Ju_mM!Nt&!d1r4|ri_>CRJr=M>m}rc+l*%M*<auObxr8b zE7NxcMQ5cdgWOk`;~l#)f8w#PFAh8kzIe9fTEoJg3=R2;k5gY{hu*q-%~+Fn$sU=j zXBZe5q_Sk5FAZB-C=);JU7wbu6i=#)^Un|K>*VY1W!|d3<u*CWmw{nL?3KcWR-v~0 zY}}S@i>*j3{L{NXSwmUU>$2<Z?e6jK?;fwudEKeF`tlorvn&h^Q<VBsKSvyyFe`D< z?2ezZ;<u{|rU>#rU3&dZ@4kDs^LJjm_Sp4woR1m<LxS9Mom|`1Cl;TqHa_<)_jZ?T zc+A@a0ZAI`cV*V!;Q#)V|Kn6|@1W>e=NT9n(gbQ{_CMQr?BYq^*=C}@`R=_3sm!<C zB=PO=|G%Zn8?Q{ax{}Pqa6pA8#b3&!wOo_yiIUaI+u<@*|5ACDGCTi#;Ju&s_rh7% zwq$EQ&h9wK!q6}==*DBo6F-91&gy>g<k(sHO{T6UQzX1Dw{NfAa$`fH^Ow@>PY$Jv z!%neH7O`YtSbnC$KXG}=DZM+pOkEC#Y!8oiUb(aDq~xi++wV($OMN{}_u}=s)3)w8 zv89t`jc?{%%d}@;a8Yaznz--Ld1;fm<@YKN?zr>yi+@(O#@1_3c$O~Rd*1T7jI{ET z|6k^OnJT=Og<(a!Zp!gvKd$(CDejU;s{3~*#z5C=y;b+RB#osPO4tA0c%}SH;`3h| zo3@%dGG}Vuz2(io;FDrGHP+jB<t|Tg=QE|ZoA>Se@mGa+Y0%A+YeMJ#$=-MTmiX<( z3IAlzEGr50m%Jat#K537NzhTTwd&o@=j*hsGJ|60&R!A!>*AHH-1|FM8wBrs+Qe-z zb%r-H1B2gnmv2iKX<qWSsM@~KQ!~U^^XlTJtSgplf9+4<UHVP^&E%`WTAO-Sep&3; zGRvER;oR}Q+Y!#QZo9F}bljQqo_E_Z72c&iNk*IPLYsHIt6tleKYiJyEUo5%_^fmW z1KWm-l;ogWqG@hvho;Wl67qer`u}b3UVkd^-L?IsOs?kI-*MUV57ku~O--8=KBLNH z!Y&tGMh3NT>6^XMy*=_%w^%u+ry21WUXPfyaozdDrZ45xx5i1{F1?@sHvGo+Nh?bN zgEM*ESHEL@<+(N|mVqHbr8?+h%F7*A(%etZ`9y`6Uy+<87<9txVpaEwzN=qzcRl#? zd2#&ox5pcgE)R>hKIPfo&&N<u)w@|ux@XC*ow)@+E7FYqs)_$!{<J4i%RKK+#^Lj^ zGFLT{J&$a%lHE69Ef)iW;K>-RSvr!e=B1OKDvM4Po4aG?me;;-EgWXb{4X{>ZZK7f zS@c`X{^Pz!g06ROjEQAV7xZRe2)bR8A3nb!c-_n^KW27pHhC=1x}s$B?iH^jxZG2x zWo!A)HqX~{*OoMV5?z^`t;5JrV|F`1O;;<E`LwT8PcE;8@Z?LgG|hIX+<h5)b;aVp zv8GWwOHYgWq*}eYD0<I?VZ~$_57tkDOO12yZ_~YUapppU%({mWiyQa!J(#$#*WTLL zE!pp=$HLWlxArH!f6G&#pII>TlyN+_l-Jxf#~2vgiu|G`b^Y&tCav#Q<jTJN?2I)Y zh3*+AIs@!qZtpA7ntNdy)AfDF&89szS6%pYm)vU(hB<l8A(w7)NbzosOH^4KR&@X1 z@8wl*jf(%hR+sI!4oa!cs$QItdZXT!ed@8G&DxW@b%fWlFnHYdDO<v{He`vQ!$F=5 zo`9tjrz~8tcV^i4f=gfLZ~K?|>f)dCp_v7%H}dpGcNP3zSL<q<smILVA!@bb{EEog z9;^FfOt;p)o_O?QZ;I2`$x^P9t6ECa(i2Wcgt_VX{&L_vd@)t8R?2IO&tX1>B~ki2 zbwoA%ircg9I(_kYdD2U`;O0GPr?Yp~tdIU$f6{#Gi3=(b%vN<;F`Ko$Wgkv`W@J;X zwerK%Pi}{$u7xr&qzP#rGMVW3tg5h`V@Vo&nDmKBol`V9o-J58_pOo2M|%z-voNu( zAtArlJPUIE=$sP6W?b_`Bg*b&KV!>E7KSq_RWF_jA6<9)`<$x=uTo{wm|h*tnRq-% zHf5{ct}mwyzPV5IFfTTLeMxbxUaf!T`x(!SYL=|HSLE7W5&CxNy!#(yUOQ{p++Muo zRQa1dODsNJzCWej{7?uJgX!cKFZc6wFaBwLv{rwvmR<ezi;tAWYuDt6TwlUF+l>8l z#ZljKk?Z#=x9oUZZLcl&<EuT_W##qzqh+jrI(|G8{lBy7tWojB*^wz(*~W8XGxNpI ze@s@tr!QCab^gOh>mPqQA9=ga*R`v?U4F>ea(n6TTl1dsN}u8qJa_(HUH*^vPY#CP z)0|iFIR5c^Y2J)0E7=)JiWeuSo&7L#-_IxKn<ej*zv%qnv~E(|jfc<ASgdoc`q)1G z6aT$=dv{J@^v-Bo_+MiwPmzys=J^jEo1TZt+kBI0=U15NH@Q~i=KS{g_E+kwkM7S_ zl6PA%zml23VsiD{;~GM9uXG#klKXqNI#GA?j<l=$e)jvRWlT!VO8<7}mGcp$iGg2# z$lJ|0W4h3fjp4zGfM-Dy=NU{{)*fHI-QwS~{mLa<m)9Ta;)z|`uN<OcEn%^A_S?#X z0oUA}gYGjtc$xE#X>II0p46KWr`}x4`?h3J$IIDIIp&_f*R$m1jgy>GURTauVrR%W zvas#aLZ{j}O2HEAejGP+>kQJ0di(3=oEPr=QfV6*86MOhOLnclvv0%F1dSA(p!IXN z1Q#yTTY6>kx}{sbY_n6lV?SN^zWtSRmsl7)%9Z!-S$pu{)|8sbxltG2>zP0LVZYcW z>GH%yb)Gi;{{J$My7MW$<z?vMIux=h@8VbPJI^Ar*8W>uHj%x4b=Va@`!1!qPFvo+ z%UwOSL@O=8FT7Grkl{c;*-|Fo6q)YVvrK2lYX;BsS#>+;z~*ZYpa1fV-9FFu`uVxN z+mpVjv<We|u{d{5Ik&kZ_0qCS=QREI=Nat{blUY^**z^(|D~1E%CCtkhEo_C7P>qx zxp^u=EmJG>c>30sUX#gcYY%T-dF6%dcDJC?-QRi?c~lup4g~0V94&D@5~P14#b;?2 zhvDQr-=+Uewr7=gdK?SX@8)4Bc#>pTdvV8N@wKO?+h^weUlwFKS9{TYxy6%8@{3uN z8BA`6@GW67EzVndY1y=A$9$~bm4!dCSmS(gf5N>zuDd(E+^*NKF&ucYAg#2y+Ggt$ zP}Aqkq`GpU>g8^8GnW0HR^8WPx<c=I{Y#cbbwUhMnHtZREDTwYHp^>flSb)eqx7;R zNk&1D4pSW8|C$@>Yca#~$|(kh7vJ)h+01ONj{lL|!+5Vu%ctnluEkeE@BO{AdWNS< zi4en`*{4!hI&7ZYX1m*fJ>K%y;Yc^t+=B8?L7UI`ES_pES98Lbna^~xp2?CjA%;C= zHIskM+h{Xqug+ql&Bvs>y{@_Lnm5z*&6~Ubb;a9)VxQT)vyVE#z%Xalscj((Zl2s# z_Bux+`gh(oy{EUluA6@~`7tkhzrbCetf0p0wh8Z47#I$pldOw#ch+nUy;#z`(Z|qf zC13IR?{dbR3=Jz?438hwsrla8Idh(@wZ7)M|HmFay3l=k;q1MQg3kk_tN-i!u`b^C z`jf5j?(M5edowjw&OYL^PMYCBNKmk)v$D`-9gVqGeVsfPjU_LO{=c{Qr*)dn>;#qY zz1!LQ{u|wyUwG!J`nr4GtE?jv)fgFqcS;A}>{<8zcbt{!s+By)Ud;Ly?#%M;_W7{> zO_@y_ue_ME^-c7S`1n2fns@dG@-F+P`o{mrx;!_Qiw(wt3=IdL?+jTG<*u2lmTF*Q z)UCb5Bl^eZ!q*>4jQn%^E={>9b^FBYee0^ufBk-DgYev(b!?a3<<u}RSS*^Tu~a=V zD5WF!PO!2|iP5ZImtxo$Oj|`Pm@iy@|D&w<R@a%xqCn5JdkrtKpL_G(Sjy`y55oe^ zi(XmU%hj(+n;qMrkvhjEcj=;%Yn}HsUNWa|`!1y`I5kI!;Y!%IB}@~)tt(dX3B2;+ z@bl!Ee`nrJpAxLi$slvn<1%07d(N3&u1toL{l1)d&0{pbk!{I#S5Afl5!(!=P4-@Y z%t&yC#F-^8Z+Pz7y?h1Z;syqWtZbc?i&g&i&NPv&pZv?SX9+VyLx_dxwAa&>N+<TP zB)5tv_T{PB_NdN!QR`~C^6f`PhMv@LDG%09_VbSM^!kJ<TlbW{-FUX-%ir*p8l&&R zUo*DNWMVk*^TFb!hbOwdbTJhvTh#lvG`R0ickUCBnPnUK-u|!nK50u?`$zlDj0_!7 zdZF&R6JK3_B;C~}DSfxtoGt3R?OLlalPOg}+TBbH49_;{ewCQ=?p(kwPe$#i+Z`d> zm;N{#pcZ(z>(?cwiyrk144NP9^j9wU(RL~RNnX|`vCg9byRL1zGVz%)14BVQ*LkNu zCsf-moqlQCKewVj>-*1{+aHyeX7gTcyW|`q@pQ(CSquzIjy}E`lcbSav1r|KpK#g! zU02@EFFZ0ua>|m;j0^_TcQ0MHrDx%sxeLE!?m0G{ees&SDxRhMyh~J{xta+xD45zU zexceHq_(&$&}(Lh`gciZNk`|E{#w<h3=9(PS0?^?d_68n!?w)nG{ZG5tCi>Sc%J4= z^^R@pUC+tDaOrWHfx7U)BLQ;OnQrRyu6&$ZlfJ!leYUK`shbif!ps~Q7#cX9HEr3l zPs)d9>6w@kXU$;iiNCd-pKmLUo#@T;^p@!c5i`LPk<1JYCv!aSPFyrY;*^3c|BOGD zm!-NNuaQib{klrRtJ`kd%0?CjhW@}a+pbJ4%IsLU#vqwfT5e|2`j>e;saq>!CW!_0 z*)cIN+<o%Y<5|&U-le9Sm)x53xazOODM!ms9hEs;3=B(7i+f!><9L^Msb^;TbwRty z*N-W=R?g1TXkWS_**(k9L56|h!5O#Woq26Rg4<Fq-`i#O{j*7SiU#{+^FC0wHa+T9 z&B-%qO>LK6X-u}he|y)JSX0rZ`5L;nZfp)QO|E*x#K17)Y~R(aS4Zc0JNLH*$)38{ zJF{&~`1*?vE#xLkH3gYW`I%YjzuL7nIf;RR;f1Qb{?;X|E46h!D+89im|v&2Q}Ow( zbuY57T`0<LD~dd|bl#PzR=@u0F6v=mU^r_wH+N-q)ZR%SW&AFE(dc^l?S<3Hrxz!R z{hd8=;g;I>)91Cbu2k*K6$AB#Ki||^866aVDph0czRRV_eb-)YxR}|~HYdy3a>A!c zyHvM&S9>1ru4iCiSn=7!_S3KGX?6PA@vhArTj$MtCG>fdSM2%N$)ygnSNO~DGB7YG zcv-1$J+~+!YW0%7mkB4_CR$y7J3o50mGYWmJ8uq#hX0Hc)2<tuG+N(bU|?YIboFyt I=akR{0HDp}od5s; literal 0 HcmV?d00001 diff --git a/test/source/_thanksgiving/2015-11-26-thanksgiving.md b/test/source/_thanksgiving/2015-11-26-thanksgiving.md new file mode 100644 index 0000000..48d2dd5 --- /dev/null +++ b/test/source/_thanksgiving/2015-11-26-thanksgiving.md @@ -0,0 +1,3 @@ +--- +--- +Happy {{ page.title }} ! diff --git a/test/source/_thanksgiving/black-friday.md b/test/source/_thanksgiving/black-friday.md new file mode 100644 index 0000000..1d8ea1b --- /dev/null +++ b/test/source/_thanksgiving/black-friday.md @@ -0,0 +1,3 @@ +--- +--- +{{ page.title }} diff --git a/test/source/_tutorials/dive-in-and-publish-already.md b/test/source/_tutorials/dive-in-and-publish-already.md new file mode 100644 index 0000000..bb94e0f --- /dev/null +++ b/test/source/_tutorials/dive-in-and-publish-already.md @@ -0,0 +1,9 @@ +--- +title: "Dive-In and Publish Already!" +lesson: 3 +approx_time: 30 mins +--- + +Jekyll converts Markdown documents to HTML by default. Don't know what's Markdown? +Read this [documentation](https://daringfireball.net/projects/markdown/) +While you're at it, might as well learn about [Kramdown](https://kramdown.gettalong.org/) diff --git a/test/source/_tutorials/extending-with-plugins.md b/test/source/_tutorials/extending-with-plugins.md new file mode 100644 index 0000000..03d2ea2 --- /dev/null +++ b/test/source/_tutorials/extending-with-plugins.md @@ -0,0 +1,9 @@ +--- +title: "Extending with Plugins" +lesson: 5 +approx_time: 1 min +--- + +A lot can be accomplished by using Jekyll out-of-the-box. But a lot more can be achieved by using plugins that extend Jekyll's functionality. There are numerous plugins supported by the official team and many other third-party plugins provided by the Jekyll Community. + +Check this [documentation page](https://jekyllrb.com/docs/plugins/) dedicated to working with plugins. diff --git a/test/source/_tutorials/getting-started.md b/test/source/_tutorials/getting-started.md new file mode 100644 index 0000000..cd9bbf8 --- /dev/null +++ b/test/source/_tutorials/getting-started.md @@ -0,0 +1,7 @@ +--- +title: "Getting Started" +lesson: 1 +approx_time: 10 mins +--- + +The first thing you need is a working installation of Ruby. Install from [the official website](https://www.ruby-lang.org/en/documentation/installation/). diff --git a/test/source/_tutorials/graduation-day.md b/test/source/_tutorials/graduation-day.md new file mode 100644 index 0000000..ec1d8c5 --- /dev/null +++ b/test/source/_tutorials/graduation-day.md @@ -0,0 +1,10 @@ +--- +title: "Graduation Day" +lesson: 6 +approx_time: 10 mins +--- + +Congratulations! You now know enough to start Jekylling! + +Want to report a bug you found? Or give something back to the community? +Head over to the [Jekyll Repo](https://github.com/jekyll/jekyll) at GitHub diff --git a/test/source/_tutorials/lets-roll.md b/test/source/_tutorials/lets-roll.md new file mode 100644 index 0000000..0cfcfe8 --- /dev/null +++ b/test/source/_tutorials/lets-roll.md @@ -0,0 +1,16 @@ +--- +title: "Let's Roll!" +lesson: 2 +approx_time: 1 min +--- + +Now that you have installed Ruby, Jekyll and Bundler, lets get Jekylling! +Enter the following in your terminal: + + $ jekyll new my blog + +Then preview your new project in your browser right away by entering the following and pointing your browser to `http://localhost:4000` : + + $ bundle exec jekyll serve + +Go ahead. Try it. diff --git a/test/source/_tutorials/tip-of-the-iceberg.md b/test/source/_tutorials/tip-of-the-iceberg.md new file mode 100644 index 0000000..4dab1cf --- /dev/null +++ b/test/source/_tutorials/tip-of-the-iceberg.md @@ -0,0 +1,6 @@ +--- +title: "Tip of the Iceberg" +lesson: 4 +--- + +Now that you know some of the basics, learn more about working with [Jekyll](https://jekyllrb.com). diff --git a/test/source/_urls_differ_by_case_invalid/page1.html b/test/source/_urls_differ_by_case_invalid/page1.html new file mode 100644 index 0000000..a6a79f9 --- /dev/null +++ b/test/source/_urls_differ_by_case_invalid/page1.html @@ -0,0 +1,6 @@ +--- +title: About +permalink: /about/ +--- + +About the site diff --git a/test/source/_urls_differ_by_case_invalid/page2.html b/test/source/_urls_differ_by_case_invalid/page2.html new file mode 100644 index 0000000..21f679f --- /dev/null +++ b/test/source/_urls_differ_by_case_invalid/page2.html @@ -0,0 +1,6 @@ +--- +title: About +permalink: /About/ +--- + +About the site diff --git a/test/source/_urls_differ_by_case_valid/page1.html b/test/source/_urls_differ_by_case_valid/page1.html new file mode 100644 index 0000000..a6a79f9 --- /dev/null +++ b/test/source/_urls_differ_by_case_valid/page1.html @@ -0,0 +1,6 @@ +--- +title: About +permalink: /about/ +--- + +About the site diff --git a/test/source/_with.dots/all.dots/2.4.0.md b/test/source/_with.dots/all.dots/2.4.0.md new file mode 100644 index 0000000..74d96bb --- /dev/null +++ b/test/source/_with.dots/all.dots/2.4.0.md @@ -0,0 +1,5 @@ +--- +title: v2.4.0 +--- + +v2.4.0 \ No newline at end of file diff --git a/test/source/_with.dots/file.with.dots.md b/test/source/_with.dots/file.with.dots.md new file mode 100644 index 0000000..211dfb5 --- /dev/null +++ b/test/source/_with.dots/file.with.dots.md @@ -0,0 +1,4 @@ +--- +--- + +I'm a file with dots. diff --git a/test/source/_with.dots/mit.txt b/test/source/_with.dots/mit.txt new file mode 100644 index 0000000..c366b0d --- /dev/null +++ b/test/source/_with.dots/mit.txt @@ -0,0 +1,4 @@ +--- +--- + +I should be output to `/with.dots/mit/index.html`. diff --git a/test/source/_with.dots/permalink.with.slash.tho.md b/test/source/_with.dots/permalink.with.slash.tho.md new file mode 100644 index 0000000..71b9b2e --- /dev/null +++ b/test/source/_with.dots/permalink.with.slash.tho.md @@ -0,0 +1,5 @@ +--- +permalink: /with.dots/permalink.with.slash.tho/ +--- + +I'm a file with dots BUT I have a permalink which ends with a slash. diff --git a/test/source/about.html b/test/source/about.html new file mode 100644 index 0000000..a6a79f9 --- /dev/null +++ b/test/source/about.html @@ -0,0 +1,6 @@ +--- +title: About +permalink: /about/ +--- + +About the site diff --git a/test/source/assets/application.coffee b/test/source/assets/application.coffee new file mode 100644 index 0000000..b9f5e1e --- /dev/null +++ b/test/source/assets/application.coffee @@ -0,0 +1,3 @@ +--- +--- +alert "From your site." diff --git a/test/source/assets/base.js b/test/source/assets/base.js new file mode 100644 index 0000000..40e8a46 --- /dev/null +++ b/test/source/assets/base.js @@ -0,0 +1 @@ +alert("From your site."); diff --git a/test/source/assets/test-styles.scss b/test/source/assets/test-styles.scss new file mode 100644 index 0000000..bf14345 --- /dev/null +++ b/test/source/assets/test-styles.scss @@ -0,0 +1,4 @@ +--- +--- + +@import "{{ site.skin | default: 'grid' }}"; diff --git a/test/source/category/_posts/2008-09-23-categories.markdown b/test/source/category/_posts/2008-09-23-categories.markdown new file mode 100644 index 0000000..7b5bc39 --- /dev/null +++ b/test/source/category/_posts/2008-09-23-categories.markdown @@ -0,0 +1,6 @@ +--- +layout: default +title: Categories +--- + +Categories _should_ work \ No newline at end of file diff --git a/test/source/contacts.html b/test/source/contacts.html new file mode 100644 index 0000000..1615afe --- /dev/null +++ b/test/source/contacts.html @@ -0,0 +1,5 @@ +--- +title: Contact Information +--- + +Contact Information diff --git a/test/source/contacts/bar.html b/test/source/contacts/bar.html new file mode 100644 index 0000000..1615afe --- /dev/null +++ b/test/source/contacts/bar.html @@ -0,0 +1,5 @@ +--- +title: Contact Information +--- + +Contact Information diff --git a/test/source/contacts/foo.md b/test/source/contacts/foo.md new file mode 100644 index 0000000..4e41beb --- /dev/null +++ b/test/source/contacts/foo.md @@ -0,0 +1,7 @@ +--- +title: Contact Information +--- + +## {{ page.title }} + +In case of emergency, contact Mr. John Doe, 1234, Foo Road, Foo. diff --git a/test/source/contacts/humans.txt b/test/source/contacts/humans.txt new file mode 100644 index 0000000..84e2982 --- /dev/null +++ b/test/source/contacts/humans.txt @@ -0,0 +1,5 @@ +--- +permalink: /contacts/humans/ +--- + +I should be output to `/contacts/humans/index.html`. diff --git a/test/source/contacts/index.html b/test/source/contacts/index.html new file mode 100644 index 0000000..1615afe --- /dev/null +++ b/test/source/contacts/index.html @@ -0,0 +1,5 @@ +--- +title: Contact Information +--- + +Contact Information diff --git a/test/source/css/main.scss b/test/source/css/main.scss new file mode 100644 index 0000000..abc9ecd --- /dev/null +++ b/test/source/css/main.scss @@ -0,0 +1,4 @@ +--- +--- + +@import "grid"; \ No newline at end of file diff --git a/test/source/css/screen.css b/test/source/css/screen.css new file mode 100644 index 0000000..c5800dc --- /dev/null +++ b/test/source/css/screen.css @@ -0,0 +1,76 @@ +/*****************************************************************************/ +/* +/* Common +/* +/*****************************************************************************/ + +/* Global Reset */ + +* { + margin: 0; + padding: 0; +} + +html, body { + height: 100%; +} + +body { + background-color: white; + font: 13.34px helvetica, arial, clean, sans-serif; + *font-size: small; + text-align: center; +} + +h1, h2, h3, h4, h5, h6 { + font-size: 100%; +} + +h1 { + margin-bottom: 1em; +} + +p { + margin: 1em 0; +} + +a { + color: #00a; +} + +a:hover { + color: black; +} + +a:visited { + color: #a0a; +} + +table { + font-size: inherit; + font: 100%; +} + +/*****************************************************************************/ +/* +/* Site +/* +/*****************************************************************************/ + +.site { + font-size: 110%; + text-align: justify; + width: 40em; + margin: 3em auto 2em auto; + line-height: 1.5em; +} + +.title { + color: #a00; + font-weight: bold; + margin-bottom: 2em; +} + +.site .meta { + color: #aaa; +} \ No newline at end of file diff --git a/test/source/deal.with.dots.html b/test/source/deal.with.dots.html new file mode 100644 index 0000000..fb3d473 --- /dev/null +++ b/test/source/deal.with.dots.html @@ -0,0 +1,5 @@ +--- +title: Deal with dots +--- + +Let's test if jekyll deals properly with dots. diff --git a/test/source/dynamic_file.php b/test/source/dynamic_file.php new file mode 100644 index 0000000..823bb47 --- /dev/null +++ b/test/source/dynamic_file.php @@ -0,0 +1,4 @@ +--- +--- + +I'm a Jekyll file! I should be output as dynamic_file.php, no .html to be found. diff --git a/test/source/environment.html b/test/source/environment.html new file mode 100644 index 0000000..b875a61 --- /dev/null +++ b/test/source/environment.html @@ -0,0 +1,5 @@ +--- +title: I'm a Jekyll environment exchequer +--- + +{{ jekyll.environment }} diff --git a/test/source/exploit.md b/test/source/exploit.md new file mode 100644 index 0000000..c3f77d1 --- /dev/null +++ b/test/source/exploit.md @@ -0,0 +1,5 @@ +--- +permalink: /%2e%2e/%2e%2e/%2e%2e/baddie.html +--- + +# Test diff --git a/test/source/foo/_posts/bar/2008-12-12-topical-post.markdown b/test/source/foo/_posts/bar/2008-12-12-topical-post.markdown new file mode 100644 index 0000000..813db11 --- /dev/null +++ b/test/source/foo/_posts/bar/2008-12-12-topical-post.markdown @@ -0,0 +1,8 @@ +--- +layout: default +title: Topical Post +--- + +h1. {{ page.title }} + +This post has a topic. diff --git a/test/source/index.html b/test/source/index.html new file mode 100644 index 0000000..ec54200 --- /dev/null +++ b/test/source/index.html @@ -0,0 +1,24 @@ +--- +layout: default +title: Tom Preston-Werner +--- + +h1. Welcome to my site + +{{ layout.front_matter_var }} + +h2. Please read our {{ site.posts | size }} Posts + +<ul> + {% for post in site.posts %} + <li>{{ post.date }} <a href="{{ post.url }}">{{ post.title }}</a></li> + {% endfor %} +</ul> + +{% assign first_post = site.posts.first %} +<div id="first_post"> + <h1>{{ first_post.title }}</h1> + <div> + {{ first_post.content }} + </div> +</div> diff --git a/test/source/info.md b/test/source/info.md new file mode 100644 index 0000000..a553952 --- /dev/null +++ b/test/source/info.md @@ -0,0 +1,7 @@ +--- +title: Information +--- + +# Information + +Very important information is here diff --git a/test/source/js/coffeescript.coffee b/test/source/js/coffeescript.coffee new file mode 100644 index 0000000..9531a7c --- /dev/null +++ b/test/source/js/coffeescript.coffee @@ -0,0 +1,11 @@ +--- +message: "I knew it!" +--- + +$ -> + list = [1, 2, 3, 4, 5] + square = (x) -> x * x + cube = (x) -> square(x) * x + cubes = (math.cube num for num in list) + + alert "{{ page.message }}" if elvis? diff --git a/test/source/pgp.key b/test/source/pgp.key new file mode 100644 index 0000000..f7f799d --- /dev/null +++ b/test/source/pgp.key @@ -0,0 +1,2 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: GnuPG/MacGPG2 v2.0.17 (Darwin) diff --git a/test/source/products.yml b/test/source/products.yml new file mode 100644 index 0000000..21828a0 --- /dev/null +++ b/test/source/products.yml @@ -0,0 +1,4 @@ +- name: sugar + price: 5.3 +- name: salt + price: 2.5 diff --git a/test/source/properties.html b/test/source/properties.html new file mode 100644 index 0000000..fd40c45 --- /dev/null +++ b/test/source/properties.html @@ -0,0 +1,8 @@ +--- +foo: bar +layout: default +permalink: /properties/ +title: Properties Page +--- + +All the properties. diff --git a/test/source/sitemap.xml b/test/source/sitemap.xml new file mode 100644 index 0000000..c3c3cbe --- /dev/null +++ b/test/source/sitemap.xml @@ -0,0 +1,32 @@ +--- +layout: nil +--- +<?xml version="1.0" encoding="UTF-8"?> +<urlset + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd" xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"> + + <url> + <loc>http://example.com</loc> + <lastmod>{{ site.time | date: "%Y-%m-%d" }}</lastmod> + <changefreq>daily</changefreq> + <priority>1.0</priority> + </url> + + {% for post in site.posts %} + <url> + <loc>http://example.com{{ post.url }}/</loc> + <lastmod>{{ post.date | date: "%Y-%m-%d" }}</lastmod> + <changefreq>monthly</changefreq> + <priority>0.2</priority> + </url> + {% endfor %} + + {% for page in site.html_pages %} + <url> + <loc>http://example.com{{ page.url }}</loc> + <lastmod>{{ site.time | date: "%Y-%m-%d" }}</lastmod> + {% if page.changefreq %}<changefreq>{{ page.changefreq }}</changefreq>{% endif %} + {% if page.priority %}<priority>{{ page.priority }}</priority>{% endif %} + </url> + {% endfor %} +</urlset> diff --git a/test/source/static_files.html b/test/source/static_files.html new file mode 100644 index 0000000..fb0786a --- /dev/null +++ b/test/source/static_files.html @@ -0,0 +1,4 @@ +--- +--- +{% for file in site.static_files %} +- {{ file.path }} last edited at {{ file.modified_time | date:"%H:%m" }} with extname {{ file.extname }}{% endfor %} diff --git a/test/source/symlink-test/_data b/test/source/symlink-test/_data new file mode 100644 index 0000000..37fb8ff --- /dev/null +++ b/test/source/symlink-test/_data @@ -0,0 +1 @@ +../_data \ No newline at end of file diff --git a/test/source/symlink-test/symlinked-dir b/test/source/symlink-test/symlinked-dir new file mode 100644 index 0000000..d2d7c52 --- /dev/null +++ b/test/source/symlink-test/symlinked-dir @@ -0,0 +1 @@ +../css \ No newline at end of file diff --git a/test/source/symlink-test/symlinked-file b/test/source/symlink-test/symlinked-file new file mode 100644 index 0000000..79c5d6f --- /dev/null +++ b/test/source/symlink-test/symlinked-file @@ -0,0 +1 @@ +../index.html \ No newline at end of file diff --git a/test/source/symlink-test/symlinked-file-outside-source b/test/source/symlink-test/symlinked-file-outside-source new file mode 100644 index 0000000..3594e94 --- /dev/null +++ b/test/source/symlink-test/symlinked-file-outside-source @@ -0,0 +1 @@ +/etc/passwd \ No newline at end of file diff --git a/test/source/trailing-dots...md b/test/source/trailing-dots...md new file mode 100644 index 0000000..2ec72a7 --- /dev/null +++ b/test/source/trailing-dots...md @@ -0,0 +1,3 @@ +--- +title: Ellipsis Path +--- diff --git a/test/source/unpublished.html b/test/source/unpublished.html new file mode 100644 index 0000000..265b606 --- /dev/null +++ b/test/source/unpublished.html @@ -0,0 +1,7 @@ +--- +layout: default +title: Not published! +published: false +--- + +This should *not* be published! diff --git a/test/source/win/_posts/2009-05-24-yaml-linebreak.markdown b/test/source/win/_posts/2009-05-24-yaml-linebreak.markdown new file mode 100644 index 0000000..0797531 --- /dev/null +++ b/test/source/win/_posts/2009-05-24-yaml-linebreak.markdown @@ -0,0 +1,7 @@ +--- +layout: post +title: "Test title" +tag: "Ruby" +--- + +This is the content \ No newline at end of file diff --git a/test/source/z_category/_posts/2008-09-23-categories.markdown b/test/source/z_category/_posts/2008-09-23-categories.markdown new file mode 100644 index 0000000..6a5e009 --- /dev/null +++ b/test/source/z_category/_posts/2008-09-23-categories.markdown @@ -0,0 +1,6 @@ +--- +layout: default +title: Categories +--- + +Categories _should_ work. Even if ordered after index. \ No newline at end of file diff --git a/test/test_ansi.rb b/test/test_ansi.rb new file mode 100644 index 0000000..3735cf1 --- /dev/null +++ b/test/test_ansi.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +require "helper" + +class TestAnsi < JekyllUnitTest + context nil do + setup do + @subject = Jekyll::Utils::Ansi + end + + Jekyll::Utils::Ansi::COLORS.each_key do |color| + should "respond_to? #{color}" do + assert_respond_to(@subject, color) + end + end + + should "be able to strip colors" do + assert_equal "hello", @subject.strip(@subject.yellow(@subject.red("hello"))) + end + + should "be able to detect colors" do + assert @subject.has?(@subject.yellow("hello")) + end + end +end diff --git a/test/test_cleaner.rb b/test/test_cleaner.rb new file mode 100644 index 0000000..3b0294a --- /dev/null +++ b/test/test_cleaner.rb @@ -0,0 +1,107 @@ +# frozen_string_literal: true + +require "helper" + +class TestCleaner < JekyllUnitTest + context "directory in keep_files" do + setup do + clear_dest + + FileUtils.mkdir_p(dest_dir("to_keep/child_dir")) + FileUtils.touch(File.join(dest_dir("to_keep"), "index.html")) + FileUtils.touch(File.join(dest_dir("to_keep/child_dir"), "index.html")) + + @site = fixture_site + @site.keep_files = ["to_keep/child_dir"] + + @cleaner = Cleaner.new(@site) + @cleaner.cleanup! + end + + teardown do + FileUtils.rm_rf(dest_dir("to_keep")) + end + + should "keep the parent directory" do + assert_exist dest_dir("to_keep") + end + + should "keep the child directory" do + assert_exist dest_dir("to_keep", "child_dir") + end + + should "keep the file in the directory in keep_files" do + assert_exist dest_dir("to_keep", "child_dir", "index.html") + end + + should "delete the file in the directory not in keep_files" do + refute_exist dest_dir("to_keep", "index.html") + end + end + + context "non-nested directory & similarly-named directory *not* in keep_files" do + setup do + clear_dest + + FileUtils.mkdir_p(dest_dir(".git/child_dir")) + FileUtils.mkdir_p(dest_dir("username.github.io")) + FileUtils.touch(File.join(dest_dir(".git"), "index.html")) + FileUtils.touch(File.join(dest_dir("username.github.io"), "index.html")) + + @site = fixture_site + @site.keep_files = [".git"] + + @cleaner = Cleaner.new(@site) + @cleaner.cleanup! + end + + teardown do + FileUtils.rm_rf(dest_dir(".git")) + FileUtils.rm_rf(dest_dir("username.github.io")) + end + + should "keep the file in the directory in keep_files" do + assert_path_exists(File.join(dest_dir(".git"), "index.html")) + end + + should "delete the file in the directory not in keep_files" do + refute_path_exists(File.join(dest_dir("username.github.io"), "index.html")) + end + + should "delete the directory not in keep_files" do + refute_path_exists(dest_dir("username.github.io")) + end + end + + context "directory containing no files and non-empty directories" do + setup do + clear_dest + + FileUtils.mkdir_p(source_dir("no_files_inside", "child_dir")) + FileUtils.touch(source_dir("no_files_inside", "child_dir", "index.html")) + + @site = fixture_site + @site.process + + @cleaner = Cleaner.new(@site) + @cleaner.cleanup! + end + + teardown do + FileUtils.rm_rf(source_dir("no_files_inside")) + FileUtils.rm_rf(dest_dir("no_files_inside")) + end + + should "keep the parent directory" do + assert_exist dest_dir("no_files_inside") + end + + should "keep the child directory" do + assert_exist dest_dir("no_files_inside", "child_dir") + end + + should "keep the file" do + assert_exist source_dir("no_files_inside", "child_dir", "index.html") + end + end +end diff --git a/test/test_coffeescript.rb b/test/test_coffeescript.rb new file mode 100644 index 0000000..6c6c376 --- /dev/null +++ b/test/test_coffeescript.rb @@ -0,0 +1,49 @@ +# frozen_string_literal: true + +require "helper" + +class TestCoffeeScript < JekyllUnitTest + context "converting CoffeeScript" do + setup do + External.require_with_graceful_fail("jekyll-coffeescript") + @site = fixture_site + @site.process + @test_coffeescript_file = dest_dir("js/coffeescript.js") + @js_output = <<~JS + (function() { + $(function() { + var cube, cubes, list, num, square; + list = [1, 2, 3, 4, 5]; + square = function(x) { + return x * x; + }; + cube = function(x) { + return square(x) * x; + }; + cubes = (function() { + var i, len, results; + results = []; + for (i = 0, len = list.length; i < len; i++) { + num = list[i]; + results.push(math.cube(num)); + } + return results; + })(); + if (typeof elvis !== "undefined" && elvis !== null) { + return alert("I knew it!"); + } + }); + + }).call(this); + JS + end + + should "write a JS file in place" do + assert_exist @test_coffeescript_file + end + + should "produce JS" do + assert_equal @js_output, File.read(@test_coffeescript_file) + end + end +end diff --git a/test/test_collections.rb b/test/test_collections.rb new file mode 100644 index 0000000..65b4b70 --- /dev/null +++ b/test/test_collections.rb @@ -0,0 +1,355 @@ +# frozen_string_literal: true + +require "helper" + +class TestCollections < JekyllUnitTest + context "an evil collection" do + setup do + @collection = Jekyll::Collection.new(fixture_site, "../../etc/password") + end + + should "sanitize the label name" do + assert_equal "....etcpassword", @collection.label + end + + should "have a sanitized relative path name" do + assert_equal "_....etcpassword", @collection.relative_directory + end + + should "have a sanitized full path" do + assert_equal @collection.directory, source_dir("_....etcpassword") + end + end + + context "a simple collection" do + setup do + @collection = Jekyll::Collection.new(fixture_site, "methods") + end + + should "sanitize the label name" do + assert_equal "methods", @collection.label + end + + should "have default URL template" do + assert_equal "/:collection/:path:output_ext", @collection.url_template + end + + should "contain no docs when initialized" do + assert_empty @collection.docs + end + + should "know its relative directory" do + assert_equal "_methods", @collection.relative_directory + end + + should "know the full path to itself on the filesystem" do + assert_equal @collection.directory, source_dir("_methods") + end + + context "when turned into Liquid" do + should "have a label attribute" do + assert_equal "methods", @collection.to_liquid["label"] + end + + should "have a docs attribute" do + assert_equal [], @collection.to_liquid["docs"] + end + + should "have a files attribute" do + assert_equal [], @collection.to_liquid["files"] + end + + should "have a directory attribute" do + assert_equal @collection.to_liquid["directory"], source_dir("_methods") + end + + should "have a relative_directory attribute" do + assert_equal "_methods", @collection.to_liquid["relative_directory"] + end + + should "have a output attribute" do + refute @collection.to_liquid["output"] + end + end + + should "know whether it should be written or not" do + refute @collection.write? + @collection.metadata["output"] = true + assert @collection.write? + @collection.metadata.delete "output" + end + end + + context "with no collections specified" do + setup do + @site = fixture_site + @site.process + end + + should "contain only the default collections" do + expected = {} + refute_equal expected, @site.collections + refute_nil @site.collections + end + end + + context "a collection with permalink" do + setup do + @site = fixture_site( + "collections" => { + "methods" => { + "permalink" => "/awesome/:path/", + }, + } + ) + @site.process + @collection = @site.collections["methods"] + end + + should "have custom URL template" do + assert_equal "/awesome/:path/", @collection.url_template + end + end + + context "with a collection" do + setup do + @site = fixture_site( + "collections" => ["methods"] + ) + @site.process + @collection = @site.collections["methods"] + end + + should "create a Hash mapping label to Collection instance" do + assert @site.collections.is_a?(Hash) + refute_nil @site.collections["methods"] + assert @site.collections["methods"].is_a? Jekyll::Collection + end + + should "collects docs in an array on the Collection object" do + assert @site.collections["methods"].docs.is_a? Array + @site.collections["methods"].docs.each do |doc| + assert doc.is_a? Jekyll::Document + # rubocop:disable Style/WordArray + assert_includes %w( + _methods/configuration.md + _methods/sanitized_path.md + _methods/collection/entries + _methods/site/generate.md + _methods/site/initialize.md + _methods/um_hi.md + _methods/escape-+\ #%20[].md + _methods/yaml_with_dots.md + _methods/3940394-21-9393050-fifif1323-test.md + _methods/trailing-dots...md + ), doc.relative_path + # rubocop:enable Style/WordArray + end + end + + should "not include files from base dir which start with an underscore" do + refute_includes @collection.filtered_entries, "_do_not_read_me.md" + end + + should "not include files which start with an underscore in a subdirectory" do + refute_includes @collection.filtered_entries, "site/_dont_include_me_either.md" + end + + should "not include the underscored files in the list of docs" do + refute_includes @collection.docs.map(&:relative_path), "_methods/_do_not_read_me.md" + refute_includes @collection.docs.map(&:relative_path), + "_methods/site/_dont_include_me_either.md" + end + end + + context "with a collection with metadata" do + setup do + @site = fixture_site( + "collections" => { + "methods" => { + "foo" => "bar", + "baz" => "whoo", + }, + } + ) + @site.process + @collection = @site.collections["methods"] + end + + should "extract the configuration collection information as metadata" do + expected = { "foo" => "bar", "baz" => "whoo" } + assert_equal expected, @collection.metadata + end + end + + context "with a collection with metadata to sort items by attribute" do + setup do + @site = fixture_site( + "collections" => { + "methods" => { + "output" => true, + }, + "tutorials" => { + "output" => true, + "sort_by" => "lesson", + }, + } + ) + @site.process + @tutorials_collection = @site.collections["tutorials"] + + @actual_array = @tutorials_collection.docs.map(&:relative_path) + end + + should "sort documents in a collection with 'sort_by' metadata set to a " \ + "FrontMatter key 'lesson'" do + default_tutorials_array = %w( + _tutorials/dive-in-and-publish-already.md + _tutorials/extending-with-plugins.md + _tutorials/getting-started.md + _tutorials/graduation-day.md + _tutorials/lets-roll.md + _tutorials/tip-of-the-iceberg.md + ) + tutorials_sorted_by_lesson_array = %w( + _tutorials/getting-started.md + _tutorials/lets-roll.md + _tutorials/dive-in-and-publish-already.md + _tutorials/tip-of-the-iceberg.md + _tutorials/extending-with-plugins.md + _tutorials/graduation-day.md + ) + refute_equal default_tutorials_array, @actual_array + assert_equal tutorials_sorted_by_lesson_array, @actual_array + end + end + + context "with a collection with metadata to rearrange items" do + setup do + @site = fixture_site( + "collections" => { + "methods" => { + "output" => true, + }, + "tutorials" => { + "output" => true, + "order" => [ + "getting-started.md", + "lets-roll.md", + "dive-in-and-publish-already.md", + "tip-of-the-iceberg.md", + "graduation-day.md", + "extending-with-plugins.md", + ], + }, + } + ) + @site.process + @tutorials_collection = @site.collections["tutorials"] + + @actual_array = @tutorials_collection.docs.map(&:relative_path) + end + + should "sort documents in a collection in the order outlined in the config file" do + default_tutorials_array = %w( + _tutorials/dive-in-and-publish-already.md + _tutorials/extending-with-plugins.md + _tutorials/getting-started.md + _tutorials/graduation-day.md + _tutorials/lets-roll.md + _tutorials/tip-of-the-iceberg.md + ) + tutorials_rearranged_in_config_array = %w( + _tutorials/getting-started.md + _tutorials/lets-roll.md + _tutorials/dive-in-and-publish-already.md + _tutorials/tip-of-the-iceberg.md + _tutorials/graduation-day.md + _tutorials/extending-with-plugins.md + ) + refute_equal default_tutorials_array, @actual_array + assert_equal tutorials_rearranged_in_config_array, @actual_array + end + end + + context "in safe mode" do + setup do + @site = fixture_site( + "collections" => ["methods"], + "safe" => true + ) + @site.process + @collection = @site.collections["methods"] + end + + should "include the symlinked file as it resolves to inside site.source" do + assert_includes @collection.filtered_entries, "um_hi.md" + refute_includes @collection.filtered_entries, "/um_hi.md" + end + + should "include the symlinked file from site.source in the list of docs" do + # no support for including symlinked file on Windows + skip_if_windows "Jekyll does not currently support symlinks on Windows." + + assert_includes @collection.docs.map(&:relative_path), "_methods/um_hi.md" + end + end + + context "with dots in the filenames" do + setup do + @site = fixture_site( + "collections" => ["with.dots"], + "safe" => true + ) + @site.process + @collection = @site.collections["with.dots"] + end + + should "exist" do + refute_nil @collection + end + + should "contain one document" do + assert_equal 4, @collection.docs.size + end + + should "allow dots in the filename" do + assert_equal "_with.dots", @collection.relative_directory + end + + should "read document in subfolders with dots" do + assert( + @collection.docs.any? { |d| d.path.include?("all.dots") } + ) + end + end + + context "a collection with included dotfiles" do + setup do + @site = fixture_site( + "collections" => { + "methods" => { + "permalink" => "/awesome/:path/", + }, + }, + "include" => %w(.htaccess .gitignore) + ) + @site.process + @collection = @site.collections["methods"] + end + + should "contain .htaccess file" do + assert(@collection.files.any? { |d| d.name == ".htaccess" }) + end + + should "contain .gitignore file" do + assert(@collection.files.any? { |d| d.name == ".gitignore" }) + end + + should "have custom URL in static file" do + assert( + @collection.files.any? { |d| d.url.include?("/awesome/with.dots/") } + ) + end + end +end diff --git a/test/test_command.rb b/test/test_command.rb new file mode 100644 index 0000000..d202628 --- /dev/null +++ b/test/test_command.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +require "helper" + +class TestCommand < JekyllUnitTest + context "when calling .add_build_options" do + should "add common options" do + cmd = Object.new + mocks_expect(cmd).to receive(:option).at_least(:once) + Command.add_build_options(cmd) + end + end + + context "when calling .process_site" do + context "when fatal error occurs" do + should "exit with non-zero error code" do + site = Object.new + def site.process + raise Jekyll::Errors::FatalException + end + error = assert_raises(SystemExit) { Command.process_site(site) } + refute_equal 0, error.status + end + end + end +end diff --git a/test/test_commands_serve.rb b/test/test_commands_serve.rb new file mode 100644 index 0000000..fe9d972 --- /dev/null +++ b/test/test_commands_serve.rb @@ -0,0 +1,316 @@ +# frozen_string_literal: true + +require "webrick" +require "mercenary" +require "helper" +require "httpclient" +require "openssl" +require "tmpdir" + +class TestCommandsServe < JekyllUnitTest + def custom_opts(what) + @cmd.send( + :webrick_opts, what + ) + 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 + skip_if_windows "EventMachine support on Windows is limited" + skip("Refinements are not fully supported in JRuby") if jruby? + + @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*!, "") + <!DOCTYPE HTML> + <html lang="en-US"> + <head> + <meta charset="UTF-8"> + <title>Hello World</title> + </head> + <body> + <p>Hello! I am a simple web page.</p> + </body> + </html> + HTML + + File.write(File.join(@destination, "hello.html"), simple_page) + 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 + 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 + 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 + 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 + 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 + @cmd = Jekyll::Commands::Serve + Mercenary.program(:jekyll) do |p| + @merc = @cmd.init_with_program( + p + ) + end + Jekyll.sites.clear + allow(SafeYAML).to receive(:load_file).and_return({}) + allow(Jekyll::Commands::Build).to receive(:build).and_return("") + end + teardown do + Jekyll.sites.clear + end + + should "label itself" do + assert_equal :serve, @merc.name + end + + should "have aliases" do + assert_includes @merc.aliases, :s + assert_includes @merc.aliases, :server + end + + should "have a description" do + refute_nil( + @merc.description + ) + end + + should "have an action" do + refute_empty( + @merc.actions + ) + end + + should "not have an empty options set" do + refute_empty( + @merc.options + ) + end + + context "with custom options" do + should "create a default set of mimetypes" do + refute_nil custom_opts({})[ + :MimeTypes + ] + end + + should "use user destinations" do + assert_equal "foo", custom_opts("destination" => "foo")[ + :DocumentRoot + ] + end + + should "use user port" do + # WHAT?!?!1 Over 9000? That's impossible. + assert_equal 9001, custom_opts("port" => 9001)[ + :Port + ] + end + + should "use empty directory index list when show_dir_listing is true" do + opts = { "show_dir_listing" => true } + assert_empty custom_opts(opts)[:DirectoryIndex] + end + + should "keep config between build and serve" do + options = { + "config" => %w(_config.yml _development.yml), + "serving" => true, + "watch" => false, # for not having guard output when running the tests + "url" => "http://localhost:4000", + } + config = Jekyll::Configuration.from(options) + + allow(Jekyll::Command).to( + receive(:configuration_from_options).with(options).and_return(config) + ) + allow(Jekyll::Command).to( + receive(:configuration_from_options).with(config).and_return(config) + ) + + expect(Jekyll::Commands::Build).to( + receive(:process).with(config).and_call_original + ) + expect(Jekyll::Commands::Serve).to receive(:process).with(config) + @merc.execute(:serve, options) + end + + context "in development environment" do + setup do + expect(Jekyll).to receive(:env).and_return("development") + expect(Jekyll::Commands::Serve).to receive(:start_up_webrick) + end + should "set the site url by default to `http://localhost:4000`" do + @merc.execute(:serve, "watch" => false, "url" => "https://jekyllrb.com/") + + assert_equal 1, Jekyll.sites.count + assert_equal "http://localhost:4000", Jekyll.sites.first.config["url"] + end + + should "take `host`, `port` and `ssl` into consideration if set" do + @merc.execute(:serve, + "watch" => false, + "host" => "example.com", + "port" => "9999", + "url" => "https://jekyllrb.com/", + "ssl_cert" => "foo", + "ssl_key" => "bar") + + assert_equal 1, Jekyll.sites.count + assert_equal "https://example.com:9999", Jekyll.sites.first.config["url"] + end + end + + context "not in development environment" do + should "not update the site url" do + expect(Jekyll).to receive(:env).and_return("production") + expect(Jekyll::Commands::Serve).to receive(:start_up_webrick) + @merc.execute(:serve, "watch" => false, "url" => "https://jekyllrb.com/") + + assert_equal 1, Jekyll.sites.count + assert_equal "https://jekyllrb.com/", Jekyll.sites.first.config["url"] + end + end + + context "verbose" do + should "debug when verbose" do + assert_equal 5, custom_opts("verbose" => true)[:Logger].level + end + + should "warn when not verbose" do + assert_equal 3, custom_opts({})[:Logger].level + end + end + + context "enabling SSL" do + should "raise if enabling without key or cert" do + assert_raises RuntimeError do + custom_opts( + "ssl_key" => "foo" + ) + end + + assert_raises RuntimeError do + custom_opts( + "ssl_key" => "foo" + ) + end + end + + should "allow SSL with a key and cert" do + expect(OpenSSL::PKey::RSA).to receive(:new).and_return("c2") + expect(OpenSSL::X509::Certificate).to receive(:new).and_return("c1") + allow(File).to receive(:read).and_return("foo") + + result = custom_opts( + "ssl_cert" => "foo", + "source" => "bar", + "enable_ssl" => true, + "ssl_key" => "bar" + ) + + assert result[:SSLEnable] + assert_equal "c2", result[:SSLPrivateKey] + assert_equal "c1", result[:SSLCertificate] + end + end + end + + should "read `configuration` only once" do + allow(Jekyll::Commands::Serve).to receive(:start_up_webrick) + + expect(Jekyll).to receive(:configuration).once.and_call_original + @merc.execute(:serve, "watch" => false) + end + end +end diff --git a/test/test_commands_serve_servlet.rb b/test/test_commands_serve_servlet.rb new file mode 100644 index 0000000..0ccc49b --- /dev/null +++ b/test/test_commands_serve_servlet.rb @@ -0,0 +1,48 @@ +# 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 + + should "find xhtml file" do + get("/bar/foo") do |response| + assert_equal("200", response.code) + assert_equal( + '<html xmlns="http://www.w3.org/1999/xhtml">Content of foo.xhtml</html>', + response.body.strip + ) + end + end + end +end diff --git a/test/test_configuration.rb b/test/test_configuration.rb new file mode 100644 index 0000000..5a8f101 --- /dev/null +++ b/test/test_configuration.rb @@ -0,0 +1,542 @@ +# frozen_string_literal: true + +require "helper" +require "colorator" + +class TestConfiguration < JekyllUnitTest + test_config = { + "source" => new(nil).source_dir, + "destination" => dest_dir, + } + + context ".from" do + should "create a Configuration object" do + assert_instance_of Configuration, Configuration.from({}) + end + + should "merge input over defaults" do + result = Configuration.from("source" => "blah") + refute_equal result["source"], Configuration::DEFAULTS["source"] + assert_equal "blah", result["source"] + end + + should "return a valid Configuration instance" do + assert_instance_of Configuration, Configuration.from({}) + end + + should "add default collections" do + result = Configuration.from({}) + expected = { "posts" => { + "output" => true, + "permalink" => "/:categories/:year/:month/:day/:title:output_ext", + } } + assert_equal expected, result["collections"] + end + + should "NOT backwards-compatibilize" do + assert( + Configuration.from("watch" => true)["watch"], + "Expected the 'watch' key to not be removed." + ) + end + end + + context "the effective site configuration" do + setup do + @config = Configuration.from( + "exclude" => %w( + README.md Licence + ) + ) + end + + should "always exclude node_modules" do + assert_includes @config["exclude"], "node_modules" + end + + should "always exclude Gemfile and related paths" do + exclude = @config["exclude"] + assert_includes exclude, "Gemfile" + assert_includes exclude, "Gemfile.lock" + assert_includes exclude, "gemfiles" + end + + should "always exclude ruby vendor directories" do + exclude = @config["exclude"] + assert_includes exclude, "vendor/bundle/" + assert_includes exclude, "vendor/cache/" + assert_includes exclude, "vendor/gems/" + assert_includes exclude, "vendor/ruby/" + end + + should "always exclude default cache directories" do + exclude = @config["exclude"] + assert_includes exclude, ".sass-cache" + assert_includes exclude, ".jekyll-cache" + end + end + + context "#add_default_collections" do + should "no-op if collections is nil" do + result = Configuration[{ "collections" => nil }].add_default_collections + assert_nil result["collections"] + end + + should "turn an array into a hash" do + result = Configuration[{ "collections" => %w(methods) }].add_default_collections + assert_instance_of Hash, result["collections"] + expected = { "posts" => { "output" => true }, "methods" => {} } + assert_equal expected, result["collections"] + end + + should "only assign collections.posts.permalink if a permalink is specified" do + result = Configuration[{ "permalink" => "pretty", "collections" => {} }] + .add_default_collections + + expected = { + "posts" => { + "output" => true, + "permalink" => "/:categories/:year/:month/:day/:title/", + }, + } + + assert_equal expected, result["collections"] + + result = Configuration[{ "permalink" => nil, "collections" => {} }].add_default_collections + expected = { "posts" => { "output" => true } } + + assert_equal expected, result["collections"] + end + + should "forces posts to output" do + result = Configuration[{ "collections" => { "posts" => { "output" => false } } }] + .add_default_collections + assert result["collections"]["posts"]["output"] + end + end + + context "#stringify_keys" do + setup do + @mixed_keys = Configuration[{ + "markdown" => "kramdown", + :permalink => "date", + "baseurl" => "/", + :include => [".htaccess"], + :source => "./", + }] + + @string_keys = Configuration[{ + "markdown" => "kramdown", + "permalink" => "date", + "baseurl" => "/", + "include" => [".htaccess"], + "source" => "./", + }] + end + + should "stringify symbol keys" do + assert_equal @string_keys, @mixed_keys.stringify_keys + end + + should "not mess with keys already strings" do + assert_equal @string_keys, @string_keys.stringify_keys + end + end + + context "#config_files" do + setup do + @config = Configuration[{ "source" => source_dir }] + @no_override = {} + @one_config_file = { "config" => "config.yml" } + @multiple_files = { + "config" => %w(config/site.yml config/deploy.toml configuration.yml), + } + end + + should "always return an array" do + assert @config.config_files(@no_override).is_a?(Array) + assert @config.config_files(@one_config_file).is_a?(Array) + assert @config.config_files(@multiple_files).is_a?(Array) + end + + should "return the default config path if no config files are specified" do + assert_equal [source_dir("_config.yml")], @config.config_files(@no_override) + end + + should "return .yaml if it exists but .yml does not" do + allow(File).to receive(:exist?).with(source_dir("_config.yml")).and_return(false) + allow(File).to receive(:exist?).with(source_dir("_config.yaml")).and_return(true) + assert_equal [source_dir("_config.yaml")], @config.config_files(@no_override) + end + + should "return .yml if both .yml and .yaml exist" do + allow(File).to receive(:exist?).with(source_dir("_config.yml")).and_return(true) + assert_equal [source_dir("_config.yml")], @config.config_files(@no_override) + end + + should "return .toml if that exists" do + allow(File).to receive(:exist?).with(source_dir("_config.yml")).and_return(false) + allow(File).to receive(:exist?).with(source_dir("_config.yaml")).and_return(false) + allow(File).to receive(:exist?).with(source_dir("_config.toml")).and_return(true) + assert_equal [source_dir("_config.toml")], @config.config_files(@no_override) + end + + should "return .yml if both .yml and .toml exist" do + allow(File).to receive(:exist?).with(source_dir("_config.yml")).and_return(true) + allow(File).to receive(:exist?).with(source_dir("_config.toml")).and_return(true) + assert_equal [source_dir("_config.yml")], @config.config_files(@no_override) + end + + should "return the config if given one config file" do + assert_equal %w(config.yml), @config.config_files(@one_config_file) + end + + should "return an array of the config files if given many config files" do + assert_equal( + %w(config/site.yml config/deploy.toml configuration.yml), + @config.config_files(@multiple_files) + ) + end + end + + context "#read_config_file" do + setup do + @config = Configuration[{ "source" => source_dir("empty.yml") }] + end + + should "not raise an error on empty files" do + allow(SafeYAML).to receive(:load_file).with(File.expand_path("empty.yml")).and_return(false) + Jekyll.logger.log_level = :warn + @config.read_config_file("empty.yml") + Jekyll.logger.log_level = :info + end + end + + context "#read_config_files" do + setup do + @config = Configuration[{ "source" => source_dir }] + end + + should "continue to read config files if one is empty" do + allow(SafeYAML).to receive(:load_file).with(File.expand_path("empty.yml")).and_return(false) + allow(SafeYAML).to receive(:load_file).with(File.expand_path("not_empty.yml")).and_return( + "foo" => "bar" + ) + Jekyll.logger.log_level = :warn + read_config = @config.read_config_files(%w(empty.yml not_empty.yml)) + Jekyll.logger.log_level = :info + assert_equal "bar", read_config["foo"] + end + end + + context "#validate" do + setup do + @config = Configuration[{ + "auto" => true, + "watch" => true, + "server" => true, + "pygments" => true, + "layouts" => true, + "data_source" => true, + "gems" => [], + }] + end + + should "raise an error if `exclude` key is a string" do + config = Configuration[{ "exclude" => "READ-ME.md, Gemfile,CONTRIBUTING.hello.markdown" }] + assert_raises(Jekyll::Errors::InvalidConfigurationError) { config.validate } + end + + should "raise an error if `include` key is a string" do + config = Configuration[{ "include" => "STOP_THE_PRESSES.txt,.heloses, .git" }] + assert_raises(Jekyll::Errors::InvalidConfigurationError) { config.validate } + end + + should "raise an error if `plugins` key is a string" do + config = Configuration[{ "plugins" => "_plugin" }] + assert_raises(Jekyll::Errors::InvalidConfigurationError) { config.validate } + end + + should "not rename configuration keys" do + assert @config.key?("layouts") + assert @config.validate.key?("layouts") + refute @config.validate.key?("layouts_dir") + + assert @config.key?("data_source") + assert @config.validate.key?("data_source") + refute @config.validate.key?("data_dir") + + assert @config.key?("gems") + assert @config.validate.key?("gems") + refute @config.validate.key?("plugins") + end + end + + context "loading configuration" do + setup do + @path = source_dir("_config.yml") + @user_config = File.join(Dir.pwd, "my_config_file.yml") + end + + should "fire warning with no _config.yml" do + allow(SafeYAML).to receive(:load_file).with(@path) do + raise SystemCallError, "No such file or directory - #{@path}" + end + allow($stderr).to receive(:puts).with( + Colorator.yellow("Configuration file: none") + ) + assert_equal site_configuration, Jekyll.configuration(test_config) + end + + should "load configuration as hash" do + allow(SafeYAML).to receive(:load_file).with(@path).and_return({}) + allow($stdout).to receive(:puts).with("Configuration file: #{@path}") + assert_equal site_configuration, Jekyll.configuration(test_config) + end + + should "fire warning with bad config" do + allow(SafeYAML).to receive(:load_file).with(@path).and_return([]) + allow($stderr) + .to receive(:puts) + .and_return( + "WARNING: ".rjust(20) + + Colorator.yellow("Error reading configuration. Using defaults (and options).") + ) + allow($stderr) + .to receive(:puts) + .and_return(Colorator.yellow("Configuration file: (INVALID) #{@path}")) + assert_equal site_configuration, Jekyll.configuration(test_config) + end + + should "fire warning when user-specified config file isn't there" do + allow(SafeYAML).to receive(:load_file).with(@user_config) do + raise SystemCallError, "No such file or directory - #{@user_config}" + end + allow($stderr) + .to receive(:puts) + .with(Colorator.red( + "Fatal: ".rjust(20) + \ + "The configuration file '#{@user_config}' could not be found." + )) + assert_raises LoadError do + Jekyll.configuration("config" => [@user_config]) + end + end + + should "not clobber YAML.load to the dismay of other libraries" do + assert_equal :foo, YAML.load(":foo") + # as opposed to: assert_equal ':foo', SafeYAML.load(':foo') + end + end + + context "loading config from external file" do + setup do + @paths = { + :default => source_dir("_config.yml"), + :other => source_dir("_config.live.yml"), + :toml => source_dir("_config.dev.toml"), + :empty => "", + } + end + + should "load default plus posts config if no config_file is set" do + allow(SafeYAML).to receive(:load_file).with(@paths[:default]).and_return({}) + allow($stdout).to receive(:puts).with("Configuration file: #{@paths[:default]}") + assert_equal site_configuration, Jekyll.configuration(test_config) + end + + should "load different config if specified" do + allow(SafeYAML) + .to receive(:load_file) + .with(@paths[:other]) + .and_return("baseurl" => "http://example.com") + allow($stdout).to receive(:puts).with("Configuration file: #{@paths[:other]}") + assert_equal \ + site_configuration( + "baseurl" => "http://example.com", + "config" => @paths[:other] + ), + Jekyll.configuration(test_config.merge("config" => @paths[:other])) + end + + should "load different config if specified with symbol key" do + allow(SafeYAML).to receive(:load_file).with(@paths[:default]).and_return({}) + allow(SafeYAML) + .to receive(:load_file) + .with(@paths[:other]) + .and_return("baseurl" => "http://example.com") + allow($stdout).to receive(:puts).with("Configuration file: #{@paths[:other]}") + assert_equal \ + site_configuration( + "baseurl" => "http://example.com", + "config" => @paths[:other] + ), + Jekyll.configuration(test_config.merge(:config => @paths[:other])) + end + + should "load default config if path passed is empty" do + allow(SafeYAML).to receive(:load_file).with(@paths[:default]).and_return({}) + allow($stdout).to receive(:puts).with("Configuration file: #{@paths[:default]}") + assert_equal \ + site_configuration("config" => [@paths[:empty]]), + Jekyll.configuration(test_config.merge("config" => [@paths[:empty]])) + end + + should "successfully load a TOML file" do + Jekyll.logger.log_level = :warn + assert_equal \ + site_configuration( + "baseurl" => "/you-beautiful-blog-you", + "title" => "My magnificent site, wut", + "config" => [@paths[:toml]] + ), + Jekyll.configuration(test_config.merge("config" => [@paths[:toml]])) + Jekyll.logger.log_level = :info + end + + should "load multiple config files" do + External.require_with_graceful_fail("tomlrb") + + allow(SafeYAML).to receive(:load_file).with(@paths[:default]).and_return({}) + allow(SafeYAML).to receive(:load_file).with(@paths[:other]).and_return({}) + allow(Tomlrb).to receive(:load_file).with(@paths[:toml]).and_return({}) + allow($stdout).to receive(:puts).with("Configuration file: #{@paths[:default]}") + allow($stdout).to receive(:puts).with("Configuration file: #{@paths[:other]}") + allow($stdout).to receive(:puts).with("Configuration file: #{@paths[:toml]}") + assert_equal( + site_configuration( + "config" => [@paths[:default], @paths[:other], @paths[:toml]] + ), + Jekyll.configuration( + test_config.merge( + "config" => [@paths[:default], @paths[:other], @paths[:toml]] + ) + ) + ) + end + + should "load multiple config files and last config should win" do + allow(SafeYAML) + .to receive(:load_file) + .with(@paths[:default]) + .and_return("baseurl" => "http://example.dev") + allow(SafeYAML) + .to receive(:load_file) + .with(@paths[:other]) + .and_return("baseurl" => "http://example.com") + allow($stdout) + .to receive(:puts) + .with("Configuration file: #{@paths[:default]}") + allow($stdout) + .to receive(:puts) + .with("Configuration file: #{@paths[:other]}") + assert_equal \ + site_configuration( + "baseurl" => "http://example.com", + "config" => [@paths[:default], @paths[:other]] + ), + Jekyll.configuration( + test_config.merge("config" => [@paths[:default], @paths[:other]]) + ) + end + end + + context "#add_default_collections" do + should "not do anything if collections is nil" do + conf = Configuration[default_configuration].tap { |c| c["collections"] = nil } + assert_equal conf.add_default_collections, conf + assert_nil conf.add_default_collections["collections"] + end + + should "converts collections to a hash if an array" do + conf = Configuration[default_configuration].tap do |c| + c["collections"] = ["docs"] + end + assert_equal conf.add_default_collections, conf.merge( + "collections" => { + "docs" => {}, + "posts" => { + "output" => true, + "permalink" => "/:categories/:year/:month/:day/:title:output_ext", + }, + } + ) + end + + should "force collections.posts.output = true" do + conf = Configuration[default_configuration].tap do |c| + c["collections"] = { "posts" => { "output" => false } } + end + assert_equal conf.add_default_collections, conf.merge( + "collections" => { + "posts" => { + "output" => true, + "permalink" => "/:categories/:year/:month/:day/:title:output_ext", + }, + } + ) + end + + should "set collections.posts.permalink if it's not set" do + conf = Configuration[default_configuration] + assert_equal conf.add_default_collections, conf.merge( + "collections" => { + "posts" => { + "output" => true, + "permalink" => "/:categories/:year/:month/:day/:title:output_ext", + }, + } + ) + end + + should "leave collections.posts.permalink alone if it is set" do + posts_permalink = "/:year/:title/" + conf = Configuration[default_configuration].tap do |c| + c["collections"] = { + "posts" => { "permalink" => posts_permalink }, + } + end + assert_equal conf.add_default_collections, conf.merge( + "collections" => { + "posts" => { + "output" => true, + "permalink" => posts_permalink, + }, + } + ) + end + end + + context "folded YAML string" do + setup do + @tester = Configuration.new + end + + should "ignore newlines in that string entirely from a sample file" do + config = Jekyll.configuration( + @tester.read_config_file( + source_dir("_config_folded.yml") + ) + ) + assert_equal( + "This string of text will ignore newlines till the next key.\n", + config["folded_string"] + ) + + assert_equal( + "This string of text will ignore newlines till the next key.", + config["clean_folded_string"] + ) + end + + should "ignore newlines in that string entirely from the template file" do + config = Jekyll.configuration( + @tester.read_config_file( + File.expand_path("../lib/site_template/_config.yml", File.dirname(__FILE__)) + ) + ) + assert_includes config["description"], "an awesome description" + refute_includes config["description"], "\n" + end + end +end diff --git a/test/test_convertible.rb b/test/test_convertible.rb new file mode 100644 index 0000000..ce84676 --- /dev/null +++ b/test/test_convertible.rb @@ -0,0 +1,84 @@ +# frozen_string_literal: true + +require "helper" +require "ostruct" + +class TestConvertible < JekyllUnitTest + context "YAML front matter" do + setup do + @convertible = OpenStruct.new( + "site" => Site.new(Jekyll.configuration( + "source" => File.expand_path("fixtures", __dir__) + )) + ) + @convertible.extend Jekyll::Convertible + @base = File.expand_path("fixtures", __dir__) + end + + should "parse the front matter correctly" do + ret = @convertible.read_yaml(@base, "front_matter.erb") + assert_equal({ "test" => "good" }, ret) + end + + should "not parse if the front matter is not at the start of the file" do + ret = @convertible.read_yaml(@base, "broken_front_matter1.erb") + assert_equal({}, ret) + end + + should "not parse if there is syntax error in front matter" do + name = "broken_front_matter2.erb" + out = capture_stderr do + ret = @convertible.read_yaml(@base, name) + assert_equal({}, ret) + end + assert_match(%r!YAML Exception!, out) + assert_match(%r!#{Regexp.escape(File.join(@base, name))}!, out) + end + + should "raise for broken front matter with `strict_front_matter` set" do + name = "broken_front_matter2.erb" + @convertible.site.config["strict_front_matter"] = true + assert_raises(Psych::SyntaxError) do + @convertible.read_yaml(@base, name) + end + end + + should "not allow ruby objects in YAML" do + out = capture_stderr do + @convertible.read_yaml(@base, "exploit_front_matter.erb") + end + refute_match(%r!undefined class/module DoesNotExist!, out) + end + + should "not parse if there is encoding error in file" do + name = "broken_front_matter3.erb" + out = capture_stderr do + ret = @convertible.read_yaml(@base, name, :encoding => "utf-8") + assert_equal({}, ret) + end + assert_match(%r!invalid byte sequence in UTF-8!, out) + assert_match(%r!#{Regexp.escape(File.join(@base, name))}!, out) + end + + should "parse the front matter but show an error if permalink is empty" do + name = "empty_permalink.erb" + assert_raises(Errors::InvalidPermalinkError) do + @convertible.read_yaml(@base, name) + end + end + + should "parse the front matter correctly without permalink" do + out = capture_stderr do + @convertible.read_yaml(@base, "front_matter.erb") + end + refute_match(%r!Invalid permalink!, out) + end + + should "not parse Liquid if disabled in front matter" do + name = "no_liquid.erb" + @convertible.read_yaml(@base, name) + ret = @convertible.content.strip + assert_equal("{% raw %}{% endraw %}", ret) + end + end +end diff --git a/test/test_data_reader.rb b/test/test_data_reader.rb new file mode 100644 index 0000000..0c0fd24 --- /dev/null +++ b/test/test_data_reader.rb @@ -0,0 +1,60 @@ +# frozen_string_literal: true + +require "helper" + +class TestDataReader < JekyllUnitTest + context "#sanitize_filename" do + setup do + @reader = DataReader.new(fixture_site) + end + + should "remove evil characters" do + assert_equal "helpwhathaveIdone", @reader.sanitize_filename( + "help/what^&$^#*(!^%*!#haveId&&&&&&&&&one" + ) + end + end + + context "with no csv options set" do + setup do + @reader = DataReader.new(fixture_site) + @parsed = [{ "id" => "1", "field_a" => "foo" }, { "id" => "2", "field_a" => "bar" }] + end + + should "parse CSV normally" do + assert_equal @parsed, @reader.read_data_file(File.expand_path("fixtures/sample.csv", __dir__)) + end + + should "parse TSV normally" do + assert_equal @parsed, @reader.read_data_file(File.expand_path("fixtures/sample.tsv", __dir__)) + end + end + + context "with csv options set" do + setup do + reader_config = { + "csv_converters" => [:numeric], + "headers" => false, + } + + @reader = DataReader.new( + fixture_site( + { + "csv_reader" => reader_config, + "tsv_reader" => reader_config, + } + ) + ) + + @parsed = [%w(id field_a), [1, "foo"], [2, "bar"]] + end + + should "parse CSV with options" do + assert_equal @parsed, @reader.read_data_file(File.expand_path("fixtures/sample.csv", __dir__)) + end + + should "parse TSV with options" do + assert_equal @parsed, @reader.read_data_file(File.expand_path("fixtures/sample.tsv", __dir__)) + end + end +end diff --git a/test/test_doctor_command.rb b/test/test_doctor_command.rb new file mode 100644 index 0000000..9a1e417 --- /dev/null +++ b/test/test_doctor_command.rb @@ -0,0 +1,42 @@ +# frozen_string_literal: true + +require "helper" +require "jekyll/commands/doctor" + +class TestDoctorCommand < JekyllUnitTest + context "URLs only differ by case" do + setup do + clear_dest + end + + should "return success on a valid site/page" do + @site = Site.new(Jekyll.configuration( + "source" => File.join(source_dir, "/_urls_differ_by_case_valid"), + "destination" => dest_dir + )) + @site.process + output = capture_stderr do + ret = Jekyll::Commands::Doctor.urls_only_differ_by_case(@site) + refute ret + end + assert_equal "", output + end + + # rubocop:disable Layout/LineLength + should "return warning for pages only differing by case" do + @site = Site.new(Jekyll.configuration( + "source" => File.join(source_dir, "/_urls_differ_by_case_invalid"), + "destination" => dest_dir + )) + @site.process + output = capture_stderr do + ret = Jekyll::Commands::Doctor.urls_only_differ_by_case(@site) + assert ret + end + assert_includes output, "Warning: The following URLs only differ by case. On a case-" \ + "insensitive file system one of the URLs will be overwritten by the " \ + "other: #{dest_dir}/about/index.html, #{dest_dir}/About/index.html" + end + # rubocop:enable Layout/LineLength + end +end diff --git a/test/test_document.rb b/test/test_document.rb new file mode 100644 index 0000000..f5f5cce --- /dev/null +++ b/test/test_document.rb @@ -0,0 +1,662 @@ +# frozen_string_literal: true + +require "helper" + +class TestDocument < JekyllUnitTest + def assert_equal_value(key, one, other) + assert_equal(one[key], other[key]) + end + + def setup_encoded_document(filename) + site = fixture_site("collections" => ["encodings"]) + site.process + Document.new(site.in_source_dir(File.join("_encodings", filename)), + :site => site, + :collection => site.collections["encodings"]).tap(&:read) + end + + def setup_document_with_dates(filename) + site = fixture_site("collections" => ["dates"]) + site.process + docs = nil + with_env("TZ", "UTC") do + docs = Document.new(site.in_source_dir(File.join("_dates", filename)), + :site => site, + :collection => site.collections["dates"]).tap(&:read) + end + docs + end + + context "a document in a collection" do + setup do + @site = fixture_site( + "collections" => ["methods"] + ) + @site.process + @document = @site.collections["methods"].docs.detect do |d| + d.relative_path == "_methods/configuration.md" + end + end + + should "exist" do + refute_nil @document + end + + should "know its relative path" do + assert_equal "_methods/configuration.md", @document.relative_path + end + + should "knows its extname" do + assert_equal ".md", @document.extname + end + + should "know its basename" do + assert_equal "configuration.md", @document.basename + end + + should "know its basename without extname" do + assert_equal "configuration", @document.basename_without_ext + end + + should "know its type" do + assert_equal :methods, @document.type + end + + should "know whether it's a YAML file" do + refute @document.yaml_file? + end + + should "know its data" do + assert_equal "Jekyll.configuration", @document.data["title"] + assert_equal "foo.bar", @document.data["whatever"] + end + + should "know its date" do + assert_nil @document.data["date"] + assert_equal Time.now.strftime("%Y/%m/%d"), @document.date.strftime("%Y/%m/%d") + assert_equal Time.now.strftime("%Y/%m/%d"), @document.to_liquid["date"].strftime("%Y/%m/%d") + end + + should "be jsonify-able" do + page_json = @document.to_liquid.to_json + parsed = JSON.parse(page_json) + + assert_equal "Jekyll.configuration", parsed["title"] + assert_equal "foo.bar", parsed["whatever"] + assert_equal "_methods/collection/entries", parsed["previous"]["path"] + assert_equal "Collection#entries", parsed["previous"]["title"] + + next_doc = parsed["next"] + assert_equal "_methods/escape-+ #%20[].md", next_doc["path"] + assert_equal "Jekyll.escape", next_doc["title"] + + next_prev_doc = next_doc["previous"] + assert_equal "Jekyll.configuration", next_prev_doc["title"] + assert_equal "_methods/configuration.md", next_prev_doc["path"] + assert_equal "_methods/escape-+ #%20[].md", next_prev_doc["next"]["path"] + assert_equal "_methods/collection/entries", next_prev_doc["previous"]["path"] + assert_nil next_prev_doc["next"]["next"] + assert_nil next_prev_doc["next"]["previous"] + assert_nil next_prev_doc["next"]["content"] + assert_nil next_prev_doc["next"]["excerpt"] + assert_nil next_prev_doc["next"]["output"] + + next_next_doc = next_doc["next"] + assert_equal "Jekyll.sanitized_path", next_next_doc["title"] + assert_equal "_methods/sanitized_path.md", next_next_doc["path"] + assert_equal "_methods/escape-+ #%20[].md", next_next_doc["previous"]["path"] + assert_equal "_methods/site/generate.md", next_next_doc["next"]["path"] + assert_nil next_next_doc["next"]["next"] + assert_nil next_next_doc["next"]["previous"] + assert_nil next_next_doc["next"]["content"] + assert_nil next_next_doc["next"]["excerpt"] + assert_nil next_next_doc["next"]["output"] + assert_nil next_next_doc["previous"]["next"] + assert_nil next_next_doc["previous"]["previous"] + assert_nil next_next_doc["previous"]["content"] + assert_nil next_next_doc["previous"]["excerpt"] + assert_nil next_next_doc["previous"]["output"] + end + + context "with the basename (without extension) ending with dot(s)" do + setup do + @site = fixture_site("collections" => ["methods"]) + @site.process + @document = @site.collections["methods"].docs.detect do |d| + d.relative_path == "_methods/trailing-dots...md" + end + end + + should "render into the proper url" do + assert_equal "/methods/trailing-dots.html", @document.url + + trailing_dots_doc = @site.posts.docs.detect do |d| + d.relative_path == "_posts/2018-10-12-trailing-dots...markdown" + end + assert_equal "/2018/10/12/trailing-dots.html", trailing_dots_doc.url + end + end + + context "with YAML ending in three dots" do + setup do + @site = fixture_site("collections" => ["methods"]) + @site.process + @document = @site.collections["methods"].docs.detect do |d| + d.relative_path == "_methods/yaml_with_dots.md" + end + end + + should "know its data" do + assert_equal "YAML with Dots", @document.data["title"] + assert_equal "foo.bar", @document.data["whatever"] + end + end + + should "output the collection name in the #to_liquid method" do + assert_equal "methods", @document.to_liquid["collection"] + end + + should "output its relative path as path in Liquid" do + assert_equal "_methods/configuration.md", @document.to_liquid["path"] + end + + context "when rendered with Liquid" do + should "respect the front matter definition" do + site = fixture_site("collections" => ["roles"]).tap(&:process) + docs = site.collections["roles"].docs + + # Ruby context: doc.basename is aliased as doc.to_liquid["name"] by default. + + document = docs.detect { |d| d.relative_path == "_roles/unnamed.md" } + assert_equal "unnamed.md", document.basename + assert_equal "unnamed.md", document.to_liquid["name"] + + document = docs.detect { |d| d.relative_path == "_roles/named.md" } + assert_equal "named.md", document.basename + assert_equal "launcher", document.to_liquid["name"] + end + end + end + + context "a document as part of a collection with front matter defaults" do + setup do + @site = fixture_site( + "collections" => ["slides"], + "defaults" => [{ + "scope" => { "path" => "", "type" => "slides" }, + "values" => { + "nested" => { + "key" => "myval", + }, + }, + }] + ) + @site.process + @document = @site.collections["slides"].docs.find { |d| d.is_a?(Document) } + end + + should "know the front matter defaults" do + assert_equal "Example slide", @document.data["title"] + assert_equal "slide", @document.data["layout"] + assert_equal({ "key"=>"myval" }, @document.data["nested"]) + end + + should "return front matter defaults via to_liquid" do + hash = @document.to_liquid + assert hash.key? "nested" + assert_equal({ "key"=>"myval" }, hash["nested"]) + end + end + + context "a document as part of a collection with overridden default values" do + setup do + @site = fixture_site( + "collections" => ["slides"], + "defaults" => [{ + "scope" => { "path" => "", "type" => "slides" }, + "values" => { + "nested" => { + "test1" => "default1", + "test2" => "default1", + }, + }, + }] + ) + @site.process + @document = @site.collections["slides"].docs[1] + end + + should "override default values in the document front matter" do + assert_equal "Override title", @document.data["title"] + assert_equal "slide", @document.data["layout"] + assert_equal( + { "test1" => "override1", "test2" => "override2" }, + @document.data["nested"] + ) + end + end + + context "a document as part of a collection with valid path" do + setup do + @site = fixture_site( + "collections" => ["slides"], + "defaults" => [{ + "scope" => { "path" => "_slides", "type" => "slides" }, + "values" => { + "nested" => { + "key" => "value123", + }, + }, + }] + ) + @site.process + @document = @site.collections["slides"].docs.first + end + + should "know the front matter defaults" do + assert_equal "Example slide", @document.data["title"] + assert_equal "slide", @document.data["layout"] + assert_equal({ "key"=>"value123" }, @document.data["nested"]) + end + end + + context "a document as part of a collection with invalid path" do + setup do + @site = fixture_site( + "collections" => ["slides"], + "defaults" => [{ + "scope" => { "path" => "somepath", "type" => "slides" }, + "values" => { + "nested" => { + "key" => "myval", + }, + }, + }] + ) + @site.process + @document = @site.collections["slides"].docs.first + end + + should "not know the specified front matter defaults" do + assert_equal "Example slide", @document.data["title"] + assert_equal "slide", @document.data["layout"] + assert_nil @document.data["nested"] + end + end + + context "a document in a collection with a custom permalink" do + setup do + @site = fixture_site( + "collections" => ["slides"] + ) + @site.process + @document = @site.collections["slides"].docs[2] + @dest_file = dest_dir("slide/3/index.html") + end + + should "know its permalink" do + assert_equal "/slide/3/", @document.permalink + end + + should "produce the right URL" do + assert_equal "/slide/3/", @document.url + end + end + + context "a document in a collection with custom filename permalinks" do + setup do + @site = fixture_site( + "collections" => { + "slides" => { + "output" => true, + "permalink" => "/slides/test/:name", + }, + }, + "permalink" => "pretty" + ) + @site.process + @document = @site.collections["slides"].docs[0] + @dest_file = dest_dir("slides/test/example-slide-1.html") + end + + should "produce the right URL" do + assert_equal "/slides/test/example-slide-1", @document.url + end + + should "produce the right destination file" do + assert_equal @dest_file, @document.destination(dest_dir) + end + + should "honor the output extension of its permalink" do + assert_equal ".html", @document.output_ext + end + end + + context "a document in a collection with pretty permalink style" do + setup do + @site = fixture_site( + "collections" => { + "slides" => { + "output" => true, + }, + } + ) + @site.permalink_style = :pretty + @site.process + @document = @site.collections["slides"].docs[0] + @dest_file = dest_dir("slides/example-slide-1/index.html") + end + + should "produce the right URL" do + assert_equal "/slides/example-slide-1/", @document.url + end + + should "produce the right destination file" do + assert_equal @dest_file, @document.destination(dest_dir) + end + end + + context "a document in a collection with cased file name" do + setup do + @site = fixture_site( + "collections" => { + "slides" => { + "output" => true, + }, + } + ) + @site.permalink_style = :pretty + @site.process + @document = @site.collections["slides"].docs[7] + @dest_file = dest_dir("slides/example-slide-Upper-Cased/index.html") + end + + should "produce the right cased URL" do + assert_equal "/slides/example-slide-Upper-Cased/", @document.url + end + end + + context "a document in a collection with cased file name" do + setup do + @site = fixture_site( + "collections" => { + "slides" => { + "output" => true, + }, + } + ) + @site.process + @document = @site.collections["slides"].docs[6] + @dest_file = dest_dir("slides/example-slide-7.php") + end + + should "be written out properly" do + assert_exist @dest_file + end + + should "produce the permalink as the url" do + assert_equal "/slides/example-slide-7.php", @document.url + end + + should "be written to the proper directory" do + assert_equal @dest_file, @document.destination(dest_dir) + end + + should "honor the output extension of its permalink" do + assert_equal ".php", @document.output_ext + end + end + + context "documents in a collection with custom title permalinks" do + setup do + @site = fixture_site( + "collections" => { + "slides" => { + "output" => true, + "permalink" => "/slides/:title", + }, + } + ) + @site.process + @document = @site.collections["slides"].docs[3] + @document_without_slug = @site.collections["slides"].docs[4] + @document_with_strange_slug = @site.collections["slides"].docs[5] + end + + should "produce the right URL if they have a slug" do + assert_equal "/slides/so-what-is-jekyll-exactly", @document.url + end + + should "produce the right destination file if they have a slug" do + dest_file = dest_dir("slides/so-what-is-jekyll-exactly.html") + assert_equal dest_file, @document.destination(dest_dir) + end + + should "produce the right URL if they don't have a slug" do + assert_equal "/slides/example-slide-5", @document_without_slug.url + end + should "produce the right destination file if they don't have a slug" do + dest_file = dest_dir("slides/example-slide-5.html") + assert_equal dest_file, @document_without_slug.destination(dest_dir) + end + + should "produce the right URL if they have a wild slug" do + assert_equal( + "/slides/Well,-so-what-is-Jekyll,-then", + @document_with_strange_slug.url + ) + end + should "produce the right destination file if they have a wild slug" do + dest_file = dest_dir("/slides/Well,-so-what-is-Jekyll,-then.html") + assert_equal dest_file, @document_with_strange_slug.destination(dest_dir) + end + end + + context "document with a permalink with dots & a trailing slash" do + setup do + @site = fixture_site("collections" => { + "with.dots" => { "output" => true }, + }) + @site.process + @document = @site.collections["with.dots"].docs.last + @dest_file = dest_dir("with.dots", "permalink.with.slash.tho", "index.html") + end + + should "yield an HTML document" do + assert_equal @dest_file, @document.destination(dest_dir) + end + + should "be written properly" do + assert_exist @dest_file + end + + should "get the right output_ext" do + assert_equal ".html", @document.output_ext + end + end + + context "documents in a collection" do + setup do + @site = fixture_site( + "collections" => { + "slides" => { + "output" => true, + }, + } + ) + @site.process + @files = @site.collections["slides"].docs + end + + context "without output overrides" do + should "be output according to collection defaults" do + refute_nil @files.find do |doc| + doc.relative_path == "_slides/example-slide-4.html" + end + assert_exist dest_dir("slides/example-slide-4.html") + end + end + + context "with output overrides" do + should "be output according its front matter" do + assert @files.find do |doc| + doc.relative_path == "_slides/non-outputted-slide.html" + end + refute_exist dest_dir("slides/non-outputted-slide.html") + end + end + end + + context "a static file in a collection" do + setup do + @site = fixture_site( + "collections" => { + "slides" => { + "output" => true, + }, + } + ) + @site.process + @document = @site.collections["slides"].files.find do |doc| + doc.relative_path == "_slides/octojekyll.png" + end + @dest_file = dest_dir("slides/octojekyll.png") + end + + should "be a static file" do + assert @document.is_a?(StaticFile) + end + + should "be set to write" do + assert @document.write? + end + + should "be in the list of docs_to_write" do + assert_includes @site.docs_to_write, @document + end + + should "be output in the correct place" do + assert File.file?(@dest_file) + end + end + + context "a document in a collection with non-alphabetic file name" do + setup do + @site = fixture_site( + "collections" => { + "methods" => { + "output" => true, + }, + } + ) + @site.process + @document = @site.collections["methods"].docs.find do |doc| + doc.relative_path == "_methods/escape-+ #%20[].md" + end + @dest_file = dest_dir("methods/escape-+ #%20[].html") + end + + should "produce the right URL" do + assert_equal "/methods/escape-+%20%23%2520%5B%5D.html", @document.url + end + + should "produce the right destination" do + assert_equal @dest_file, @document.destination(dest_dir) + end + + should "be output in the correct place" do + assert File.file?(@dest_file) + end + end + + context "a document in a collection with dash-separated numeric file name" do + setup do + @site = fixture_site( + "collections" => { + "methods" => { + "output" => true, + }, + } + ) + @site.process + @document = @site.collections["methods"].docs.find do |doc| + doc.relative_path == "_methods/3940394-21-9393050-fifif1323-test.md" + end + @dest_file = dest_dir("methods/3940394-21-9393050-fifif1323-test.html") + end + + should "produce the right URL" do + assert_equal "/methods/3940394-21-9393050-fifif1323-test.html", @document.url + end + + should "produce the right destination" do + assert_equal @dest_file, @document.destination(dest_dir) + end + + should "be output in the correct place" do + assert File.file?(@dest_file) + end + end + + context "a document with UTF-8 CLRF" do + setup do + @document = setup_encoded_document "UTF8CRLFandBOM.md" + end + + should "not throw an error" do + Jekyll::Renderer.new(@document.site, @document).render_document + end + end + + context "a document with UTF-16LE CLRF" do + setup do + @document = setup_encoded_document "Unicode16LECRLFandBOM.md" + end + + should "not throw an error" do + Jekyll::Renderer.new(@document.site, @document).render_document + end + end + + context "a document with a date with timezone" do + setup do + @document = setup_document_with_dates "time_with_timezone.md" + end + + should "have the expected date" do + assert_equal "2015/09/30", @document.data["date"].strftime("%Y/%m/%d") + end + + should "return the expected date via Liquid" do + assert_equal "2015/09/30", @document.to_liquid["date"].strftime("%Y/%m/%d") + end + end + + context "a document with a date with time but without timezone" do + setup do + @document = setup_document_with_dates "time_without_timezone.md" + end + + should "have the expected date" do + assert_equal "2015/10/01", @document.data["date"].strftime("%Y/%m/%d") + end + + should "return the expected date via Liquid" do + assert_equal "2015/10/01", @document.to_liquid["date"].strftime("%Y/%m/%d") + end + end + + context "a document with a date without time" do + setup do + @document = setup_document_with_dates "date_without_time.md" + end + + should "have the expected date" do + assert_equal "2015/10/01", @document.data["date"].strftime("%Y/%m/%d") + end + + should "return the expected date via Liquid" do + assert_equal "2015/10/01", @document.to_liquid["date"].strftime("%Y/%m/%d") + end + end +end diff --git a/test/test_drop.rb b/test/test_drop.rb new file mode 100644 index 0000000..623cc9f --- /dev/null +++ b/test/test_drop.rb @@ -0,0 +1,154 @@ +# frozen_string_literal: true + +require "helper" + +class DropFixture < Jekyll::Drops::Drop + mutable true + + attr_accessor :lipsum + + def foo + "bar" + end + + def fallback_data + @fallback_data ||= { "baz" => "buzz" } + end +end + +class TestDrop < JekyllUnitTest + context "Drops" do + setup do + @site = fixture_site( + "collections" => ["methods"] + ) + @site.process + @document = @site.collections["methods"].docs.detect do |d| + d.relative_path == "_methods/configuration.md" + end + @document_drop = @document.to_liquid + @drop = DropFixture.new({}) + end + + should "reject 'nil' key" do + refute @drop.key?(nil) + end + + should "return values for #[]" do + assert_equal "bar", @drop["foo"] + end + + should "return values for #invoke_drop" do + assert_equal "bar", @drop.invoke_drop("foo") + end + + should "return array of strings for .getter_methods" do + assert(@drop.class.getter_method_names.all? { |entry| entry.is_a?(String) }) + end + + should "return array of only getter method name strings for .getter_methods" do + [:lipsum, :lipsum=].each { |id| assert_includes(@drop.class.instance_methods, id) } + + assert_includes @drop.class.getter_method_names, "lipsum" + refute_includes @drop.class.getter_method_names, "lipsum=" + end + + should "not munge results for another Jekyll::Drops::Drop subclass" do + fixture_ids = [:lipsum, :lipsum=, :foo] + fixture_getter_names = %w(lipsum foo) + fixture_ids.each { |id| assert_includes(@drop.class.instance_methods, id) } + + fixture_getter_names.each do |name| + assert_includes @drop.class.getter_method_names, name + refute_includes @document_drop.class.getter_method_names, name + end + end + + should "return only getter method names for #content_methods" do + drop_base_class_method_names = Jekyll::Drops::Drop.instance_methods.map(&:to_s) + sample_method_names = ["lipsum=", "fallback_data", "collapse_document"] + + (sample_method_names + drop_base_class_method_names).each do |entry| + refute_includes @drop.content_methods, entry + end + + assert_equal %w(foo lipsum), @drop.content_methods.sort + end + + context "mutations" do + should "return mutations for #[]" do + @drop["foo"] = "baz" + assert_equal "baz", @drop["foo"] + end + + should "return mutations for #invoke_drop" do + @drop["foo"] = "baz" + assert_equal "baz", @drop.invoke_drop("foo") + end + end + + context "a document drop" do + context "fetch" do + should "raise KeyError if key is not found and no default provided" do + assert_raises KeyError do + @document_drop.fetch("not_existing_key") + end + end + + should "fetch value without default" do + assert_equal "Jekyll.configuration", @document_drop.fetch("title") + end + + should "fetch default if key is not found" do + assert_equal "default", @document_drop.fetch("not_existing_key", "default") + end + + should "fetch default boolean value correctly" do + refute @document_drop.fetch("bar", false) + end + + should "fetch default value from block if key is not found" do + assert_equal "default bar", @document_drop.fetch("bar") { |el| "default #{el}" } + end + + should "fetch default value from block first if both argument and block given" do + assert_equal "baz", @document_drop.fetch("bar", "default") { "baz" } + end + + should "not change mutability when fetching" do + assert @drop.class.mutable? + @drop["foo"] = "baz" + assert_equal "baz", @drop.fetch("foo") + assert @drop.class.mutable? + end + end + end + + context "key?" do + context "a mutable drop" do + should "respond true for native methods" do + assert @drop.key? "foo" + end + + should "respond true for mutable keys" do + @drop["bar"] = "baz" + assert @drop.key? "bar" + end + + should "return true for fallback data" do + assert @drop.key? "baz" + end + end + + context "a document drop" do + should "respond true for native methods" do + assert @document_drop.key? "collection" + end + + should "return true for fallback data" do + assert @document_drop.key? "title" + end + end + end + end +end diff --git a/test/test_entry_filter.rb b/test/test_entry_filter.rb new file mode 100644 index 0000000..65d1d36 --- /dev/null +++ b/test/test_entry_filter.rb @@ -0,0 +1,170 @@ +# frozen_string_literal: true + +require "helper" + +class TestEntryFilter < JekyllUnitTest + context "Filtering entries" do + setup do + @site = fixture_site + end + + should "filter entries" do + ent1 = %w(foo.markdown bar.markdown baz.markdown #baz.markdown# + .baz.markdown foo.markdown~ .htaccess _posts _pages ~$benbalter.docx) + + entries = EntryFilter.new(@site).filter(ent1) + assert_equal %w(foo.markdown bar.markdown baz.markdown .htaccess), entries + end + + should "allow regexp filtering" do + files = %w(README.md) + @site.exclude = [ + %r!README!, + ] + + assert_empty @site.reader.filter_entries( + files + ) + end + + should "filter entries with exclude" do + excludes = %w(README TODO vendor/bundle) + files = %w(index.html site.css .htaccess vendor) + + @site.exclude = excludes + ["exclude*"] + assert_equal files, @site.reader.filter_entries(excludes + files + ["excludeA"]) + end + + should "filter entries with exclude relative to site source" do + excludes = %w(README TODO css) + files = %w(index.html vendor/css .htaccess) + + @site.exclude = excludes + assert_equal files, @site.reader.filter_entries(excludes + files + ["css"]) + end + + should "filter excluded directory and contained files" do + excludes = %w(README TODO css) + files = %w(index.html .htaccess) + + @site.exclude = excludes + assert_equal( + files, + @site.reader.filter_entries( + excludes + files + ["css", "css/main.css", "css/vendor.css"] + ) + ) + end + + should "not filter entries within include" do + includes = %w(_index.html .htaccess include*) + files = %w(index.html _index.html .htaccess includeA) + + @site.include = includes + assert_equal files, @site.reader.filter_entries(files) + end + + should "not exclude explicitly included entry" do + entries = %w(README TODO css .htaccess _movies/.) + excludes = %w(README TODO css) + includes = %w(README .htaccess) + @site.exclude = excludes + @site.include = includes + filtered_entries = EntryFilter.new(@site).filter(entries) + assert_equal %w(README .htaccess), filtered_entries + end + + should "keep safe symlink entries when safe mode enabled" do + allow(File).to receive(:symlink?).with("symlink.js").and_return(true) + files = %w(symlink.js) + assert_equal files, @site.reader.filter_entries(files) + end + + should "not filter symlink entries when safe mode disabled" do + allow(File).to receive(:symlink?).with("symlink.js").and_return(true) + files = %w(symlink.js) + assert_equal files, @site.reader.filter_entries(files) + end + + should "filter symlink pointing outside site source" do + ent1 = %w(_includes/tmp) + entries = EntryFilter.new(@site).filter(ent1) + assert_equal %w(), entries + end + + should "include only safe symlinks in safe mode" do + # no support for symlinks on Windows + skip_if_windows "Jekyll does not currently support symlinks on Windows." + + site = fixture_site("safe" => true) + site.reader.read_directories("symlink-test") + + assert_equal %w(main.scss symlinked-file).length, site.pages.length + refute_equal [], site.static_files + end + + should "include symlinks in unsafe mode" do + # no support for symlinks on Windows + skip_if_windows "Jekyll does not currently support symlinks on Windows." + + @site.reader.read_directories("symlink-test") + refute_equal [], @site.pages + refute_equal [], @site.static_files + end + + should "include only safe symlinks in safe mode even when included" do + # no support for symlinks on Windows + skip_if_windows "Jekyll does not currently support symlinks on Windows." + + site = fixture_site("safe" => true, "include" => ["symlinked-file-outside-source"]) + site.reader.read_directories("symlink-test") + + assert_equal %w(main.scss symlinked-file).length, site.pages.length + refute_includes site.static_files.map(&:name), "symlinked-file-outside-source" + end + end + + context "#glob_include?" do + setup do + @site = Site.new(site_configuration) + @filter = EntryFilter.new(@site) + end + + should "return false with no glob patterns" do + refute @filter.glob_include?([], "a.txt") + end + + should "return false with all not match path" do + data = ["a*", "b?"] + refute @filter.glob_include?(data, "ca.txt") + refute @filter.glob_include?(data, "ba.txt") + end + + should "return true with match path" do + data = ["a*", "b?", "**/a*"] + assert @filter.glob_include?(data, "a.txt") + assert @filter.glob_include?(data, "ba") + assert @filter.glob_include?(data, "c/a/a.txt") + assert @filter.glob_include?(data, "c/a/b/a.txt") + end + + should "match even if there is no leading slash" do + data = ["vendor/bundle"] + assert @filter.glob_include?(data, "/vendor/bundle") + assert @filter.glob_include?(data, "vendor/bundle") + end + + should "match even if there is no trailing slash" do + data = ["/vendor/bundle/", "vendor/ruby"] + assert @filter.glob_include?(data, "vendor/bundle/jekyll/lib/page.rb") + assert @filter.glob_include?(data, "/vendor/ruby/lib/set.rb") + end + + should "match directory only if there is trailing slash" do + data = ["_glob_include_test/_is_dir/", "_glob_include_test/_not_dir/"] + assert @filter.glob_include?(data, "_glob_include_test/_is_dir") + assert @filter.glob_include?(data, "_glob_include_test/_is_dir/include_me.txt") + refute @filter.glob_include?(data, "_glob_include_test/_not_dir") + end + end +end diff --git a/test/test_excerpt.rb b/test/test_excerpt.rb new file mode 100644 index 0000000..5a4ce0a --- /dev/null +++ b/test/test_excerpt.rb @@ -0,0 +1,310 @@ +# frozen_string_literal: true + +require "helper" + +class TestExcerpt < JekyllUnitTest + def setup_post(file) + Document.new(@site.in_source_dir(File.join("_posts", file)), + :site => @site, + :collection => @site.posts).tap(&:read) + end + + def do_render(document) + @site.layouts = { + "default" => Layout.new(@site, source_dir("_layouts"), "simple.html"), + } + document.output = Jekyll::Renderer.new(@site, document, @site.site_payload).run + end + + context "With extraction disabled" do + setup do + clear_dest + @site = fixture_site("excerpt_separator" => "") + @post = setup_post("2013-07-22-post-excerpt-with-layout.markdown") + end + + should "not be generated" do + refute @post.generate_excerpt? + end + end + + context "An extracted excerpt" do + setup do + clear_dest + @site = fixture_site + @post = setup_post("2013-07-22-post-excerpt-with-layout.markdown") + @excerpt = @post.data["excerpt"] + end + + context "#include(string)" do + setup do + @excerpt.output = "Here is a fake output stub" + end + + should "return true only if an excerpt output contains a specified string" do + assert_includes @excerpt, "fake output" + refute_includes @excerpt, "real output" + end + end + + context "#id" do + should "contain the UID for the post" do + assert_equal @excerpt.id, "#{@post.id}#excerpt" + end + should "return a string" do + assert_same @post.id.class, String + end + end + + context "#type" do + should "return the post's type" do + assert_equal @excerpt.type, @post.type + end + should "return a symbol" do + assert_same @excerpt.type.class, Symbol + end + end + + context "#to_s" do + should "return rendered output" do + assert_equal @excerpt.output, @excerpt.to_s + end + + should "return its output if output present" do + @excerpt.output = "Fake Output" + assert_equal @excerpt.output, @excerpt.to_s + end + end + + context "#inspect" do + should "contain the excerpt id as a shorthand string identifier" do + assert_equal @excerpt.inspect, "<#{@excerpt.class} id=#{@excerpt.id}>" + end + + should "return a string" do + assert_same @post.id.class, String + end + end + + context "#relative_path" do + should "return its document's relative path with '/#excerpt' appended" do + assert_equal "#{@excerpt.doc.relative_path}/#excerpt", + @excerpt.relative_path + assert_equal "_posts/2013-07-22-post-excerpt-with-layout.markdown/#excerpt", + @excerpt.relative_path + end + end + + context "#to_liquid" do + should "contain the proper page data to mimic the post liquid" do + assert_equal "Post Excerpt with Layout", @excerpt.to_liquid["title"] + url = "/bar/baz/z_category/mixedcase/2013/07/22/post-excerpt-with-layout.html" + assert_equal url, @excerpt.to_liquid["url"] + assert_equal Time.parse("2013-07-22"), @excerpt.to_liquid["date"] + assert_equal %w(bar baz z_category MixedCase), @excerpt.to_liquid["categories"] + assert_equal %w(first second third jekyllrb.com), @excerpt.to_liquid["tags"] + assert_equal "_posts/2013-07-22-post-excerpt-with-layout.markdown/#excerpt", + @excerpt.to_liquid["path"] + end + end + + context "#content" do + context "before render" do + should "be the first paragraph of the page" do + expected = "First paragraph with [link ref][link].\n\n[link]: https://jekyllrb.com/" + assert_equal expected, @excerpt.content + end + + should "contain any refs at the bottom of the page" do + assert_includes @excerpt.content, "[link]: https://jekyllrb.com/" + end + end + + context "after render" do + setup do + @rendered_post = @post.dup + do_render(@rendered_post) + @extracted_excerpt = @rendered_post.data["excerpt"] + end + + should "be the first paragraph of the page" do + expected = "<p>First paragraph with <a href=\"https://jekyllrb.com/\">link " \ + "ref</a>.</p>\n\n" + assert_equal expected, @extracted_excerpt.output + end + + should "link properly" do + assert_includes @extracted_excerpt.content, "https://jekyllrb.com/" + end + end + + context "with indented link references" do + setup do + @post = setup_post("2016-08-16-indented-link-references.markdown") + @excerpt = @post.excerpt + end + + should "contain all refs at the bottom of the page" do + 4.times do |i| + assert_match "[link_#{i}]: www.example.com/#{i}", @excerpt.content + end + end + + should "ignore indented code" do + refute_match "[fakelink]:", @excerpt.content + end + + should "render links properly" do + @rendered_post = @post.dup + do_render(@rendered_post) + output = @rendered_post.data["excerpt"].output + 4.times do |i| + assert_includes output, "<a href=\"www.example.com/#{i}\">" + end + end + end + end + end + + context "A whole-post excerpt" do + setup do + clear_dest + @site = fixture_site + @post = setup_post("2008-02-02-published.markdown") + @excerpt = @post.data["excerpt"] + end + + should "be generated" do + assert @excerpt.is_a?(Jekyll::Excerpt) + end + + context "#content" do + should "match the post content" do + assert_equal @post.content, @excerpt.content + end + end + end + + context "An excerpt with non-closed but valid Liquid block tag" do + setup do + clear_dest + @site = fixture_site + @post = setup_post("2018-01-28-open-liquid-block-excerpt.markdown") + @excerpt = @post.data["excerpt"] + + head = @post.content.split("\n\n")[0] + + assert_includes @post.content, "{%\n highlight\n" + assert_includes @post.content, "{% raw" + refute_includes head, "{% endraw %}" + refute_includes head, "{% endhighlight %}" + end + + should "be appended to as necessary and generated" do + assert_includes @excerpt.content, "{% endraw %}" + assert_includes @excerpt.content, "{% endhighlight %}" + assert @excerpt.is_a?(Jekyll::Excerpt) + end + end + + context "An excerpt with valid closed Liquid block tag" do + setup do + clear_dest + @site = fixture_site + @post = setup_post("2018-01-28-closed-liquid-block-excerpt.markdown") + @excerpt = @post.data["excerpt"] + + head = @post.content.split("\n\n")[0] + + assert_includes @post.content, "{%\n highlight\n" + assert_includes @post.content, "{% raw" + assert_includes head, "{%\n endraw\n%}" + assert_includes head, "{%\n endhighlight\n%}" + end + + should "not be appended to but generated as is" do + assert_includes @excerpt.content, "{%\n endraw\n%}" + assert_includes @excerpt.content, "{%\n endhighlight\n%}" + refute_includes @excerpt.content, "{%\n endraw\n%}\n\n{% endraw %}" + refute_includes @excerpt.content, "{%\n endhighlight\n%}\n\n{% endhighlight %}" + assert @excerpt.is_a?(Jekyll::Excerpt) + end + end + + context "An excerpt with non-closed but valid Liquid block tag with whitespace control" do + setup do + clear_dest + @site = fixture_site + @post = setup_post("2018-05-15-open-liquid-block-excerpt-whitespace-control.md") + @excerpt = @post.data["excerpt"] + + assert_includes @post.content, "{%- for" + refute_includes @post.content.split("\n\n")[0], "{%- endfor -%}" + end + + should "be appended to as necessary and generated" do + assert_includes @excerpt.content, "{% endfor %}" + refute_includes @excerpt.content, "{% endfor %}\n\n{% endfor %}" + assert @excerpt.is_a?(Jekyll::Excerpt) + end + end + + context "An excerpt with valid closed Liquid block tag with whitespace control" do + setup do + clear_dest + @site = fixture_site + @post = setup_post("2018-05-15-closed-liquid-block-excerpt-whitespace-control.md") + @excerpt = @post.data["excerpt"] + + assert_includes @post.content, "{%- for" + assert_includes @post.content.split("\n\n")[0], "{%- endfor -%}" + end + + should "not be appended to but generated as is" do + assert_includes @excerpt.content, "{%- endfor -%}" + refute_includes @excerpt.content, "{% endfor %}\n\n{% endfor %}" + assert @excerpt.is_a?(Jekyll::Excerpt) + end + end + + context "An excerpt with valid Liquid variable with whitespace control" do + setup do + clear_dest + @site = fixture_site + @post = setup_post("2018-05-15-excerpt-whitespace-control-variable.md") + @excerpt = @post.data["excerpt"] + + assert_includes @post.content, "{%- assign" + end + + should "not be appended to but generated as is" do + assert_includes @excerpt.content, "{{- xyzzy -}}" + assert @excerpt.is_a?(Jekyll::Excerpt) + end + end + + context "An excerpt with Liquid tags" do + setup do + clear_dest + @site = fixture_site + @post = setup_post("2018-11-15-excerpt-liquid-block.md") + @excerpt = @post.data["excerpt"] + + assert_includes @post.content.split("\n\n")[0].strip, "{% continue %}" + assert_includes Jekyll::DoNothingBlock.ancestors, Liquid::Block + refute_includes Jekyll::DoNothingOther.ancestors, Liquid::Block + assert_match "Jekyll::DoNothingBlock", Liquid::Template.tags["do_nothing"].name + assert_match "Jekyll::DoNothingOther", Liquid::Template.tags["do_nothing_other"].name + end + + should "close open block tags, including custom tags, and ignore others" do + assert_includes @excerpt.content, "{% endcase %}" + assert_includes @excerpt.content, "{% endif %}" + assert_includes @excerpt.content, "{% endfor %}" + assert_includes @excerpt.content, "{% endunless %}" + assert_includes @excerpt.content, "{% enddo_nothing %}" + refute_includes @excerpt.content, "{% enddo_nothing_other %}" + assert @excerpt.is_a?(Jekyll::Excerpt) + end + end +end diff --git a/test/test_excerpt_drop.rb b/test/test_excerpt_drop.rb new file mode 100644 index 0000000..e9e387d --- /dev/null +++ b/test/test_excerpt_drop.rb @@ -0,0 +1,42 @@ +# frozen_string_literal: true + +require "helper" + +class TestExcerptDrop < JekyllUnitTest + context "an excerpt drop" do + setup do + @site = fixture_site + @site.read + @doc = @site.docs_to_write.find { |d| !d.data["layout"].nil? } + @doc_drop = @doc.to_liquid + @excerpt = @doc.data["excerpt"] + @excerpt_drop = @excerpt.to_liquid + end + + should "have the right thing" do + assert @doc.is_a? Jekyll::Document + assert @doc_drop.is_a? Jekyll::Drops::DocumentDrop + assert @excerpt.is_a? Jekyll::Excerpt + assert @excerpt_drop.is_a? Jekyll::Drops::ExcerptDrop + end + + should "not have an excerpt" do + assert_nil @excerpt.data["excerpt"] + assert @excerpt_drop.class.invokable? "excerpt" + assert_nil @excerpt_drop["excerpt"] + end + + should "inherit the layout for the drop but not the excerpt" do + assert_nil @excerpt.data["layout"] + assert_equal @doc_drop["layout"], @excerpt_drop["layout"] + end + + should "be inspectable" do + refute_empty @excerpt_drop.inspect + end + + should "inherit values from the document" do + assert_equal @doc_drop.keys.sort, @excerpt_drop.keys.sort + end + end +end diff --git a/test/test_filters.rb b/test/test_filters.rb new file mode 100644 index 0000000..176e5ed --- /dev/null +++ b/test/test_filters.rb @@ -0,0 +1,1542 @@ +# frozen_string_literal: true + +require "helper" + +class TestFilters < JekyllUnitTest + class JekyllFilter + include Jekyll::Filters + attr_accessor :site, :context + + def initialize(opts = {}) + @site = Jekyll::Site.new(opts.merge("skip_config_files" => true)) + @context = Liquid::Context.new(@site.site_payload, {}, :site => @site) + end + end + + class Value + def initialize(value) + @value = value + end + + def to_s + @value.respond_to?(:call) ? @value.call : @value.to_s + end + end + + def make_filter_mock(opts = {}) + JekyllFilter.new(site_configuration(opts)).tap do |f| + tz = f.site.config["timezone"] + Jekyll.set_timezone(tz) if tz + end + end + + class SelectDummy + def select; end + end + + context "filters" do + setup do + @sample_time = Time.utc(2013, 3, 27, 11, 22, 33) + @timezone_before_test = ENV["TZ"] + @filter = make_filter_mock( + "timezone" => "UTC", + "url" => "http://example.com", + "baseurl" => "/base", + "dont_show_posts_before" => @sample_time + ) + @sample_date = Date.parse("2013-03-02") + @time_as_string = "September 11, 2001 12:46:30 -0000" + @time_as_numeric = 1_399_680_607 + @integer_as_string = "142857" + @array_of_objects = [ + { "color" => "teal", "size" => "large" }, + { "color" => "red", "size" => "large" }, + { "color" => "red", "size" => "medium" }, + { "color" => "blue", "size" => "medium" }, + ] + end + + teardown do + ENV["TZ"] = @timezone_before_test + end + + should "markdownify with simple string" do + assert_equal( + "<p>something <strong>really</strong> simple</p>\n", + @filter.markdownify("something **really** simple") + ) + end + + should "markdownify with a number" do + assert_equal( + "<p>404</p>\n", + @filter.markdownify(404) + ) + end + + context "smartify filter" do + should "convert quotes and typographic characters" do + assert_equal( + "SmartyPants is *not* Markdown", + @filter.smartify("SmartyPants is *not* Markdown") + ) + assert_equal( + "“This filter’s test…”", + @filter.smartify(%q{"This filter's test..."}) + ) + end + + should "convert not convert markdown to block HTML elements" do + assert_equal( + "#hashtag", # NOT "<h1>hashtag</h1>" + @filter.smartify("#hashtag") + ) + end + + should "escapes special characters when configured to do so" do + kramdown = make_filter_mock(:kramdown => { :entity_output => :symbolic }) + assert_equal( + "“This filter’s test…”", + kramdown.smartify(%q{"This filter's test..."}) + ) + end + + should "convert HTML entities to unicode characters" do + assert_equal "’", @filter.smartify("’") + 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 <em>not</em> escaped", + @filter.smartify("Span HTML is <em>not</em> escaped") + ) + assert_equal( + "<div>Block HTML is not escaped</div>", + @filter.smartify("<div>Block HTML is not escaped</div>") + ) + end + + should "escape special characters" do + assert_equal "3 < 4", @filter.smartify("3 < 4") + assert_equal "5 > 4", @filter.smartify("5 > 4") + assert_equal "This & that", @filter.smartify("This & that") + end + + should "convert a number to a string" do + assert_equal( + "404", + @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 + assert_equal( + "p { color: #123456; }\n", + @filter.sassify(<<~SASS) + $blue: #123456 + p + color: $blue + SASS + ) + end + + should "scssify with simple string" do + assert_equal( + "p { color: #123456; }\n", + @filter.scssify("$blue:#123456; p{color: $blue}") + ) + end + + should "convert array to sentence string with no args" do + assert_equal "", @filter.array_to_sentence_string([]) + end + + should "convert array to sentence string with one arg" do + assert_equal "1", @filter.array_to_sentence_string([1]) + assert_equal "chunky", @filter.array_to_sentence_string(["chunky"]) + end + + should "convert array to sentence string with two args" do + assert_equal "1 and 2", @filter.array_to_sentence_string([1, 2]) + assert_equal "chunky and bacon", @filter.array_to_sentence_string(%w(chunky bacon)) + end + + should "convert array to sentence string with multiple args" do + assert_equal "1, 2, 3, and 4", @filter.array_to_sentence_string([1, 2, 3, 4]) + assert_equal( + "chunky, bacon, bits, and pieces", + @filter.array_to_sentence_string(%w(chunky bacon bits pieces)) + ) + end + + should "convert array to sentence string with different connector" do + assert_equal "1 or 2", @filter.array_to_sentence_string([1, 2], "or") + assert_equal "1, 2, 3, or 4", @filter.array_to_sentence_string([1, 2, 3, 4], "or") + end + + context "normalize_whitespace filter" do + should "replace newlines with a space" do + assert_equal "a b", @filter.normalize_whitespace("a\nb") + assert_equal "a b", @filter.normalize_whitespace("a\n\nb") + end + + should "replace tabs with a space" do + assert_equal "a b", @filter.normalize_whitespace("a\tb") + assert_equal "a b", @filter.normalize_whitespace("a\t\tb") + end + + should "replace multiple spaces with a single space" do + assert_equal "a b", @filter.normalize_whitespace("a b") + assert_equal "a b", @filter.normalize_whitespace("a\t\nb") + assert_equal "a b", @filter.normalize_whitespace("a \t \n\nb") + end + + should "strip whitespace from beginning and end of string" do + assert_equal "a", @filter.normalize_whitespace("a ") + assert_equal "a", @filter.normalize_whitespace(" a") + assert_equal "a", @filter.normalize_whitespace(" a ") + end + end + + context "date filters" do + context "with Time object" do + should "format a date with short format" do + assert_equal "27 Mar 2013", @filter.date_to_string(@sample_time) + end + + should "format a date with long format" do + assert_equal "27 March 2013", @filter.date_to_long_string(@sample_time) + end + + should "format a date with ordinal, US format" do + assert_equal "Mar 27th, 2013", + @filter.date_to_string(@sample_time, "ordinal", "US") + end + + should "format a date with long, ordinal format" do + assert_equal "27th March 2013", + @filter.date_to_long_string(@sample_time, "ordinal") + end + + should "format a time with xmlschema" do + assert_equal( + "2013-03-27T11:22:33+00:00", + @filter.date_to_xmlschema(@sample_time) + ) + end + + should "format a time according to RFC-822" do + assert_equal( + "Wed, 27 Mar 2013 11:22:33 +0000", + @filter.date_to_rfc822(@sample_time) + ) + end + + should "not modify a time in-place when using filters" do + t = Time.new(2004, 9, 15, 0, 2, 37, "+01:00") + assert_equal 3600, t.utc_offset + @filter.date_to_string(t) + assert_equal 3600, t.utc_offset + end + end + + context "with Date object" do + should "format a date with short format" do + assert_equal "02 Mar 2013", @filter.date_to_string(@sample_date) + end + + should "format a date with long format" do + assert_equal "02 March 2013", @filter.date_to_long_string(@sample_date) + end + + should "format a date with ordinal format" do + assert_equal "2nd Mar 2013", @filter.date_to_string(@sample_date, "ordinal") + end + + should "format a date with ordinal, US, long format" do + assert_equal "March 2nd, 2013", + @filter.date_to_long_string(@sample_date, "ordinal", "US") + end + + should "format a time with xmlschema" do + assert_equal( + "2013-03-02T00:00:00+00:00", + @filter.date_to_xmlschema(@sample_date) + ) + end + + should "format a time according to RFC-822" do + assert_equal( + "Sat, 02 Mar 2013 00:00:00 +0000", + @filter.date_to_rfc822(@sample_date) + ) + end + end + + context "with String object" do + should "format a date with short format" do + assert_equal "11 Sep 2001", @filter.date_to_string(@time_as_string) + end + + should "format a date with long format" do + assert_equal "11 September 2001", @filter.date_to_long_string(@time_as_string) + end + + should "format a date with ordinal, US format" do + assert_equal "Sep 11th, 2001", + @filter.date_to_string(@time_as_string, "ordinal", "US") + end + + should "format a date with ordinal long format" do + assert_equal "11th September 2001", + @filter.date_to_long_string(@time_as_string, "ordinal", "UK") + end + + should "format a time with xmlschema" do + assert_equal( + "2001-09-11T12:46:30+00:00", + @filter.date_to_xmlschema(@time_as_string) + ) + end + + should "format a time according to RFC-822" do + assert_equal( + "Tue, 11 Sep 2001 12:46:30 +0000", + @filter.date_to_rfc822(@time_as_string) + ) + end + + should "convert a String to Integer" do + assert_equal( + 142_857, + @filter.to_integer(@integer_as_string) + ) + end + end + + context "with a Numeric object" do + should "format a date with short format" do + assert_equal "10 May 2014", @filter.date_to_string(@time_as_numeric) + end + + should "format a date with long format" do + assert_equal "10 May 2014", @filter.date_to_long_string(@time_as_numeric) + end + + should "format a date with ordinal, US format" do + assert_equal "May 10th, 2014", + @filter.date_to_string(@time_as_numeric, "ordinal", "US") + end + + should "format a date with ordinal, long format" do + assert_equal "10th May 2014", + @filter.date_to_long_string(@time_as_numeric, "ordinal") + end + + should "format a time with xmlschema" do + assert_match( + "2014-05-10T00:10:07", + @filter.date_to_xmlschema(@time_as_numeric) + ) + end + + should "format a time according to RFC-822" do + assert_equal( + "Sat, 10 May 2014 00:10:07 +0000", + @filter.date_to_rfc822(@time_as_numeric) + ) + end + end + + context "without input" do + should "return input" do + assert_nil(@filter.date_to_xmlschema(nil)) + assert_equal("", @filter.date_to_xmlschema("")) + end + end + end + + should "escape xml with ampersands" do + assert_equal "AT&T", @filter.xml_escape("AT&T") + assert_equal( + "<code>command &lt;filename&gt;</code>", + @filter.xml_escape("<code>command <filename></code>") + ) + end + + should "not error when xml escaping nil" do + assert_equal "", @filter.xml_escape(nil) + end + + should "escape space as plus" do + assert_equal "my+things", @filter.cgi_escape("my things") + end + + should "escape special characters" do + assert_equal "hey%21", @filter.cgi_escape("hey!") + end + + should "escape space as %20" do + assert_equal "my%20things", @filter.uri_escape("my things") + end + + should "allow reserver characters in URI" do + assert_equal( + "foo!*'();:@&=+$,/?#[]bar", + @filter.uri_escape("foo!*'();:@&=+$,/?#[]bar") + ) + assert_equal( + "foo%20bar!*'();:@&=+$,/?#[]baz", + @filter.uri_escape("foo bar!*'();:@&=+$,/?#[]baz") + ) + end + + context "absolute_url filter" do + should "produce an absolute URL from a page URL" do + page_url = "/about/my_favorite_page/" + assert_equal "http://example.com/base#{page_url}", @filter.absolute_url(page_url) + end + + should "ensure the leading slash" do + page_url = "about/my_favorite_page/" + assert_equal "http://example.com/base/#{page_url}", @filter.absolute_url(page_url) + end + + should "ensure the leading slash for the baseurl" do + page_url = "about/my_favorite_page/" + filter = make_filter_mock( + "url" => "http://example.com", + "baseurl" => "base" + ) + assert_equal "http://example.com/base/#{page_url}", filter.absolute_url(page_url) + end + + should "be ok with a blank but present 'url'" do + page_url = "about/my_favorite_page/" + filter = make_filter_mock( + "url" => "", + "baseurl" => "base" + ) + assert_equal "/base/#{page_url}", filter.absolute_url(page_url) + end + + should "be ok with a nil 'url'" do + page_url = "about/my_favorite_page/" + filter = make_filter_mock( + "url" => nil, + "baseurl" => "base" + ) + assert_equal "/base/#{page_url}", filter.absolute_url(page_url) + end + + should "be ok with a nil 'baseurl'" do + page_url = "about/my_favorite_page/" + filter = make_filter_mock( + "url" => "http://example.com", + "baseurl" => nil + ) + assert_equal "http://example.com/#{page_url}", filter.absolute_url(page_url) + end + + should "not prepend a forward slash if input is empty" do + page_url = "" + filter = make_filter_mock( + "url" => "http://example.com", + "baseurl" => "/base" + ) + assert_equal "http://example.com/base", filter.absolute_url(page_url) + end + + should "not append a forward slash if input is '/'" do + page_url = "/" + filter = make_filter_mock( + "url" => "http://example.com", + "baseurl" => "/base" + ) + assert_equal "http://example.com/base/", filter.absolute_url(page_url) + end + + should "not append a forward slash if input is '/' and nil 'baseurl'" do + page_url = "/" + filter = make_filter_mock( + "url" => "http://example.com", + "baseurl" => nil + ) + assert_equal "http://example.com/", filter.absolute_url(page_url) + end + + should "not append a forward slash if both input and baseurl are simply '/'" do + page_url = "/" + filter = make_filter_mock( + "url" => "http://example.com", + "baseurl" => "/" + ) + assert_equal "http://example.com/", filter.absolute_url(page_url) + end + + should "normalize international URLs" do + page_url = "" + filter = make_filter_mock( + "url" => "http://ümlaut.example.org/", + "baseurl" => nil + ) + assert_equal "http://xn--mlaut-jva.example.org/", filter.absolute_url(page_url) + end + + should "not modify an absolute URL" do + page_url = "http://example.com/" + assert_equal "http://example.com/", @filter.absolute_url(page_url) + end + + should "transform the input URL to a string" do + page_url = "/my-page.html" + filter = make_filter_mock("url" => Value.new(proc { "http://example.org" })) + assert_equal "http://example.org#{page_url}", filter.absolute_url(page_url) + end + + should "not raise a TypeError when passed a hash" do + assert @filter.absolute_url("foo" => "bar") + end + + context "with a document" do + setup do + @site = fixture_site( + "collections" => ["methods"] + ) + @site.process + @document = @site.collections["methods"].docs.detect do |d| + d.relative_path == "_methods/configuration.md" + end + end + + should "make a url" do + expected = "http://example.com/base/methods/configuration.html" + assert_equal expected, @filter.absolute_url(@document) + end + end + end + + context "relative_url filter" do + should "produce a relative URL from a page URL" do + page_url = "/about/my_favorite_page/" + assert_equal "/base#{page_url}", @filter.relative_url(page_url) + end + + should "ensure the leading slash between baseurl and input" do + page_url = "about/my_favorite_page/" + assert_equal "/base/#{page_url}", @filter.relative_url(page_url) + end + + should "ensure the leading slash for the baseurl" do + page_url = "about/my_favorite_page/" + filter = make_filter_mock("baseurl" => "base") + assert_equal "/base/#{page_url}", filter.relative_url(page_url) + end + + should "normalize international URLs" do + page_url = "错误.html" + assert_equal "/base/%E9%94%99%E8%AF%AF.html", @filter.relative_url(page_url) + end + + should "be ok with a nil 'baseurl'" do + page_url = "about/my_favorite_page/" + filter = make_filter_mock( + "url" => "http://example.com", + "baseurl" => nil + ) + assert_equal "/#{page_url}", filter.relative_url(page_url) + end + + should "not prepend a forward slash if input is empty" do + page_url = "" + filter = make_filter_mock( + "url" => "http://example.com", + "baseurl" => "/base" + ) + assert_equal "/base", filter.relative_url(page_url) + end + + should "not prepend a forward slash if baseurl ends with a single '/'" do + page_url = "/css/main.css" + filter = make_filter_mock( + "url" => "http://example.com", + "baseurl" => "/base/" + ) + assert_equal "/base/css/main.css", filter.relative_url(page_url) + end + + should "not return valid URI if baseurl ends with multiple '/'" do + page_url = "/css/main.css" + filter = make_filter_mock( + "url" => "http://example.com", + "baseurl" => "/base//" + ) + refute_equal "/base/css/main.css", filter.relative_url(page_url) + end + + should "not prepend a forward slash if both input and baseurl are simply '/'" do + page_url = "/" + filter = make_filter_mock( + "url" => "http://example.com", + "baseurl" => "/" + ) + assert_equal "/", filter.relative_url(page_url) + end + + should "not return the url by reference" do + filter = make_filter_mock(:baseurl => nil) + page = Page.new(filter.site, test_dir("fixtures"), "", "front_matter.erb") + assert_equal "/front_matter.erb", page.url + url = filter.relative_url(page.url) + url << "foo" + assert_equal "/front_matter.erb", filter.relative_url(page.url) + assert_equal "/front_matter.erb", page.url + end + + should "transform the input baseurl to a string" do + page_url = "/my-page.html" + filter = make_filter_mock("baseurl" => Value.new(proc { "/baseurl/" })) + assert_equal "/baseurl#{page_url}", filter.relative_url(page_url) + end + + should "transform protocol-relative url" do + url = "//example.com/" + assert_equal "/base//example.com/", @filter.relative_url(url) + end + + should "not modify an absolute url with scheme" do + url = "file:///file.html" + assert_equal url, @filter.relative_url(url) + end + + should "not normalize absolute international URLs" do + url = "https://example.com/错误" + assert_equal "https://example.com/错误", @filter.relative_url(url) + end + end + + context "strip_index filter" do + should "strip trailing /index.html" do + assert_equal "/foo/", @filter.strip_index("/foo/index.html") + end + + should "strip trailing /index.htm" do + assert_equal "/foo/", @filter.strip_index("/foo/index.htm") + end + + should "not strip HTML in the middle of URLs" do + assert_equal "/index.html/foo", @filter.strip_index("/index.html/foo") + end + + should "not raise an error on nil strings" do + assert_nil @filter.strip_index(nil) + end + + should "not mangle other URLs" do + assert_equal "/foo/", @filter.strip_index("/foo/") + end + end + + context "jsonify filter" do + should "convert hash to json" do + assert_equal "{\"age\":18}", @filter.jsonify(:age => 18) + end + + should "convert array to json" do + assert_equal "[1,2]", @filter.jsonify([1, 2]) + assert_equal( + "[{\"name\":\"Jack\"},{\"name\":\"Smith\"}]", + @filter.jsonify([{ :name => "Jack" }, { :name => "Smith" }]) + ) + end + + should "convert drop to json" do + @filter.site.read + expected = { + "name" => "2008-02-02-published.markdown", + "path" => "_posts/2008-02-02-published.markdown", + "previous" => nil, + "output" => nil, + "content" => "This should be published.\n", + "id" => "/publish_test/2008/02/02/published", + "url" => "/publish_test/2008/02/02/published.html", + "relative_path" => "_posts/2008-02-02-published.markdown", + "collection" => "posts", + "excerpt" => "<p>This should be published.</p>\n", + "draft" => false, + "categories" => [ + "publish_test", + ], + "layout" => "default", + "title" => "Publish", + "category" => "publish_test", + "date" => "2008-02-02 00:00:00 +0000", + "slug" => "published", + "ext" => ".markdown", + "tags" => [], + } + actual = JSON.parse(@filter.jsonify(@filter.site.docs_to_write.first.to_liquid)) + + next_doc = actual.delete("next") + refute_nil next_doc + assert next_doc.is_a?(Hash), "doc.next should be an object" + + assert_equal expected, actual + end + + should "convert drop with drops to json" do + @filter.site.read + actual = @filter.jsonify(@filter.site.to_liquid) + expected = { + "environment" => "development", + "version" => Jekyll::VERSION, + } + assert_equal expected, JSON.parse(actual)["jekyll"] + end + + # rubocop:disable Style/StructInheritance + class M < Struct.new(:message) + def to_liquid + [message] + end + end + + class T < Struct.new(:name) + def to_liquid + { + "name" => name, + :v => 1, + :thing => M.new(:kay => "jewelers"), + :stuff => true, + } + end + end + + should "call #to_liquid " do + expected = [ + { + "name" => "Jeremiah", + "v" => 1, + "thing" => [ + { + "kay" => "jewelers", + }, + ], + "stuff" => true, + }, + { + "name" => "Smathers", + "v" => 1, + "thing" => [ + { + "kay" => "jewelers", + }, + ], + "stuff" => true, + }, + ] + result = @filter.jsonify([T.new("Jeremiah"), T.new("Smathers")]) + assert_equal expected, JSON.parse(result) + end + # rubocop:enable Style/StructInheritance + + should "handle hashes with all sorts of weird keys and values" do + my_hash = { "posts" => Array.new(3) { |i| T.new(i) } } + expected = { + "posts" => [ + { + "name" => 0, + "v" => 1, + "thing" => [ + { + "kay" => "jewelers", + }, + ], + "stuff" => true, + }, + { + "name" => 1, + "v" => 1, + "thing" => [ + { + "kay" => "jewelers", + }, + ], + "stuff" => true, + }, + { + "name" => 2, + "v" => 1, + "thing" => [ + { + "kay" => "jewelers", + }, + ], + "stuff" => true, + }, + ], + } + result = @filter.jsonify(my_hash) + assert_equal expected, JSON.parse(result) + end + end + + context "group_by filter" do + should "successfully group array of Jekyll::Page's" do + @filter.site.process + grouping = @filter.group_by(@filter.site.pages, "layout") + names = ["default", "nil", ""] + grouping.each do |g| + assert_includes names, g["name"], "#{g["name"]} isn't a valid grouping." + case g["name"] + when "default" + assert( + g["items"].is_a?(Array), + "The list of grouped items for 'default' is not an Array." + ) + # adjust array.size to ignore symlinked page in Windows + qty = Utils::Platforms.really_windows? ? 4 : 5 + assert_equal qty, g["items"].size + when "nil" + assert( + g["items"].is_a?(Array), + "The list of grouped items for 'nil' is not an Array." + ) + assert_equal 2, g["items"].size + when "" + assert( + g["items"].is_a?(Array), + "The list of grouped items for '' is not an Array." + ) + # adjust array.size to ignore symlinked page in Windows + qty = Utils::Platforms.really_windows? ? 19 : 21 + assert_equal qty, g["items"].size + end + end + end + + should "include the size of each grouping" do + grouping = @filter.group_by(@filter.site.pages, "layout") + grouping.each do |g| + assert_equal( + g["items"].size, + g["size"], + "The size property for '#{g["name"]}' doesn't match the size of the Array." + ) + end + end + + should "should pass integers as is" do + grouping = @filter.group_by([ + { "name" => "Allison", "year" => 2016 }, + { "name" => "Amy", "year" => 2016 }, + { "name" => "George", "year" => 2019 }, + ], "year") + assert_equal "2016", grouping[0]["name"] + assert_equal "2019", grouping[1]["name"] + end + end + + context "where filter" do + should "return any input that is not an array" do + assert_equal "some string", @filter.where("some string", "la", "le") + end + + should "filter objects in a hash appropriately" do + hash = { "a" => { "color"=>"red" }, "b" => { "color"=>"blue" } } + assert_equal 1, @filter.where(hash, "color", "red").length + assert_equal [{ "color"=>"red" }], @filter.where(hash, "color", "red") + end + + should "filter objects appropriately" do + assert_equal 2, @filter.where(@array_of_objects, "color", "red").length + end + + should "filter objects with null properties appropriately" do + array = [{}, { "color" => nil }, { "color" => "" }, { "color" => "text" }] + assert_equal 2, @filter.where(array, "color", nil).length + end + + should "filter objects with numerical properties appropriately" do + array = [ + { "value" => "555" }, + { "value" => 555 }, + { "value" => 24.625 }, + { "value" => "24.625" }, + ] + assert_equal 2, @filter.where(array, "value", 24.625).length + assert_equal 2, @filter.where(array, "value", 555).length + end + + should "filter array properties appropriately" do + hash = { + "a" => { "tags"=>%w(x y) }, + "b" => { "tags"=>["x"] }, + "c" => { "tags"=>%w(y z) }, + } + assert_equal 2, @filter.where(hash, "tags", "x").length + end + + should "filter array properties alongside string properties" do + hash = { + "a" => { "tags"=>%w(x y) }, + "b" => { "tags"=>"x" }, + "c" => { "tags"=>%w(y z) }, + } + assert_equal 2, @filter.where(hash, "tags", "x").length + end + + should "filter hash properties with null and empty values" do + hash = { + "a" => { "tags" => {} }, + "b" => { "tags" => "" }, + "c" => { "tags" => nil }, + "d" => { "tags" => ["x", nil] }, + "e" => { "tags" => [] }, + "f" => { "tags" => "xtra" }, + } + + assert_equal [{ "tags" => nil }], @filter.where(hash, "tags", nil) + + assert_equal( + [{ "tags" => "" }, { "tags" => ["x", nil] }], + @filter.where(hash, "tags", "") + ) + + # `{{ hash | where: 'tags', empty }}` + assert_equal( + [{ "tags" => {} }, { "tags" => "" }, { "tags" => nil }, { "tags" => [] }], + @filter.where(hash, "tags", Liquid::Expression::LITERALS["empty"]) + ) + + # `{{ `hash | where: 'tags', blank }}` + assert_equal( + [{ "tags" => {} }, { "tags" => "" }, { "tags" => nil }, { "tags" => [] }], + @filter.where(hash, "tags", Liquid::Expression::LITERALS["blank"]) + ) + end + + should "not match substrings" do + hash = { + "a" => { "category"=>"bear" }, + "b" => { "category"=>"wolf" }, + "c" => { "category"=>%w(bear lion) }, + } + assert_equal 0, @filter.where(hash, "category", "ear").length + end + + should "stringify during comparison for compatibility with liquid parsing" do + hash = { + "The Words" => { "rating" => 1.2, "featured" => false }, + "Limitless" => { "rating" => 9.2, "featured" => true }, + "Hustle" => { "rating" => 4.7, "featured" => true }, + } + + results = @filter.where(hash, "featured", "true") + assert_equal 2, results.length + assert_in_delta(9.2, results[0]["rating"]) + assert_in_delta(4.7, results[1]["rating"]) + + results = @filter.where(hash, "rating", 4.7) + assert_equal 1, results.length + assert_in_delta(4.7, results[0]["rating"]) + end + + should "always return an array if the object responds to 'select'" do + results = @filter.where(SelectDummy.new, "obj", "1 == 1") + assert_equal [], results + end + end + + context "where_exp filter" do + should "return any input that is not an array" do + assert_equal "some string", @filter.where_exp("some string", "la", "le") + end + + should "filter objects in a hash appropriately" do + hash = { "a" => { "color"=>"red" }, "b" => { "color"=>"blue" } } + assert_equal 1, @filter.where_exp(hash, "item", "item.color == 'red'").length + assert_equal( + [{ "color"=>"red" }], + @filter.where_exp(hash, "item", "item.color == 'red'") + ) + end + + should "filter objects appropriately" do + assert_equal( + 2, + @filter.where_exp(@array_of_objects, "item", "item.color == 'red'").length + ) + end + + should "filter objects appropriately with 'or', 'and' operators" do + assert_equal( + [ + { "color" => "teal", "size" => "large" }, + { "color" => "red", "size" => "large" }, + { "color" => "red", "size" => "medium" }, + ], + @filter.where_exp( + @array_of_objects, "item", "item.color == 'red' or item.size == 'large'" + ) + ) + + assert_equal( + [ + { "color" => "red", "size" => "large" }, + ], + @filter.where_exp( + @array_of_objects, "item", "item.color == 'red' and item.size == 'large'" + ) + ) + end + + should "filter objects across multiple conditions" do + sample = [ + { "color" => "teal", "size" => "large", "type" => "variable" }, + { "color" => "red", "size" => "large", "type" => "fixed" }, + { "color" => "red", "size" => "medium", "type" => "variable" }, + { "color" => "blue", "size" => "medium", "type" => "fixed" }, + ] + assert_equal( + [ + { "color" => "red", "size" => "large", "type" => "fixed" }, + ], + @filter.where_exp( + sample, "item", "item.type == 'fixed' and item.color == 'red' or item.color == 'teal'" + ) + ) + end + + should "stringify during comparison for compatibility with liquid parsing" do + hash = { + "The Words" => { "rating" => 1.2, "featured" => false }, + "Limitless" => { "rating" => 9.2, "featured" => true }, + "Hustle" => { "rating" => 4.7, "featured" => true }, + } + + results = @filter.where_exp(hash, "item", "item.featured == true") + assert_equal 2, results.length + assert_in_delta(9.2, results[0]["rating"]) + assert_in_delta(4.7, results[1]["rating"]) + + results = @filter.where_exp(hash, "item", "item.rating == 4.7") + assert_equal 1, results.length + assert_in_delta(4.7, results[0]["rating"]) + end + + should "filter with other operators" do + assert_equal [3, 4, 5], @filter.where_exp([1, 2, 3, 4, 5], "n", "n >= 3") + end + + objects = [ + { "id" => "a", "groups" => [1, 2] }, + { "id" => "b", "groups" => [2, 3] }, + { "id" => "c" }, + { "id" => "d", "groups" => [1, 3] }, + ] + should "filter with the contains operator over arrays" do + results = @filter.where_exp(objects, "obj", "obj.groups contains 1") + assert_equal 2, results.length + assert_equal "a", results[0]["id"] + assert_equal "d", results[1]["id"] + end + + should "filter with the contains operator over hash keys" do + results = @filter.where_exp(objects, "obj", "obj contains 'groups'") + assert_equal 3, results.length + assert_equal "a", results[0]["id"] + assert_equal "b", results[1]["id"] + assert_equal "d", results[2]["id"] + end + + should "filter posts" do + site = fixture_site.tap(&:read) + posts = site.site_payload["site"]["posts"] + results = @filter.where_exp(posts, "obj", "obj.title == 'Foo Bar'") + assert_equal 1, results.length + assert_equal site.posts.find { |p| p.title == "Foo Bar" }, results.first + end + + should "always return an array if the object responds to 'select'" do + results = @filter.where_exp(SelectDummy.new, "obj", "1 == 1") + assert_equal [], results + end + + should "filter by variable values" do + @filter.site.tap(&:read) + posts = @filter.site.site_payload["site"]["posts"] + results = @filter.where_exp(posts, "post", + "post.date > site.dont_show_posts_before") + assert_equal posts.count { |p| p.date > @sample_time }, results.length + end + end + + context "find filter" do + should "return any input that is not an array" do + assert_equal "some string", @filter.find("some string", "la", "le") + end + + should "filter objects in a hash appropriately" do + hash = { "a" => { "color" => "red" }, "b" => { "color" => "blue" } } + assert_equal({ "color" => "red" }, @filter.find(hash, "color", "red")) + end + + should "filter objects appropriately" do + assert_equal( + { "color" => "red", "size" => "large" }, + @filter.find(@array_of_objects, "color", "red") + ) + end + + should "filter objects with null properties appropriately" do + array = [{}, { "color" => nil }, { "color" => "" }, { "color" => "text" }] + assert_equal({}, @filter.find(array, "color", nil)) + end + + should "filter objects with numerical properties appropriately" do + array = [ + { "value" => "555" }, + { "value" => 555 }, + { "value" => 24.625 }, + { "value" => "24.625" }, + ] + assert_equal({ "value" => 24.625 }, @filter.find(array, "value", 24.625)) + assert_equal({ "value" => "555" }, @filter.find(array, "value", 555)) + end + + should "filter array properties appropriately" do + hash = { + "a" => { "tags" => %w(x y) }, + "b" => { "tags" => ["x"] }, + "c" => { "tags" => %w(y z) }, + } + assert_equal({ "tags" => %w(x y) }, @filter.find(hash, "tags", "x")) + end + + should "filter array properties alongside string properties" do + hash = { + "a" => { "tags" => %w(x y) }, + "b" => { "tags" => "x" }, + "c" => { "tags" => %w(y z) }, + } + assert_equal({ "tags" => %w(x y) }, @filter.find(hash, "tags", "x")) + end + + should "filter hash properties with null and empty values" do + hash = { + "a" => { "tags" => {} }, + "b" => { "tags" => "" }, + "c" => { "tags" => nil }, + "d" => { "tags" => ["x", nil] }, + "e" => { "tags" => [] }, + "f" => { "tags" => "xtra" }, + } + + assert_equal({ "tags" => nil }, @filter.find(hash, "tags", nil)) + assert_equal({ "tags" => "" }, @filter.find(hash, "tags", "")) + + # `{{ hash | find: 'tags', empty }}` + assert_equal( + { "tags" => {} }, + @filter.find(hash, "tags", Liquid::Expression::LITERALS["empty"]) + ) + + # `{{ `hash | find: 'tags', blank }}` + assert_equal( + { "tags" => {} }, + @filter.find(hash, "tags", Liquid::Expression::LITERALS["blank"]) + ) + end + + should "not match substrings" do + hash = { + "a" => { "category" => "bear" }, + "b" => { "category" => "wolf" }, + "c" => { "category" => %w(bear lion) }, + } + assert_nil @filter.find(hash, "category", "ear") + end + + should "stringify during comparison for compatibility with liquid parsing" do + hash = { + "The Words" => { "rating" => 1.2, "featured" => false }, + "Limitless" => { "rating" => 9.2, "featured" => true }, + "Hustle" => { "rating" => 4.7, "featured" => true }, + } + + result = @filter.find(hash, "featured", "true") + assert_in_delta(9.2, result["rating"]) + + result = @filter.find(hash, "rating", 4.7) + assert_in_delta(4.7, result["rating"]) + end + end + + context "find_exp filter" do + should "return any input that is not an array" do + assert_equal "some string", @filter.find_exp("some string", "la", "le") + end + + should "filter objects in a hash appropriately" do + hash = { "a" => { "color"=>"red" }, "b" => { "color"=>"blue" } } + assert_equal( + { "color" => "red" }, + @filter.find_exp(hash, "item", "item.color == 'red'") + ) + end + + should "filter objects appropriately" do + assert_equal( + { "color" => "red", "size" => "large" }, + @filter.find_exp(@array_of_objects, "item", "item.color == 'red'") + ) + end + + should "filter objects appropriately with 'or', 'and' operators" do + assert_equal( + { "color" => "teal", "size" => "large" }, + @filter.find_exp( + @array_of_objects, "item", "item.color == 'red' or item.size == 'large'" + ) + ) + + assert_equal( + { "color" => "red", "size" => "large" }, + @filter.find_exp( + @array_of_objects, "item", "item.color == 'red' and item.size == 'large'" + ) + ) + end + + should "filter objects across multiple conditions" do + sample = [ + { "color" => "teal", "size" => "large", "type" => "variable" }, + { "color" => "red", "size" => "large", "type" => "fixed" }, + { "color" => "red", "size" => "medium", "type" => "variable" }, + { "color" => "blue", "size" => "medium", "type" => "fixed" }, + ] + assert_equal( + { "color" => "red", "size" => "large", "type" => "fixed" }, + @filter.find_exp( + sample, "item", "item.type == 'fixed' and item.color == 'red' or item.color == 'teal'" + ) + ) + end + + should "stringify during comparison for compatibility with liquid parsing" do + hash = { + "The Words" => { "rating" => 1.2, "featured" => false }, + "Limitless" => { "rating" => 9.2, "featured" => true }, + "Hustle" => { "rating" => 4.7, "featured" => true }, + } + + result = @filter.find_exp(hash, "item", "item.featured == true") + assert_in_delta(9.2, result["rating"]) + + result = @filter.find_exp(hash, "item", "item.rating == 4.7") + assert_in_delta(4.7, result["rating"]) + end + + should "filter with other operators" do + assert_equal 3, @filter.find_exp([1, 2, 3, 4, 5], "n", "n >= 3") + end + + objects = [ + { "id" => "a", "groups" => [1, 2] }, + { "id" => "b", "groups" => [2, 3] }, + { "id" => "c" }, + { "id" => "d", "groups" => [1, 3] }, + ] + should "filter with the contains operator over arrays" do + result = @filter.find_exp(objects, "obj", "obj.groups contains 1") + assert_equal "a", result["id"] + end + + should "filter with the contains operator over hash keys" do + result = @filter.find_exp(objects, "obj", "obj contains 'groups'") + assert_equal "a", result["id"] + end + + should "filter posts" do + site = fixture_site.tap(&:read) + posts = site.site_payload["site"]["posts"] + result = @filter.find_exp(posts, "obj", "obj.title == 'Foo Bar'") + assert_equal(result, site.posts.find { |p| p.title == "Foo Bar" }) + end + + should "filter by variable values" do + @filter.site.tap(&:read) + posts = @filter.site.site_payload["site"]["posts"] + result = @filter.find_exp(posts, "post", "post.date > site.dont_show_posts_before") + assert result.date > @sample_time + end + end + + context "group_by_exp filter" do + should "successfully group array of Jekyll::Page's" do + @filter.site.process + groups = @filter.group_by_exp(@filter.site.pages, "page", "page.layout | upcase") + names = ["DEFAULT", "NIL", ""] + groups.each do |g| + assert_includes names, g["name"], "#{g["name"]} isn't a valid grouping." + case g["name"] + when "DEFAULT" + assert( + g["items"].is_a?(Array), + "The list of grouped items for 'default' is not an Array." + ) + # adjust array.size to ignore symlinked page in Windows + qty = Utils::Platforms.really_windows? ? 4 : 5 + assert_equal qty, g["items"].size + when "nil" + assert( + g["items"].is_a?(Array), + "The list of grouped items for 'nil' is not an Array." + ) + assert_equal 2, g["items"].size + when "" + assert( + g["items"].is_a?(Array), + "The list of grouped items for '' is not an Array." + ) + # adjust array.size to ignore symlinked page in Windows + qty = Utils::Platforms.really_windows? ? 19 : 21 + assert_equal qty, g["items"].size + end + end + end + + should "include the size of each grouping" do + groups = @filter.group_by_exp(@filter.site.pages, "page", "page.layout") + groups.each do |g| + assert_equal( + g["items"].size, + g["size"], + "The size property for '#{g["name"]}' doesn't match the size of the Array." + ) + end + end + + should "allow more complex filters" do + items = [ + { "version" => "1.0", "result" => "slow" }, + { "version" => "1.1.5", "result" => "medium" }, + { "version" => "2.7.3", "result" => "fast" }, + ] + + result = @filter.group_by_exp(items, "item", "item.version | split: '.' | first") + assert_equal 2, result.size + end + + should "be equivalent of group_by" do + actual = @filter.group_by_exp(@filter.site.pages, "page", "page.layout") + expected = @filter.group_by(@filter.site.pages, "layout") + + assert_equal expected, actual + end + + should "return any input that is not an array" do + assert_equal "some string", @filter.group_by_exp("some string", "la", "le") + end + + should "group by full element (as opposed to a field of the element)" do + items = %w(a b c d) + + result = @filter.group_by_exp(items, "item", "item") + assert_equal 4, result.length + assert_equal ["a"], result.first["items"] + end + + should "accept hashes" do + hash = { 1 => "a", 2 => "b", 3 => "c", 4 => "d" } + + result = @filter.group_by_exp(hash, "item", "item") + assert_equal 4, result.length + end + end + + context "sort filter" do + should "raise Exception when input is nil" do + err = assert_raises ArgumentError do + @filter.sort(nil) + end + assert_equal "Cannot sort a null object.", err.message + end + should "return sorted numbers" do + assert_equal [1, 2, 2.2, 3], @filter.sort([3, 2.2, 2, 1]) + end + should "return sorted strings" do + assert_equal %w(10 2), @filter.sort(%w(10 2)) + assert_equal %w(FOO Foo foo), @filter.sort(%w(foo Foo FOO)) + assert_equal %w(_foo foo foo_), @filter.sort(%w(foo_ _foo foo)) + # Cyrillic + assert_equal %w(ВУЗ Вуз вуз), @filter.sort(%w(Вуз вуз ВУЗ)) + assert_equal %w(_вуз вуз вуз_), @filter.sort(%w(вуз_ _вуз вуз)) + # Hebrew + assert_equal %w(אלף בית), @filter.sort(%w(בית אלף)) + end + should "return sorted by property array" do + assert_equal [{ "a" => 1 }, { "a" => 2 }, { "a" => 3 }, { "a" => 4 }], + @filter.sort([{ "a" => 4 }, { "a" => 3 }, { "a" => 1 }, { "a" => 2 }], "a") + end + should "return sorted by property array with numeric strings sorted as numbers" do + assert_equal([{ "a" => ".5" }, { "a" => "0.65" }, { "a" => "10" }], + @filter.sort([{ "a" => "10" }, { "a" => ".5" }, { "a" => "0.65" }], "a")) + end + should "return sorted by property array with numeric strings first" do + assert_equal([{ "a" => ".5" }, { "a" => "0.6" }, { "a" => "twelve" }], + @filter.sort([{ "a" => "twelve" }, { "a" => ".5" }, { "a" => "0.6" }], "a")) + end + should "return sorted by property array with numbers and strings " do + assert_equal([{ "a" => "1" }, { "a" => "1abc" }, { "a" => "20" }], + @filter.sort([{ "a" => "20" }, { "a" => "1" }, { "a" => "1abc" }], "a")) + end + should "return sorted by property array with nils first" do + ary = [{ "a" => 2 }, { "b" => 1 }, { "a" => 1 }] + assert_equal [{ "b" => 1 }, { "a" => 1 }, { "a" => 2 }], @filter.sort(ary, "a") + assert_equal @filter.sort(ary, "a"), @filter.sort(ary, "a", "first") + end + should "return sorted by property array with nils last" do + assert_equal [{ "a" => 1 }, { "a" => 2 }, { "b" => 1 }], + @filter.sort([{ "a" => 2 }, { "b" => 1 }, { "a" => 1 }], "a", "last") + end + should "return sorted by subproperty array" do + assert_equal [{ "a" => { "b" => 1 } }, { "a" => { "b" => 2 } }, + { "a" => { "b" => 3 } },], + @filter.sort([{ "a" => { "b" => 2 } }, { "a" => { "b" => 1 } }, + { "a" => { "b" => 3 } },], "a.b") + end + end + + context "to_integer filter" do + should "raise Exception when input is not integer or string" do + assert_raises NoMethodError do + @filter.to_integer([1, 2]) + end + end + should "return 0 when input is nil" do + assert_equal 0, @filter.to_integer(nil) + end + should "return integer when input is boolean" do + assert_equal 0, @filter.to_integer(false) + assert_equal 1, @filter.to_integer(true) + end + should "return integers" do + assert_equal 0, @filter.to_integer(0) + assert_equal 1, @filter.to_integer(1) + assert_equal 1, @filter.to_integer(1.42857) + assert_equal(-1, @filter.to_integer(-1)) + assert_equal(-1, @filter.to_integer(-1.42857)) + end + end + + context "inspect filter" do + should "return a HTML-escaped string representation of an object" do + assert_equal "{"<a>"=>1}", @filter.inspect("<a>" => 1) + end + + should "quote strings" do + assert_equal ""string"", @filter.inspect("string") + end + end + + context "slugify filter" do + should "return a slugified string" do + assert_equal "q-bert-says", @filter.slugify(" Q*bert says @!#?@!") + end + + should "return a slugified string with mode" do + assert_equal "q-bert-says-@!-@!", @filter.slugify(" Q*bert says @!#?@!", "pretty") + end + end + + context "push filter" do + should "return a new array with the element pushed to the end" do + assert_equal %w(hi there bernie), @filter.push(%w(hi there), "bernie") + end + end + + context "pop filter" do + should "return a new array with the last element popped" do + assert_equal %w(hi there), @filter.pop(%w(hi there bernie)) + end + + should "allow multiple els to be popped" do + assert_equal %w(hi there bert), @filter.pop(%w(hi there bert and ernie), 2) + end + + should "cast string inputs for # into nums" do + assert_equal %w(hi there bert), @filter.pop(%w(hi there bert and ernie), "2") + end + end + + context "shift filter" do + should "return a new array with the element removed from the front" do + assert_equal %w(a friendly greeting), @filter.shift(%w(just a friendly greeting)) + end + + should "allow multiple els to be shifted" do + assert_equal %w(bert and ernie), @filter.shift(%w(hi there bert and ernie), 2) + end + + should "cast string inputs for # into nums" do + assert_equal %w(bert and ernie), @filter.shift(%w(hi there bert and ernie), "2") + end + end + + context "unshift filter" do + should "return a new array with the element put at the front" do + assert_equal %w(aloha there bernie), @filter.unshift(%w(there bernie), "aloha") + end + end + + context "sample filter" do + should "return a random item from the array" do + input = %w(hey there bernie) + assert_includes input, @filter.sample(input) + end + + should "allow sampling of multiple values (n > 1)" do + input = %w(hey there bernie) + @filter.sample(input, 2).each do |val| + assert_includes input, val + end + end + end + + context "number_of_words filter" do + should "return the number of words for Latin-only text" do + assert_equal 5, @filter.number_of_words("hello world and taoky strong!", "auto") + assert_equal 5, @filter.number_of_words("hello world and taoky strong!", "cjk") + end + + should "return the number of characters for CJK-only text" do + assert_equal 17, @filter.number_of_words("こんにちは、世界!안녕하세요 세상!", "auto") + assert_equal 17, @filter.number_of_words("こんにちは、世界!안녕하세요 세상!", "cjk") + end + + should "process Latin and CJK independently" do + # Intentional: No space between Latin and CJK + assert_equal 6, @filter.number_of_words("你好hello世界world", "auto") + assert_equal 6, @filter.number_of_words("你好hello世界world", "cjk") + end + + should "maintain original behavior unless specified" do + assert_equal 1, @filter.number_of_words("你好hello世界world") + end + end + end +end diff --git a/test/test_front_matter_defaults.rb b/test/test_front_matter_defaults.rb new file mode 100644 index 0000000..9b7f1b6 --- /dev/null +++ b/test/test_front_matter_defaults.rb @@ -0,0 +1,225 @@ +# frozen_string_literal: true + +require "helper" + +class TestFrontMatterDefaults < JekyllUnitTest + context "A site with full front matter defaults" do + setup do + @site = fixture_site( + "defaults" => [{ + "scope" => { + "path" => "contacts", + "type" => "page", + }, + "values" => { + "key" => "val", + }, + }] + ) + @output = capture_output { @site.process } + @affected = @site.pages.find { |page| page.relative_path == "contacts/bar.html" } + @not_affected = @site.pages.find { |page| page.relative_path == "about.html" } + end + + should "affect only the specified path and type" do + assert_equal "val", @affected.data["key"] + assert_nil @not_affected.data["key"] + end + + should "not call Dir.glob block" do + refute_includes @output, "Globbed Scope Path:" + end + end + + context "A site with full front matter defaults (glob)" do + setup do + @site = fixture_site( + "defaults" => [{ + "scope" => { + "path" => "contacts/*.html", + "type" => "page", + }, + "values" => { + "key" => "val", + }, + }] + ) + @output = capture_output { @site.process } + @affected = @site.pages.find { |page| page.relative_path == "contacts/bar.html" } + @not_affected = @site.pages.find { |page| page.relative_path == "about.html" } + end + + should "affect only the specified path and type" do + assert_equal "val", @affected.data["key"] + assert_nil @not_affected.data["key"] + end + + should "call Dir.glob block" do + assert_includes @output, "Globbed Scope Path:" + end + end + + context "A site with front matter type pages and an extension" do + setup do + @site = fixture_site( + "defaults" => [{ + "scope" => { + "path" => "index.html", + }, + "values" => { + "key" => "val", + }, + }] + ) + + @site.process + @affected = @site.pages.find { |page| page.relative_path == "index.html" } + @not_affected = @site.pages.find { |page| page.relative_path == "about.html" } + end + + should "affect only the specified path" do + assert_equal "val", @affected.data["key"] + assert_nil @not_affected.data["key"] + end + end + + context "A site with front matter defaults with no type" do + setup do + @site = fixture_site( + "defaults" => [{ + "scope" => { + "path" => "win", + }, + "values" => { + "key" => "val", + }, + }] + ) + + @site.process + @affected = @site.posts.docs.find { |page| page.relative_path.include?("win") } + @not_affected = @site.pages.find { |page| page.relative_path == "about.html" } + end + + should "affect only the specified path and all types" do + assert_equal "val", @affected.data["key"] + assert_nil @not_affected.data["key"] + end + end + + context "A site with front matter defaults with no path and a deprecated type" do + setup do + @site = fixture_site( + "defaults" => [{ + "scope" => { + "type" => "page", + }, + "values" => { + "key" => "val", + }, + }] + ) + + @site.process + @affected = @site.pages + @not_affected = @site.posts.docs + end + + should "affect only the specified type and all paths" do + assert_equal([], @affected.reject { |page| page.data["key"] == "val" }) + assert_equal @not_affected.reject { |page| page.data["key"] == "val" }, + @not_affected + end + end + + context "A site with front matter defaults with no path" do + setup do + @site = fixture_site( + "defaults" => [{ + "scope" => { + "type" => "pages", + }, + "values" => { + "key" => "val", + }, + }] + ) + @site.process + @affected = @site.pages + @not_affected = @site.posts.docs + end + + should "affect only the specified type and all paths" do + assert_equal([], @affected.reject { |page| page.data["key"] == "val" }) + assert_equal @not_affected.reject { |page| page.data["key"] == "val" }, + @not_affected + end + end + + context "A site with front matter defaults with no path or type" do + setup do + @site = fixture_site( + "defaults" => [{ + "scope" => { + }, + "values" => { + "key" => "val", + }, + }] + ) + @site.process + @affected = @site.pages + @not_affected = @site.posts + end + + should "affect all types and paths" do + assert_equal([], @affected.reject { |page| page.data["key"] == "val" }) + assert_equal([], @not_affected.reject { |page| page.data["key"] == "val" }) + end + end + + context "A site with front matter defaults with no scope" do + setup do + @site = fixture_site( + "defaults" => [{ + "values" => { + "key" => "val", + }, + }] + ) + @site.process + @affected = @site.pages + @not_affected = @site.posts + end + + should "affect all types and paths" do + assert_equal([], @affected.reject { |page| page.data["key"] == "val" }) + assert_equal([], @not_affected.reject { |page| page.data["key"] == "val" }) + end + end + + context "A site with front matter defaults with quoted date" do + setup do + @site = Site.new(Jekyll.configuration( + "source" => source_dir, + "destination" => dest_dir, + "defaults" => [{ + "values" => { + "date" => "2015-01-01 00:00:01", + }, + }] + )) + end + + should "not raise error" do + @site.process + end + + should "parse date" do + @site.process + date = Time.parse("2015-01-01 00:00:01") + assert(@site.pages.find { |page| page.data["date"] == date }) + assert(@site.posts.find { |page| page.data["date"] == date }) + end + end +end diff --git a/test/test_generated_site.rb b/test/test_generated_site.rb new file mode 100644 index 0000000..5b9b9cb --- /dev/null +++ b/test/test_generated_site.rb @@ -0,0 +1,110 @@ +# frozen_string_literal: true + +require "helper" + +class TestGeneratedSite < JekyllUnitTest + context "generated sites" do + setup do + clear_dest + + @site = fixture_site + @site.process + @index = File.read( + dest_dir("index.html"), + **Utils.merged_file_read_opts(@site, {}) + ) + end + + should "ensure post count is as expected" do + assert_equal 59, @site.posts.size + end + + should "insert site.posts into the index" do + assert_includes @index, "#{@site.posts.size} Posts" + end + + should "insert variable from layout into the index" do + assert_includes @index, "variable from layout" + end + + should "render latest post's content" do + assert_includes @index, @site.posts.last.content + end + + should "hide unpublished posts" do + published = Dir[dest_dir("publish_test/2008/02/02/*.html")].map \ + { |f| File.basename(f) } + assert_equal 1, published.size + assert_equal "published.html", published.first + end + + should "hide unpublished page" do + refute_exist dest_dir("/unpublished.html") + end + + should "not copy _posts directory" do + refute_exist dest_dir("_posts") + end + + should "process a page with a folder permalink properly" do + about = @site.pages.find { |page| page.name == "about.html" } + assert_equal dest_dir("about", "index.html"), about.destination(dest_dir) + assert_exist dest_dir("about", "index.html") + end + + should "process other static files and generate correct permalinks" do + assert_exist dest_dir("contacts.html") + assert_exist dest_dir("dynamic_file.php") + end + + should "include a post with a abbreviated dates" do + refute_nil( + @site.posts.index do |post| + post.relative_path == "_posts/2017-2-5-i-dont-like-zeroes.md" + end + ) + assert_exist dest_dir("2017", "02", "05", "i-dont-like-zeroes.html") + end + + should "print a nice list of static files" do + time_regexp = "\\d+:\\d+" + # + # adding a pipe character at the beginning preserves formatting with newlines + expected_output = Regexp.new <<~OUTPUT + | - /css/screen.css last edited at #{time_regexp} with extname .css + - /pgp.key last edited at #{time_regexp} with extname .key + - /products.yml last edited at #{time_regexp} with extname .yml + - /symlink-test/symlinked-dir/screen.css last edited at #{time_regexp} with extname .css + OUTPUT + assert_match expected_output, File.read(dest_dir("static_files.html")) + end + end + + context "generating limited posts" do + setup do + clear_dest + @site = fixture_site("limit_posts" => 5) + @site.process + @index = File.read(dest_dir("index.html")) + end + + should "generate only the specified number of posts" do + assert_equal 5, @site.posts.size + end + + should "ensure limit posts is 0 or more" do + assert_raises ArgumentError do + clear_dest + @site = fixture_site("limit_posts" => -1) + end + end + + should "acceptable limit post is 0" do + clear_dest + assert( + fixture_site("limit_posts" => 0), + "Couldn't create a site with limit_posts=0." + ) + end + end +end diff --git a/test/test_kramdown.rb b/test/test_kramdown.rb new file mode 100644 index 0000000..f2e5b51 --- /dev/null +++ b/test/test_kramdown.rb @@ -0,0 +1,202 @@ +# frozen_string_literal: true + +require "helper" +require "rouge" + +class TestKramdown < JekyllUnitTest + def fixture_converter(config) + site = fixture_site( + Utils.deep_merge_hashes( + { + "markdown" => "kramdown", + }, + config + ) + ) + Jekyll::Cache.clear + site.find_converter_instance( + Jekyll::Converters::Markdown + ) + end + + context "kramdown" do + setup do + @config = { + "kramdown" => { + "smart_quotes" => "lsquo,rsquo,ldquo,rdquo", + "entity_output" => "as_char", + "toc_levels" => "1..6", + "auto_ids" => false, + "footnote_nr" => 1, + "show_warnings" => true, + + "syntax_highlighter" => "rouge", + "syntax_highlighter_opts" => { + "bold_every" => 8, + "css" => :class, + "css_class" => "highlight", + "formatter" => ::Rouge::Formatters::HTMLLegacy, + "foobar" => "lipsum", + }, + }, + } + @kramdown_config_keys = @config["kramdown"].keys + @syntax_highlighter_opts_config_keys = \ + @config["kramdown"]["syntax_highlighter_opts"].keys + + @config = Jekyll.configuration(@config) + @converter = fixture_converter(@config) + end + + should "not break kramdown" do + kramdown_doc = Kramdown::Document.new("# Some Header #", @config["kramdown"]) + assert_equal :class, kramdown_doc.options[:syntax_highlighter_opts][:css] + assert_equal "lipsum", kramdown_doc.options[:syntax_highlighter_opts][:foobar] + end + + should "run Kramdown" do + assert_equal "<h1>Some Header</h1>", @converter.convert("# Some Header #").strip + end + + should "should log kramdown warnings" do + allow_any_instance_of(Kramdown::Document).to receive(:warnings).and_return(["foo"]) + expect(Jekyll.logger).to receive(:warn).with("Kramdown warning:", "foo") + @converter.convert("Something") + end + + should "render fenced code blocks with syntax highlighting" do + result = nokogiri_fragment(@converter.convert(<<~MARKDOWN)) + ~~~ruby + puts "Hello World" + ~~~ + MARKDOWN + div_highlight = ">div.highlight" + selector = "div.highlighter-rouge#{div_highlight}>pre.highlight>code" + refute_empty(result.css(selector), result.to_html) + end + + context "when configured" do + setup do + @source = <<~TEXT + ## Code Sample + + def ruby_fu + "Hello" + end + TEXT + end + + should "have 'plaintext' as the default syntax_highlighter language" do + converter = fixture_converter(@config) + parser = converter.setup && converter.instance_variable_get(:@parser) + parser_config = parser.instance_variable_get(:@config) + + assert_equal "plaintext", parser_config.dig("syntax_highlighter_opts", "default_lang") + end + + should "accept the specified default syntax_highlighter language" do + override = { + "kramdown" => { + "syntax_highlighter_opts" => { + "default_lang" => "yaml", + }, + }, + } + converter = fixture_converter(Utils.deep_merge_hashes(@config, override)) + parser = converter.setup && converter.instance_variable_get(:@parser) + parser_config = parser.instance_variable_get(:@config) + + assert_equal "yaml", parser_config.dig("syntax_highlighter_opts", "default_lang") + refute_match %r!<div class="language-plaintext!, converter.convert(@source) + refute_match %r!<div class="language-html!, converter.convert(@source) + assert_match %r!<div class="language-yaml!, converter.convert(@source) + end + end + + context "when asked to convert smart quotes" do + should "convert" do + converter = fixture_converter(@config) + assert_match( + %r!<p>(“|“)Pit(’|’)hy(”|”)</p>!, + converter.convert(%("Pit'hy")).strip + ) + end + + should "support custom types" do + override = { + "highlighter" => nil, + "kramdown" => { + "smart_quotes" => "lsaquo,rsaquo,laquo,raquo", + }, + } + converter = fixture_converter(Utils.deep_merge_hashes(@config, override)) + assert_match %r!<p>(«|«)Pit(›|›)hy(»|»)</p>!, \ + converter.convert(%("Pit'hy")).strip + end + end + + context "when a custom highlighter is chosen" do + should "use the chosen highlighter if it's available" do + override = { + "highlighter" => nil, + "kramdown" => { + "syntax_highlighter" => "coderay", + }, + } + converter = fixture_converter(Utils.deep_merge_hashes(@config, override)) + result = nokogiri_fragment(converter.convert(<<~MARKDOWN)) + ~~~ruby + puts "Hello World" + ~~~ + MARKDOWN + + selector = "div.highlighter-coderay>div.CodeRay>div.code>pre" + refute_empty result.css(selector) + end + + should "support legacy enable_coderay... for now" do + override = { + "kramdown" => { + "enable_coderay" => true, + }, + } + + @config.delete("highlighter") + @config["kramdown"].delete("syntax_highlighter") + + converter = fixture_converter(Utils.deep_merge_hashes(@config, override)) + result = nokogiri_fragment(converter.convert(<<~MARKDOWN)) + ~~~ruby + puts "Hello World" + ~~~ + MARKDOWN + + selector = "div.highlighter-coderay>div.CodeRay>div.code>pre" + refute_empty result.css(selector), "pre tag should exist" + end + end + + should "move coderay to syntax_highlighter_opts" do + override = { + "highlighter" => nil, + "kramdown" => { + "syntax_highlighter" => "coderay", + "coderay" => { + "hello" => "world", + }, + }, + } + original = Kramdown::Document.method(:new) + converter = fixture_converter( + Utils.deep_merge_hashes(@config, override) + ) + + expect(Kramdown::Document).to receive(:new) do |arg1, hash| + assert_equal "world", hash["syntax_highlighter_opts"]["hello"] + original.call(arg1, hash) + end + + converter.convert("hello world") + end + end +end diff --git a/test/test_layout_reader.rb b/test/test_layout_reader.rb new file mode 100644 index 0000000..0ba64eb --- /dev/null +++ b/test/test_layout_reader.rb @@ -0,0 +1,83 @@ +# frozen_string_literal: true + +require "helper" + +class TestLayoutReader < JekyllUnitTest + context "reading layouts" do + setup do + config = Jekyll::Configuration::DEFAULTS.merge("source" => source_dir, + "destination" => dest_dir) + @site = fixture_site(config) + end + + should "read layouts" do + layouts = LayoutReader.new(@site).read + assert_equal ["default", "simple", "post/simple"].sort, layouts.keys.sort + end + + context "when no _layouts directory exists in CWD" do + should "know to use the layout directory relative to the site source" do + assert_equal LayoutReader.new(@site).layout_directory, source_dir("_layouts") + end + end + + context "when a _layouts directory exists in CWD" do + setup do + allow(File).to receive(:directory?).and_return(true) + allow(Dir).to receive(:pwd).and_return(source_dir("blah")) + end + + should "ignore the layout directory in CWD and use the directory relative to site source" do + refute_equal source_dir("blah/_layouts"), LayoutReader.new(@site).layout_directory + assert_equal source_dir("_layouts"), LayoutReader.new(@site).layout_directory + end + end + + context "when a layout is a symlink" do + setup do + symlink_if_allowed("/etc/passwd", source_dir("_layouts", "symlink.html")) + + @site = fixture_site( + "safe" => true, + "include" => ["symlink.html"] + ) + end + + teardown do + FileUtils.rm_f(source_dir("_layouts", "symlink.html")) + end + + should "only read the layouts which are in the site" do + skip_if_windows "Jekyll does not currently support symlinks on Windows." + + layouts = LayoutReader.new(@site).read + + refute layouts.key?("symlink"), "Should not read the symlinked layout" + end + end + + context "with a theme" do + setup do + symlink_if_allowed("/etc/passwd", theme_dir("_layouts", "theme-symlink.html")) + @site = fixture_site( + "include" => ["theme-symlink.html"], + "theme" => "test-theme", + "safe" => true + ) + end + + teardown do + FileUtils.rm_f(theme_dir("_layouts", "theme-symlink.html")) + end + + should "not read a symlink'd theme" do + skip_if_windows "Jekyll does not currently support symlinks on Windows." + + layouts = LayoutReader.new(@site).read + + refute layouts.key?("theme-symlink"), \ + "Should not read symlinked layout from theme" + end + end + end +end diff --git a/test/test_liquid_extensions.rb b/test/test_liquid_extensions.rb new file mode 100644 index 0000000..5886d80 --- /dev/null +++ b/test/test_liquid_extensions.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +require "helper" + +class TestLiquidExtensions < JekyllUnitTest + context "looking up a variable in a Liquid context" do + class SayHi < Liquid::Tag + include Jekyll::LiquidExtensions + + def initialize(_tag_name, markup, _tokens) + @markup = markup.strip + end + + def render(context) + "hi #{lookup_variable(context, @markup)}" + end + end + Liquid::Template.register_tag("say_hi", SayHi) + setup do + # Parses and compiles the template + @template = Liquid::Template.parse("{% say_hi page.name %}") + end + + should "extract the var properly" do + assert_equal "hi tobi", @template.render("page" => { "name" => "tobi" }) + end + + should "return the variable name if the value isn't there" do + assert_equal "hi page.name", @template.render("page" => { "title" => "tobi" }) + end + end +end diff --git a/test/test_liquid_renderer.rb b/test/test_liquid_renderer.rb new file mode 100644 index 0000000..4079580 --- /dev/null +++ b/test/test_liquid_renderer.rb @@ -0,0 +1,58 @@ +# frozen_string_literal: true + +require "helper" + +class TestLiquidRenderer < JekyllUnitTest + context "profiler" do + setup do + @site = Site.new(site_configuration) + @renderer = @site.liquid_renderer + end + + should "return a table with profiling results" do + @site.process + + output = @renderer.stats_table + + # rubocop:disable Layout/LineLength + expected = [ + %r!^\| Filename\s+|\s+Count\s+|\s+Bytes\s+|\s+Time$!, + %r!^\+(?:-+\+){4}$!, + %r!^\|_posts/2010-01-09-date-override\.markdown\s+|\s+\d+\s+|\s+\d+\.\d{2}K\s+|\s+\d+\.\d{3}$!, + ] + # rubocop:enable Layout/LineLength + + expected.each do |regexp| + assert_match regexp, output + end + end + + should "normalize paths of rendered items" do + site = fixture_site("theme" => "test-theme") + MockRenderer = Class.new(Jekyll::LiquidRenderer) { public :normalize_path } + renderer = MockRenderer.new(site) + + assert_equal "feed.xml", renderer.normalize_path("/feed.xml") + assert_equal( + "_layouts/post.html", + renderer.normalize_path(site.in_source_dir("_layouts", "post.html")) + ) + assert_equal( + "test-theme/_layouts/page.html", + renderer.normalize_path(site.in_theme_dir("_layouts", "page.html")) + ) + assert_equal( + "my_plugin-0.1.0/lib/my_plugin/layout.html", + renderer.normalize_path( + "/users/jo/blog/vendor/bundle/ruby/2.4.0/gems/my_plugin-0.1.0/lib/my_plugin/layout.html" + ) + ) + assert_equal( + "test_plugin-0.1.0/lib/test_plugin/layout.html", + renderer.normalize_path( + "C:/Ruby2.4/lib/ruby/gems/2.4.0/gems/test_plugin-0.1.0/lib/test_plugin/layout.html" + ) + ) + end + end +end diff --git a/test/test_log_adapter.rb b/test/test_log_adapter.rb new file mode 100644 index 0000000..b15dca5 --- /dev/null +++ b/test/test_log_adapter.rb @@ -0,0 +1,135 @@ +# frozen_string_literal: true + +require "helper" + +class TestLogAdapter < JekyllUnitTest + class LoggerDouble + attr_accessor :level + + def debug(*); end + + def info(*); end + + def warn(*); end + + def error(*); end + end + + context "#log_level=" do + should "set the writers logging level" do + subject = Jekyll::LogAdapter.new(LoggerDouble.new) + subject.log_level = :error + assert_equal Jekyll::LogAdapter::LOG_LEVELS[:error], subject.writer.level + end + end + + context "#adjust_verbosity" do + should "set the writers logging level to error when quiet" do + subject = Jekyll::LogAdapter.new(LoggerDouble.new) + subject.adjust_verbosity(:quiet => true) + assert_equal Jekyll::LogAdapter::LOG_LEVELS[:error], subject.writer.level + end + + should "set the writers logging level to debug when verbose" do + subject = Jekyll::LogAdapter.new(LoggerDouble.new) + subject.adjust_verbosity(:verbose => true) + assert_equal Jekyll::LogAdapter::LOG_LEVELS[:debug], subject.writer.level + end + + should "set the writers logging level to error when quiet and verbose are both set" do + subject = Jekyll::LogAdapter.new(LoggerDouble.new) + subject.adjust_verbosity(:quiet => true, :verbose => true) + assert_equal Jekyll::LogAdapter::LOG_LEVELS[:error], subject.writer.level + end + + should "not change the writer's logging level when neither verbose or quiet" do + subject = Jekyll::LogAdapter.new(LoggerDouble.new) + original_level = subject.writer.level + refute_equal Jekyll::LogAdapter::LOG_LEVELS[:error], subject.writer.level + refute_equal Jekyll::LogAdapter::LOG_LEVELS[:debug], subject.writer.level + subject.adjust_verbosity(:quiet => false, :verbose => false) + assert_equal original_level, subject.writer.level + end + + should "call #debug on writer return true" do + writer = LoggerDouble.new + logger = Jekyll::LogAdapter.new(writer, :debug) + allow(writer).to receive(:debug).and_return(true) + assert logger.adjust_verbosity + end + end + + context "#debug" do + should "call #debug on writer return true" do + writer = LoggerDouble.new + logger = Jekyll::LogAdapter.new(writer, :debug) + allow(writer).to receive(:debug) + .with("topic ".rjust(20) + "log message").and_return(true) + assert logger.debug("topic", "log message") + end + end + + context "#info" do + should "call #info on writer return true" do + writer = LoggerDouble.new + logger = Jekyll::LogAdapter.new(writer, :info) + allow(writer).to receive(:info) + .with("topic ".rjust(20) + "log message").and_return(true) + assert logger.info("topic", "log message") + end + end + + context "#warn" do + should "call #warn on writer return true" do + writer = LoggerDouble.new + logger = Jekyll::LogAdapter.new(writer, :warn) + allow(writer).to receive(:warn) + .with("topic ".rjust(20) + "log message").and_return(true) + assert logger.warn("topic", "log message") + end + end + + context "#error" do + should "call #error on writer return true" do + writer = LoggerDouble.new + logger = Jekyll::LogAdapter.new(writer, :error) + allow(writer).to receive(:error) + .with("topic ".rjust(20) + "log message").and_return(true) + assert logger.error("topic", "log message") + end + end + + context "#abort_with" do + should "call #error and abort" do + logger = Jekyll::LogAdapter.new(LoggerDouble.new, :error) + allow(logger).to receive(:error).with("topic", "log message").and_return(true) + assert_raises(SystemExit) { logger.abort_with("topic", "log message") } + end + end + + context "#messages" do + should "return an array" do + assert_equal [], Jekyll::LogAdapter.new(LoggerDouble.new).messages + end + + should "store each log value in the array" do + logger = Jekyll::LogAdapter.new(LoggerDouble.new, :debug) + values = %w(one two three four) + logger.debug(values[0]) + logger.info(values[1]) + logger.warn(values[2]) + logger.error(values[3]) + assert_equal values.map { |value| "#{value} ".rjust(20) }, logger.messages + end + end + + context "#write_message?" do + should "return false up to the desired logging level" do + subject = Jekyll::LogAdapter.new(LoggerDouble.new, :warn) + refute subject.write_message?(:debug), "Should not print debug messages" + refute subject.write_message?(:info), "Should not print info messages" + assert subject.write_message?(:warn), "Should print warn messages" + assert subject.write_message?(:error), "Should print error messages" + end + end +end diff --git a/test/test_new_command.rb b/test/test_new_command.rb new file mode 100644 index 0000000..c71cf41 --- /dev/null +++ b/test/test_new_command.rb @@ -0,0 +1,143 @@ +# frozen_string_literal: true + +require "helper" +require "jekyll/commands/new" + +class TestNewCommand < JekyllUnitTest + def dir_contents(path) + Dir["#{path}/**/*"].each do |file| + file.gsub! path, "" + end + end + + def site_template + File.expand_path("../lib/site_template", __dir__) + end + + def blank_template + File.expand_path("../lib/blank_template", __dir__) + end + + context "when args contains a path" do + setup do + @path = "new-site" + @args = [@path] + @full_path = File.expand_path(@path, Dir.pwd) + end + + teardown do + FileUtils.rm_r @full_path if File.directory?(@full_path) + end + + should "create a new directory" do + refute_exist @full_path + capture_output { Jekyll::Commands::New.process(@args) } + assert_exist @full_path + end + + should "create a Gemfile" do + gemfile = File.join(@full_path, "Gemfile") + refute_exist @full_path + capture_output { Jekyll::Commands::New.process(@args) } + assert_exist gemfile + assert_match(%r!gem "jekyll", "~> #{Jekyll::VERSION}"!o, File.read(gemfile)) + assert_match(%r!gem "github-pages"!, File.read(gemfile)) + end + + should "display a success message" do + output = capture_output { Jekyll::Commands::New.process(@args) } + success_message = "New jekyll site installed in #{@full_path.cyan}. " + bundle_message = "Running bundle install in #{@full_path.cyan}... " + assert_includes output, success_message + assert_includes output, bundle_message + end + + should "copy the static files in site template to the new directory" do + static_template_files = dir_contents(site_template).reject do |f| + File.extname(f) == ".erb" + end + static_template_files << "/Gemfile" + + capture_output { Jekyll::Commands::New.process(@args) } + + new_site_files = dir_contents(@full_path).reject do |f| + f.end_with?("welcome-to-jekyll.markdown") + end + + assert_same_elements static_template_files, new_site_files + end + + should "process any ERB files" do + erb_template_files = dir_contents(site_template).select do |f| + File.extname(f) == ".erb" + end + + stubbed_date = "2013-01-01" + allow_any_instance_of(Time).to receive(:strftime) { stubbed_date } + + erb_template_files.each do |f| + f.chomp! ".erb" + f.gsub! "0000-00-00", stubbed_date + end + + capture_output { Jekyll::Commands::New.process(@args) } + + new_site_files = dir_contents(@full_path).select do |f| + erb_template_files.include? f + end + + assert_same_elements erb_template_files, new_site_files + end + + should "create blank project" do + blank_contents = dir_contents(blank_template) + blank_contents += %w(/_data /_drafts /_includes /_posts) + output = capture_output { Jekyll::Commands::New.process(@args, "--blank") } + bundle_message = "Running bundle install in #{@full_path.cyan}..." + assert_same_elements blank_contents, dir_contents(@full_path) + refute_includes output, bundle_message + end + + should "force created folder" do + capture_output { Jekyll::Commands::New.process(@args) } + output = capture_output { Jekyll::Commands::New.process(@args, "--force") } + assert_match %r!New jekyll site installed in!, output + end + + should "skip bundle install when opted to" do + output = capture_output { Jekyll::Commands::New.process(@args, "--skip-bundle") } + bundle_message = "Bundle install skipped." + assert_includes output, bundle_message + end + end + + context "when multiple args are given" do + setup do + @site_name_with_spaces = "new site name" + @multiple_args = @site_name_with_spaces.split + end + + teardown do + FileUtils.rm_r File.expand_path(@site_name_with_spaces, Dir.pwd) + end + + should "create a new directory" do + refute_exist @site_name_with_spaces + capture_output { Jekyll::Commands::New.process(@multiple_args) } + assert_exist @site_name_with_spaces + end + end + + context "when no args are given" do + setup do + @empty_args = [] + end + + should "raise an ArgumentError" do + exception = assert_raises ArgumentError do + Jekyll::Commands::New.process(@empty_args) + end + assert_equal "You must specify a path.", exception.message + end + end +end diff --git a/test/test_page.rb b/test/test_page.rb new file mode 100644 index 0000000..593d006 --- /dev/null +++ b/test/test_page.rb @@ -0,0 +1,428 @@ +# frozen_string_literal: true + +require "helper" + +class TestPage < JekyllUnitTest + def setup_page(*args) + dir, file = args + if file.nil? + file = dir + dir = "" + end + @page = Page.new(@site, source_dir, dir, file) + end + + def do_render(page) + layouts = { + "default" => Layout.new(@site, source_dir("_layouts"), "simple.html"), + } + page.render(layouts, @site.site_payload) + end + + context "A Page" do + setup do + clear_dest + @site = Site.new(Jekyll.configuration( + "source" => source_dir, + "destination" => dest_dir, + "skip_config_files" => true + )) + end + + context "processing pages" do + should "create URL based on filename" do + @page = setup_page("contacts.html") + assert_equal "/contacts.html", @page.url + end + + should "create proper URL from filename" do + @page = setup_page("trailing-dots...md") + assert_equal "/trailing-dots.html", @page.url + end + + should "not published when published yaml is false" do + @page = setup_page("unpublished.html") + refute @page.published? + end + + should "create URL with non-alphabetic characters" do + @page = setup_page("+", "%# +.md") + assert_equal "/+/%25%23%20+.html", @page.url + end + + context "in a directory hierarchy" do + should "create URL based on filename" do + @page = setup_page("/contacts", "bar.html") + assert_equal "/contacts/bar.html", @page.url + end + + should "create index URL based on filename" do + @page = setup_page("/contacts", "index.html") + assert_equal "/contacts/", @page.url + end + end + + should "deal properly with extensions" do + @page = setup_page("deal.with.dots.html") + assert_equal ".html", @page.ext + end + + should "deal properly with non-html extensions" do + @page = setup_page("dynamic_page.php") + @dest_file = dest_dir("dynamic_page.php") + assert_equal ".php", @page.ext + assert_equal "dynamic_page", @page.basename + assert_equal "/dynamic_page.php", @page.url + assert_equal @dest_file, @page.destination(dest_dir) + end + + should "deal properly with dots" do + @page = setup_page("deal.with.dots.html") + @dest_file = dest_dir("deal.with.dots.html") + + assert_equal "deal.with.dots", @page.basename + assert_equal @dest_file, @page.destination(dest_dir) + end + + should "make properties accessible through #[]" do + page = setup_page("properties.html") + attrs = { + :content => "All the properties.\n", + :dir => "/properties/", + :excerpt => nil, + :foo => "bar", + :layout => "default", + :name => "properties.html", + :path => "properties.html", + :permalink => "/properties/", + :published => nil, + :title => "Properties Page", + :url => "/properties/", + } + + attrs.each do |attr, val| + attr_str = attr.to_s + result = page[attr_str] + if val.nil? + assert_nil result, "For <page[\"#{attr_str}\"]>:" + else + assert_equal val, result, "For <page[\"#{attr_str}\"]>:" + end + end + end + + context "with pretty permalink style" do + setup do + @site.permalink_style = :pretty + end + + should "return dir, URL, and destination correctly" do + @page = setup_page("contacts.html") + @dest_file = dest_dir("contacts/index.html") + + assert_equal "/contacts/", @page.dir + assert_equal "/contacts/", @page.url + assert_equal @dest_file, @page.destination(dest_dir) + end + + should "return dir correctly for index page" do + @page = setup_page("index.html") + assert_equal "/", @page.dir + end + + context "in a directory hierarchy" do + should "create url based on filename" do + @page = setup_page("/contacts", "bar.html") + assert_equal "/contacts/bar/", @page.url + end + + should "create index URL based on filename" do + @page = setup_page("/contacts", "index.html") + assert_equal "/contacts/", @page.url + end + + should "return dir correctly" do + @page = setup_page("/contacts", "bar.html") + assert_equal "/contacts/bar/", @page.dir + end + + should "return dir correctly for index page" do + @page = setup_page("/contacts", "index.html") + assert_equal "/contacts/", @page.dir + end + end + end + + context "with date permalink style" do + setup do + @site.permalink_style = :date + end + + should "return url and destination correctly" do + @page = setup_page("contacts.html") + @dest_file = dest_dir("contacts.html") + assert_equal "/contacts.html", @page.url + assert_equal @dest_file, @page.destination(dest_dir) + end + + should "return dir correctly" do + assert_equal "/", setup_page("contacts.html").dir + assert_equal "/contacts/", setup_page("contacts/bar.html").dir + assert_equal "/contacts/", setup_page("contacts/index.html").dir + end + end + + context "with custom permalink style with trailing slash" do + setup do + @site.permalink_style = "/:title/" + end + + should "return URL and destination correctly" do + @page = setup_page("contacts.html") + @dest_file = dest_dir("contacts/index.html") + assert_equal "/contacts/", @page.url + assert_equal @dest_file, @page.destination(dest_dir) + end + end + + context "with custom permalink style with file extension" do + setup do + @site.permalink_style = "/:title:output_ext" + end + + should "return URL and destination correctly" do + @page = setup_page("contacts.html") + @dest_file = dest_dir("contacts.html") + assert_equal "/contacts.html", @page.url + assert_equal @dest_file, @page.destination(dest_dir) + end + end + + context "with custom permalink style with no extension" do + setup do + @site.permalink_style = "/:title" + end + + should "return URL and destination correctly" do + @page = setup_page("contacts.html") + @dest_file = dest_dir("contacts.html") + assert_equal "/contacts", @page.url + assert_equal @dest_file, @page.destination(dest_dir) + end + end + + context "with any other permalink style" do + should "return dir correctly" do + @site.permalink_style = nil + assert_equal "/", setup_page("contacts.html").dir + assert_equal "/contacts/", setup_page("contacts/index.html").dir + assert_equal "/contacts/", setup_page("contacts/bar.html").dir + end + end + + should "respect permalink in YAML front matter" do + file = "about.html" + @page = setup_page(file) + + assert_equal "/about/", @page.permalink + assert_equal @page.permalink, @page.url + assert_equal "/about/", @page.dir + end + + should "return nil permalink if no permalink exists" do + @page = setup_page("") + assert_nil @page.permalink + end + + should "not be writable outside of destination" do + unexpected = File.expand_path("../../../baddie.html", dest_dir) + File.delete unexpected if File.exist?(unexpected) + page = setup_page("exploit.md") + do_render(page) + page.write(dest_dir) + + refute_exist unexpected + end + end + + context "with specified layout of nil" do + setup do + @page = setup_page("sitemap.xml") + end + + should "layout of nil is respected" do + assert_equal "nil", @page.data["layout"] + end + end + + context "rendering" do + setup do + clear_dest + end + + should "write properly" do + page = setup_page("contacts.html") + do_render(page) + page.write(dest_dir) + + assert File.directory?(dest_dir) + assert_exist dest_dir("contacts.html") + end + + should "write even when the folder name is plus and permalink has +" do + page = setup_page("+", "foo.md") + do_render(page) + page.write(dest_dir) + + assert File.directory?(dest_dir), "#{dest_dir} should be a directory" + assert_exist dest_dir("+", "plus+in+url.html") + end + + should "write even when permalink has '%# +'" do + page = setup_page("+", "%# +.md") + do_render(page) + page.write(dest_dir) + + assert File.directory?(dest_dir) + assert_exist dest_dir("+", "%# +.html") + end + + should "write properly without html extension" do + page = setup_page("contacts.html") + page.site.permalink_style = :pretty + do_render(page) + page.write(dest_dir) + + assert File.directory?(dest_dir) + assert_exist dest_dir("contacts", "index.html") + end + + should "support .htm extension and respects that" do + page = setup_page("contacts.htm") + page.site.permalink_style = :pretty + do_render(page) + page.write(dest_dir) + + assert File.directory?(dest_dir) + assert_exist dest_dir("contacts", "index.htm") + end + + should "support .xhtml extension and respects that" do + page = setup_page("contacts.xhtml") + page.site.permalink_style = :pretty + do_render(page) + page.write(dest_dir) + + assert File.directory?(dest_dir) + assert_exist dest_dir("contacts", "index.xhtml") + end + + should "write properly with extension different from html" do + page = setup_page("sitemap.xml") + page.site.permalink_style = :pretty + do_render(page) + page.write(dest_dir) + + assert_equal "/sitemap.xml", page.url + assert_nil page.url[%r!\.html$!] + assert File.directory?(dest_dir) + assert_exist dest_dir("sitemap.xml") + end + + should "write dotfiles properly" do + page = setup_page(".htaccess") + do_render(page) + page.write(dest_dir) + + assert File.directory?(dest_dir) + assert_exist dest_dir(".htaccess") + end + + context "in a directory hierarchy" do + should "write properly the index" do + page = setup_page("/contacts", "index.html") + do_render(page) + page.write(dest_dir) + + assert File.directory?(dest_dir) + assert_exist dest_dir("contacts", "index.html") + end + + should "write properly" do + page = setup_page("/contacts", "bar.html") + do_render(page) + page.write(dest_dir) + + assert File.directory?(dest_dir) + assert_exist dest_dir("contacts", "bar.html") + end + + should "write properly without html extension" do + page = setup_page("/contacts", "bar.html") + page.site.permalink_style = :pretty + do_render(page) + page.write(dest_dir) + + assert File.directory?(dest_dir) + assert_exist dest_dir("contacts", "bar", "index.html") + end + end + + context "read-in by default" do + should "not initialize excerpts by default" do + page = setup_page("contacts", "foo.md") + assert_nil page.excerpt + end + + should "not expose an excerpt to Liquid templates by default" do + page = setup_page("/contacts", "bar.html") + assert_nil page.to_liquid["excerpt"] + end + + context "in a site configured to generate page excerpts" do + setup { @configured_site = fixture_site("page_excerpts" => true) } + + should "initialize excerpt eagerly but render only when needed" do + test_page = Jekyll::Page.new(@configured_site, source_dir, "contacts", "foo.md") + assert_equal Jekyll::PageExcerpt, test_page.data["excerpt"].class + assert_equal String, test_page.excerpt.class + assert_equal( + "<h2 id=\"contact-information\">Contact Information</h2>\n", + test_page.excerpt + ) + end + + should "expose an excerpt to Liquid templates" do + test_page = Jekyll::Page.new(@configured_site, source_dir, "/contacts", "bar.html") + assert_equal "Contact Information\n", test_page.to_liquid["excerpt"] + end + + should "not expose an excerpt for non-html pages" do + test_page = Jekyll::Page.new(@configured_site, source_dir, "assets", "test-styles.scss") + refute_equal ".half { width: 50%; }\n", test_page.to_liquid["excerpt"] + assert_nil test_page.to_liquid["excerpt"] + end + end + end + + context "generated via plugin" do + setup do + PageSubclass = Class.new(Jekyll::Page) + @test_page = PageSubclass.new(@site, source_dir, "/contacts", "bar.html") + @test_page.data.clear + end + + should "not expose an excerpt to Liquid templates by default" do + assert_equal "Contact Information\n", @test_page.content + assert_nil @test_page.to_liquid["excerpt"] + end + + should "expose an excerpt to Liquid templates if hardcoded" do + @test_page.data["excerpt"] = "Test excerpt." + assert_equal "Contact Information\n", @test_page.content + assert_equal "Test excerpt.", @test_page.to_liquid["excerpt"] + end + end + end + end +end diff --git a/test/test_page_without_a_file.rb b/test/test_page_without_a_file.rb new file mode 100644 index 0000000..e920213 --- /dev/null +++ b/test/test_page_without_a_file.rb @@ -0,0 +1,182 @@ +# 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 + + should "have non-frozen path and relative_path attributes" do + { + ["foo", "bar.md"] => "foo/bar.md", + [nil, nil] => "", + ["", ""] => "", + ["/lorem/", "/ipsum"] => "lorem/ipsum", + %w(lorem ipsum) => "lorem/ipsum", + }.each do |(dir, name), result| + page = PageWithoutAFile.new(@site, @site.source, dir, name) + assert_equal result, page.path + assert_equal result, page.relative_path + refute page.relative_path.frozen? + end + end + + context "with default site configuration" do + setup do + @page = setup_page("properties.html") + end + + should "identify itself properly" do + assert_equal '#<Jekyll::PageWithoutAFile @relative_path="properties.html">', @page.inspect + 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) + # assert a couple of attributes accessible in a regular Jekyll::Page instance + assert_equal "All the properties.\n", regular_page["content"] + assert_equal "properties.html", regular_page["name"] + + basic_attrs = %w(dir name path url excerpt) + attrs = { + "content" => nil, + "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 that all attributes (of a Jekyll::PageWithoutAFile instance) other than + # "dir", "name", "path", "url" are `nil`. + # For example, @page[dir] should be "/" but @page[content] or @page[layout], should + # simply be nil. + # + if basic_attrs.include?(prop) + assert_equal value, @page[prop], "For Jekyll::PageWithoutAFile attribute '#{prop}':" + 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 "receive 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_path_exists(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_path_exists(dest_dir("virtual-about", "index.html")) + assert_equal "Test Site", File.read(dest_dir("virtual-about", "index.html")) + end + end + end +end diff --git a/test/test_path_manager.rb b/test/test_path_manager.rb new file mode 100644 index 0000000..e17175c --- /dev/null +++ b/test/test_path_manager.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +require "helper" + +class TestPathManager < JekyllUnitTest + context "PathManager" do + setup do + @source = Dir.pwd + end + + should "return frozen copy of base if questionable path is nil" do + assert_equal @source, Jekyll::PathManager.sanitized_path(@source, nil) + assert Jekyll::PathManager.sanitized_path(@source, nil).frozen? + end + + should "return a frozen copy of base if questionable path expands into the base" do + assert_equal @source, Jekyll::PathManager.sanitized_path(@source, File.join(@source, "/")) + assert Jekyll::PathManager.sanitized_path(@source, File.join(@source, "/")).frozen? + end + + should "return a frozen string result" do + if Jekyll::Utils::Platforms.really_windows? + assert_equal( + "#{@source}/_config.yml", + Jekyll::PathManager.sanitized_path(@source, "E:\\_config.yml") + ) + end + assert_equal( + "#{@source}/_config.yml", + Jekyll::PathManager.sanitized_path(@source, "//_config.yml") + ) + assert Jekyll::PathManager.sanitized_path(@source, "//_config.yml").frozen? + end + end +end diff --git a/test/test_path_sanitization.rb b/test/test_path_sanitization.rb new file mode 100644 index 0000000..fd45594 --- /dev/null +++ b/test/test_path_sanitization.rb @@ -0,0 +1,77 @@ +# frozen_string_literal: true + +require "helper" + +class TestPathSanitization < JekyllUnitTest + context "on Windows with absolute source" do + setup do + @source = "C:/Users/xmr/Desktop/mpc-hc.org" + @dest = "./_site/" + allow(Dir).to receive(:pwd).and_return("C:/Users/xmr/Desktop/mpc-hc.org") + end + should "strip drive name from path" do + assert_equal "C:/Users/xmr/Desktop/mpc-hc.org/_site", + Jekyll.sanitized_path(@source, @dest) + end + + should "strip just the initial drive name" do + assert_equal "/tmp/foobar/jail/..c:/..c:/..c:/etc/passwd", + Jekyll.sanitized_path("/tmp/foobar/jail", "..c:/..c:/..c:/etc/passwd") + end + end + + should "escape tilde" do + assert_equal source_dir("~hi.txt"), Jekyll.sanitized_path(source_dir, "~hi.txt") + assert_equal source_dir("files", "~hi.txt"), + Jekyll.sanitized_path(source_dir, "files/../files/~hi.txt") + end + + should "remove path traversals" do + assert_equal source_dir("files", "hi.txt"), + Jekyll.sanitized_path(source_dir, "f./../../../../../../files/hi.txt") + end + + should "strip extra slashes in questionable path" do + subdir = "/files/" + file_path = "/hi.txt" + assert_equal source_dir("files", "hi.txt"), + Jekyll.sanitized_path(source_dir, "/#{subdir}/#{file_path}") + end + + should "handle nil questionable_path" do + assert_equal source_dir, Jekyll.sanitized_path(source_dir, nil) + end + + if Jekyll::Utils::Platforms.really_windows? + context "on Windows with absolute path" do + setup do + @base_path = "D:/demo" + @file_path = "D:/demo/_site" + allow(Dir).to receive(:pwd).and_return("D:/") + end + + should "strip just the clean path drive name" do + assert_equal "D:/demo/_site", + Jekyll.sanitized_path(@base_path, @file_path) + end + end + + context "on Windows with file path has matching prefix" do + setup do + @base_path = "D:/site" + @file_path = "D:/sitemap.xml" + allow(Dir).to receive(:pwd).and_return("D:/") + end + + should "not strip base path" do + assert_equal "D:/site/sitemap.xml", + Jekyll.sanitized_path(@base_path, @file_path) + end + end + end + + should "not strip base path if file path has matching prefix" do + assert_equal "/site/sitemap.xml", + Jekyll.sanitized_path("/site", "sitemap.xml") + end +end diff --git a/test/test_plugin_manager.rb b/test/test_plugin_manager.rb new file mode 100644 index 0000000..a7b9359 --- /dev/null +++ b/test/test_plugin_manager.rb @@ -0,0 +1,186 @@ +# frozen_string_literal: true + +require "helper" + +class TestPluginManager < JekyllUnitTest + def with_no_gemfile + FileUtils.mv "Gemfile", "Gemfile.old" + yield + ensure + FileUtils.mv "Gemfile.old", "Gemfile" + end + + def with_bundle_gemfile + FileUtils.mv "Gemfile", "AlternateGemfile" + yield + ensure + FileUtils.mv "AlternateGemfile", "Gemfile" + end + + context "JEKYLL_NO_BUNDLER_REQUIRE set to `nil`" do + should "require from bundler" do + with_env("JEKYLL_NO_BUNDLER_REQUIRE", nil) do + assert Jekyll::PluginManager.require_from_bundler, + "require_from_bundler should return true." + assert ENV["JEKYLL_NO_BUNDLER_REQUIRE"], "Gemfile plugins were not required." + end + end + end + + context "BUNDLE_GEMFILE set to `AlternateGemfile`" do + should "require from bundler" do + with_env("BUNDLE_GEMFILE", "AlternateGemfile") do + with_bundle_gemfile do + assert Jekyll::PluginManager.require_from_bundler, + "require_from_bundler should return true" + assert ENV["JEKYLL_NO_BUNDLER_REQUIRE"], "Gemfile plugins were not required." + end + end + end + end + + context "JEKYLL_NO_BUNDLER_REQUIRE set to `true`" do + should "not require from bundler" do + with_env("JEKYLL_NO_BUNDLER_REQUIRE", "true") do + refute Jekyll::PluginManager.require_from_bundler, + "Gemfile plugins were required but shouldn't have been" + assert ENV["JEKYLL_NO_BUNDLER_REQUIRE"] + end + end + end + + context "JEKYLL_NO_BUNDLER_REQUIRE set to `nil` and no Gemfile present" do + should "not require from bundler" do + with_env("JEKYLL_NO_BUNDLER_REQUIRE", nil) do + with_no_gemfile do + refute Jekyll::PluginManager.require_from_bundler, + "Gemfile plugins were required but shouldn't have been" + assert_nil ENV["JEKYLL_NO_BUNDLER_REQUIRE"] + end + end + end + end + + context "require gems" do + should "invoke `require_with_graceful_fail`" do + gems = %w(jemojii foobar) + + expect(Jekyll::External).to( + receive(:require_with_graceful_fail).with(gems).and_return(nil) + ) + site = double(:gems => gems) + plugin_manager = PluginManager.new(site) + + allow(plugin_manager).to receive(:plugin_allowed?).with("foobar").and_return(true) + allow(plugin_manager).to receive(:plugin_allowed?).with("jemojii").and_return(true) + + plugin_manager.require_gems + end + end + + context "site is not marked as safe" do + should "allow all plugins" do + site = double(:safe => false) + plugin_manager = PluginManager.new(site) + + assert plugin_manager.plugin_allowed?("foobar") + end + + should "require plugin files" do + site = double(:safe => false, + :config => { "plugins_dir" => "_plugins" }, + :in_source_dir => "/tmp/") + plugin_manager = PluginManager.new(site) + + expect(Jekyll::External).to receive(:require_with_graceful_fail) + plugin_manager.require_plugin_files + end + end + + context "site is marked as safe" do + should "allow plugins if they are whitelisted" do + site = double(:safe => true, :config => { "whitelist" => ["jemoji"] }) + plugin_manager = PluginManager.new(site) + + assert plugin_manager.plugin_allowed?("jemoji") + refute plugin_manager.plugin_allowed?("not_allowed_plugin") + end + + should "not require plugin files" do + site = double(:safe => true) + plugin_manager = PluginManager.new(site) + + expect(Jekyll::External).to_not receive(:require_with_graceful_fail) + plugin_manager.require_plugin_files + end + end + + context "plugins_dir is set to the default" do + should "call site's in_source_dir" do + site = double( + :config => { + "plugins_dir" => Jekyll::Configuration::DEFAULTS["plugins_dir"], + }, + :in_source_dir => "/tmp/" + ) + plugin_manager = PluginManager.new(site) + + expect(site).to receive(:in_source_dir).with("_plugins") + plugin_manager.plugins_path + end + end + + context "plugins_dir is set to a different dir" do + should "expand plugin path" do + site = double(:config => { "plugins_dir" => "some_other_plugins_path" }) + plugin_manager = PluginManager.new(site) + + expect(File).to receive(:expand_path).with("some_other_plugins_path") + plugin_manager.plugins_path + end + end + + context "`paginate` config is activated" do + should "print deprecation warning if jekyll-paginate is not present" do + site = double(:config => { "paginate" => true }) + plugin_manager = PluginManager.new(site) + + expect(Jekyll::Deprecator).to( + receive(:deprecation_message).with(%r!jekyll-paginate!) + ) + plugin_manager.deprecation_checks + end + + should "print no deprecation warning if jekyll-paginate is present" do + site = double( + :config => { "paginate" => true, "plugins" => ["jekyll-paginate"] } + ) + plugin_manager = PluginManager.new(site) + + expect(Jekyll::Deprecator).to_not receive(:deprecation_message) + plugin_manager.deprecation_checks + end + end + + should "conscientious require" do + site = double( + :config => { "theme" => "test-dependency-theme" }, + :in_dest_dir => "/tmp/_site/" + ) + plugin_manager = PluginManager.new(site) + + expect(site).to receive(:theme).and_return(true) + expect(site).to receive(:process).and_return(true) + expect(plugin_manager).to( + receive_messages([ + :require_theme_deps, + :require_plugin_files, + :require_gems, + :deprecation_checks, + ]) + ) + plugin_manager.conscientious_require + site.process + assert site.in_dest_dir("test.txt") + end +end diff --git a/test/test_post_reader.rb b/test/test_post_reader.rb new file mode 100644 index 0000000..51c806c --- /dev/null +++ b/test/test_post_reader.rb @@ -0,0 +1,42 @@ +# frozen_string_literal: true + +require "helper" + +class TestPostReader < JekyllUnitTest + context "#read_publishable" do + setup do + @site = Site.new(site_configuration) + @post_reader = PostReader.new(@site) + @dir = "" + @magic_dir = "_posts" + @matcher = Document::DATE_FILENAME_MATCHER + end + + should "skip unprocessable documents" do + all_file_names = all_documents.collect(&:basename) + processed_file_names = processed_documents.collect(&:basename) + + actual_skipped_file_names = all_file_names - processed_file_names + + expected_skipped_file_names = [ + "2008-02-02-not-published.markdown", + "2008-02-03-wrong-extension.yml", + ] + + skipped_file_names_difference = expected_skipped_file_names - actual_skipped_file_names + + assert expected_skipped_file_names.count.positive?, + "There should be at least one document expected to be skipped" + assert_empty skipped_file_names_difference, + "The skipped documents (expected/actual) should be congruent (= empty array)" + end + end + + def all_documents + @post_reader.read_content(@dir, @magic_dir, @matcher) + end + + def processed_documents + @post_reader.read_publishable(@dir, @magic_dir, @matcher) + end +end diff --git a/test/test_regenerator.rb b/test/test_regenerator.rb new file mode 100644 index 0000000..3a3b16b --- /dev/null +++ b/test/test_regenerator.rb @@ -0,0 +1,328 @@ +# frozen_string_literal: true + +require "helper" + +class TestRegenerator < JekyllUnitTest + context "The site regenerator" do + setup do + FileUtils.rm_rf(source_dir(".jekyll-metadata")) + + @site = fixture_site( + "collections" => { + "methods" => { + "output" => true, + }, + }, + "incremental" => true + ) + + @site.read + @page = @site.pages.first + @post = @site.posts.first + @document = @site.docs_to_write.first + @asset_file = @site.pages.find(&:asset_file?) + @regenerator = @site.regenerator + end + + should "regenerate documents and assets if changed or not in metadata" do + assert @regenerator.regenerate?(@page) + assert @regenerator.regenerate?(@post) + assert @regenerator.regenerate?(@document) + assert @regenerator.regenerate?(@asset_file) + end + + should "not regenerate if not changed" do + # Process files + @regenerator.regenerate?(@page) + @regenerator.regenerate?(@post) + @regenerator.regenerate?(@document) + @regenerator.regenerate?(@asset_file) + + # we need to create the destinations for these files, + # because regenerate? checks if the destination exists + [@page, @post, @document, @asset_file].each do |item| + next unless item.respond_to?(:destination) + + dest = item.destination(@site.dest) + FileUtils.mkdir_p(File.dirname(dest)) + FileUtils.touch(dest) + end + @regenerator.write_metadata + @regenerator = Regenerator.new(@site) + + # these should pass, since nothing has changed, and the + # loop above made sure the designations exist + refute @regenerator.regenerate?(@page) + refute @regenerator.regenerate?(@post) + refute @regenerator.regenerate?(@document) + end + + should "regenerate if destination missing" do + # Process files + @regenerator.regenerate?(@page) + @regenerator.regenerate?(@post) + @regenerator.regenerate?(@document) + @regenerator.regenerate?(@asset_file) + + @regenerator.write_metadata + @regenerator = Regenerator.new(@site) + + # make sure the files don't actually exist + [@page, @post, @document, @asset_file].each do |item| + if item.respond_to?(:destination) + dest = item.destination(@site.dest) + File.unlink(dest) if File.exist?(dest) + end + end + + # while nothing has changed, the output files were not + # generated, so they still need to be regenerated + assert @regenerator.regenerate?(@page) + assert @regenerator.regenerate?(@post) + assert @regenerator.regenerate?(@document) + end + + should "always regenerate asset files" do + assert @regenerator.regenerate?(@asset_file) + end + + should "always regenerate objects that don't respond to :path" do + assert @regenerator.regenerate?(Object.new) + end + end + + context "The site regenerator" do + setup do + FileUtils.rm_rf(source_dir(".jekyll-metadata")) + @site = fixture_site( + "incremental" => true + ) + + @site.read + @post = @site.posts.first + @regenerator = @site.regenerator + @regenerator.regenerate?(@post) + + @layout_path = source_dir("_layouts/default.html") + end + + teardown do + File.rename(@layout_path + ".tmp", @layout_path) + end + + should "handle deleted/nonexistent dependencies" do + assert_equal 1, @regenerator.metadata.size + path = @regenerator.metadata.keys[0] + + assert_exist @layout_path + @regenerator.add_dependency(path, @layout_path) + + File.rename(@layout_path, @layout_path + ".tmp") + refute_path_exists(@layout_path) + + @regenerator.clear_cache + assert @regenerator.regenerate?(@post) + end + end + + context "The site metadata" do + setup do + FileUtils.rm_rf(source_dir(".jekyll-metadata")) + + @site = Site.new(Jekyll.configuration( + "source" => source_dir, + "destination" => dest_dir, + "incremental" => true + )) + + @site.process + @path = @site.in_source_dir(@site.pages.first.path) + @regenerator = @site.regenerator + end + + should "store modification times" do + assert_equal File.mtime(@path), @regenerator.metadata[@path]["mtime"] + end + + should "cache processed entries" do + assert @regenerator.cache[@path] + end + + should "clear the cache on clear_cache" do + # @path will be in the cache because the + # site will have processed it + assert @regenerator.cache[@path] + + @regenerator.clear_cache + expected = {} + assert_equal expected, @regenerator.cache + end + + should "write to the metadata file" do + @regenerator.clear + @regenerator.add(@path) + @regenerator.write_metadata + assert File.file?(source_dir(".jekyll-metadata")) + end + + should "read from the metadata file" do + @regenerator = Regenerator.new(@site) + assert_equal File.mtime(@path), @regenerator.metadata[@path]["mtime"] + end + + should "read legacy YAML metadata" do + metadata_file = source_dir(".jekyll-metadata") + @regenerator = Regenerator.new(@site) + + File.write(metadata_file, @regenerator.metadata.to_yaml) + + @regenerator = Regenerator.new(@site) + assert_equal File.mtime(@path), @regenerator.metadata[@path]["mtime"] + end + + should "not crash when reading corrupted marshal file" do + metadata_file = source_dir(".jekyll-metadata") + File.open(metadata_file, "w") do |file| + file.puts Marshal.dump(:foo => "bar")[0, 5] + end + + @regenerator = Regenerator.new(@site) + assert_equal({}, @regenerator.metadata) + end + + # Methods + + should "be able to add a path to the metadata" do + @regenerator.clear + @regenerator.add(@path) + assert_equal File.mtime(@path), @regenerator.metadata[@path]["mtime"] + assert_equal [], @regenerator.metadata[@path]["deps"] + assert @regenerator.cache[@path] + end + + should "return true on nonexistent path" do + @regenerator.clear + assert @regenerator.add("/bogus/path.md") + assert @regenerator.modified?("/bogus/path.md") + end + + should "be able to force a path to regenerate" do + @regenerator.clear + @regenerator.force(@path) + assert @regenerator.cache[@path] + assert @regenerator.modified?(@path) + end + + should "be able to clear metadata and cache" do + @regenerator.clear + @regenerator.add(@path) + assert_equal 1, @regenerator.metadata.length + assert_equal 1, @regenerator.cache.length + @regenerator.clear + assert_equal 0, @regenerator.metadata.length + assert_equal 0, @regenerator.cache.length + end + + should "not regenerate a path if it is not modified" do + @regenerator.clear + @regenerator.add(@path) + @regenerator.write_metadata + @regenerator = Regenerator.new(@site) + + refute @regenerator.modified?(@path) + end + + should "not regenerate if path in cache is false" do + @regenerator.clear + @regenerator.add(@path) + @regenerator.write_metadata + @regenerator = Regenerator.new(@site) + + refute @regenerator.modified?(@path) + refute @regenerator.cache[@path] + refute @regenerator.modified?(@path) + end + + should "regenerate if path in not in metadata" do + @regenerator.clear + @regenerator.add(@path) + + assert @regenerator.modified?(@path) + end + + should "regenerate if path in cache is true" do + @regenerator.clear + @regenerator.add(@path) + + assert @regenerator.modified?(@path) + assert @regenerator.cache[@path] + assert @regenerator.modified?(@path) + end + + should "regenerate if file is modified" do + @regenerator.clear + @regenerator.add(@path) + @regenerator.metadata[@path]["mtime"] = Time.at(0) + @regenerator.write_metadata + @regenerator = Regenerator.new(@site) + + refute_same File.mtime(@path), @regenerator.metadata[@path]["mtime"] + assert @regenerator.modified?(@path) + end + + should "regenerate if dependency is modified" do + @regenerator.clear + @regenerator.add(@path) + @regenerator.write_metadata + @regenerator = Regenerator.new(@site) + + @regenerator.add_dependency(@path, "new.dependency") + assert_equal ["new.dependency"], @regenerator.metadata[@path]["deps"] + assert @regenerator.modified?("new.dependency") + assert @regenerator.modified?(@path) + end + + should "not regenerate again if multiple dependencies" do + multi_deps = @regenerator.metadata.select { |_k, v| v["deps"].length > 2 } + multi_dep_path = multi_deps.keys.first + + assert @regenerator.metadata[multi_dep_path]["deps"].length > 2 + + assert @regenerator.modified?(multi_dep_path) + + @site.process + @regenerator.clear_cache + + refute @regenerator.modified?(multi_dep_path) + end + + should "regenerate everything if metadata is disabled" do + @site.config["incremental"] = false + @regenerator.clear + @regenerator.add(@path) + @regenerator.write_metadata + @regenerator = Regenerator.new(@site) + + assert @regenerator.modified?(@path) + end + end + + context "when incremental regeneration is disabled" do + setup do + FileUtils.rm_rf(source_dir(".jekyll-metadata")) + @site = Site.new(Jekyll.configuration( + "source" => source_dir, + "destination" => dest_dir, + "incremental" => false + )) + + @site.process + @path = @site.in_source_dir(@site.pages.first.path) + @regenerator = @site.regenerator + end + + should "not create .jekyll-metadata" do + refute File.file?(source_dir(".jekyll-metadata")) + end + end +end diff --git a/test/test_related_posts.rb b/test/test_related_posts.rb new file mode 100644 index 0000000..7f9a978 --- /dev/null +++ b/test/test_related_posts.rb @@ -0,0 +1,66 @@ +# frozen_string_literal: true + +require "helper" + +class TestRelatedPosts < JekyllUnitTest + context "building related posts without lsi" do + setup do + @site = fixture_site + end + + should "use the most recent posts for related posts" do + @site.reset + @site.read + + last_post = @site.posts.last + related_posts = Jekyll::RelatedPosts.new(last_post).build + + last_ten_recent_posts = (@site.posts.docs.reverse - [last_post]).first(10) + assert_equal last_ten_recent_posts, related_posts + end + end + + context "building related posts with LSI" do + setup do + if jruby? + skip( + "JRuby does not perform well with CExt, test disabled." + ) + end + + allow_any_instance_of(Jekyll::RelatedPosts).to receive(:display) + @site = fixture_site( + "lsi" => true + ) + + @site.reset + @site.read + require "classifier-reborn" + Jekyll::RelatedPosts.lsi = nil + end + + should "index Jekyll::Post objects" do + @site.posts.docs = @site.posts.docs.first(1) + expect_any_instance_of(::ClassifierReborn::LSI).to \ + receive(:add_item).with(kind_of(Jekyll::Document)) + Jekyll::RelatedPosts.new(@site.posts.last).build_index + end + + should "find related Jekyll::Post objects, given a Jekyll::Post object" do + post = @site.posts.last + allow_any_instance_of(::ClassifierReborn::LSI).to receive(:build_index) + expect_any_instance_of(::ClassifierReborn::LSI).to \ + receive(:find_related).with(post, 11).and_return(@site.posts[-1..-9]) + + Jekyll::RelatedPosts.new(post).build + end + + should "use LSI for the related posts" do + allow_any_instance_of(::ClassifierReborn::LSI).to \ + receive(:find_related).and_return(@site.posts[-1..-9]) + allow_any_instance_of(::ClassifierReborn::LSI).to receive(:build_index) + + assert_equal @site.posts[-1..-9], Jekyll::RelatedPosts.new(@site.posts.last).build + end + end +end diff --git a/test/test_sass.rb b/test/test_sass.rb new file mode 100644 index 0000000..372cded --- /dev/null +++ b/test/test_sass.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +require "helper" + +class TestSass < JekyllUnitTest + context "importing partials" do + setup do + @site = Jekyll::Site.new(Jekyll.configuration( + "source" => source_dir, + "destination" => dest_dir + )) + @site.process + @test_css_file = dest_dir("css/main.css") + end + + should "import SCSS partial" do + result = <<~CSS + .half { width: 50%; } + + /*# sourceMappingURL=main.css.map */ + CSS + assert_equal result.rstrip, File.read(@test_css_file) + end + + should "register the SCSS converter" do + message = "SCSS converter implementation should exist." + refute !@site.find_converter_instance(Jekyll::Converters::Scss), message + end + + should "register the Sass converter" do + message = "Sass converter implementation should exist." + refute !@site.find_converter_instance(Jekyll::Converters::Sass), message + end + end +end diff --git a/test/test_site.rb b/test/test_site.rb new file mode 100644 index 0000000..7556174 --- /dev/null +++ b/test/test_site.rb @@ -0,0 +1,762 @@ +# frozen_string_literal: true + +require "helper" + +class TestSite < JekyllUnitTest + def with_image_as_post + tmp_image_path = File.join(source_dir, "_posts", "2017-09-01-jekyll-sticker.jpg") + FileUtils.cp File.join(Dir.pwd, "docs", "img", "jekyll-sticker.jpg"), tmp_image_path + yield + ensure + FileUtils.rm tmp_image_path + end + + def read_posts + @site.posts.docs.concat(PostReader.new(@site).read_posts("")) + posts = Dir[source_dir("_posts", "**", "*")] + posts.delete_if do |post| + File.directory?(post) && post !~ Document::DATE_FILENAME_MATCHER + end + end + + context "configuring sites" do + should "have an array for plugins by default" do + site = Site.new default_configuration + assert_equal [File.join(Dir.pwd, "_plugins")], site.plugins + end + + should "look for plugins under the site directory by default" do + site = Site.new(site_configuration) + assert_equal [source_dir("_plugins")], site.plugins + end + + should "have an array for plugins if passed as a string" do + site = Site.new(site_configuration("plugins_dir" => "/tmp/plugins")) + array = [temp_dir("plugins")] + assert_equal array, site.plugins + end + + should "have an array for plugins if passed as an array" do + site = Site.new(site_configuration( + "plugins_dir" => ["/tmp/plugins", "/tmp/otherplugins"] + )) + array = [temp_dir("plugins"), temp_dir("otherplugins")] + assert_equal array, site.plugins + end + + should "have an empty array for plugins if nothing is passed" do + site = Site.new(site_configuration("plugins_dir" => [])) + assert_equal [], site.plugins + end + + should "have the default for plugins if nil is passed" do + site = Site.new(site_configuration("plugins_dir" => nil)) + assert_equal [source_dir("_plugins")], site.plugins + end + + should "default baseurl to `nil`" do + site = Site.new(default_configuration) + assert_nil site.baseurl + end + + should "expose baseurl passed in from config" do + site = Site.new(site_configuration("baseurl" => "/blog")) + assert_equal "/blog", site.baseurl + end + + should "only include theme includes_path if the path exists" do + site = fixture_site("theme" => "test-theme") + assert_equal [source_dir("_includes"), theme_dir("_includes")], + site.includes_load_paths + + allow(File).to receive(:directory?).with(theme_dir("_sass")).and_return(true) + allow(File).to receive(:directory?).with(theme_dir("_layouts")).and_return(true) + allow(File).to receive(:directory?).with(theme_dir("_includes")).and_return(false) + site = fixture_site("theme" => "test-theme") + assert_equal [source_dir("_includes")], site.includes_load_paths + end + + should "configure cache_dir" do + fixture_site.process + assert File.directory?(source_dir(".jekyll-cache", "Jekyll", "Cache")) + assert File.directory?(source_dir(".jekyll-cache", "Jekyll", "Cache", "Jekyll--Cache")) + end + + should "use .jekyll-cache directory at source as cache_dir by default" do + site = Site.new(default_configuration) + assert_equal File.join(site.source, ".jekyll-cache"), site.cache_dir + end + + should "have the cache_dir hidden from Git" do + site = fixture_site + assert_equal site.source, source_dir + assert_exist source_dir(".jekyll-cache", ".gitignore") + assert_equal( + "# ignore everything in this directory\n*\n", + File.binread(source_dir(".jekyll-cache", ".gitignore")) + ) + end + + should "load config file from theme-gem as Jekyll::Configuration instance" do + site = fixture_site("theme" => "test-theme") + assert_instance_of Jekyll::Configuration, site.config + assert_equal "Hello World", site.config["title"] + end + + context "with a custom cache_dir configuration" do + should "have the custom cache_dir hidden from Git" do + site = fixture_site("cache_dir" => "../../custom-cache-dir") + refute_exist File.expand_path("../../custom-cache-dir/.gitignore", site.source) + assert_exist source_dir("custom-cache-dir", ".gitignore") + assert_equal( + "# ignore everything in this directory\n*\n", + File.binread(source_dir("custom-cache-dir", ".gitignore")) + ) + end + end + end + + context "creating sites" do + setup do + @site = Site.new(site_configuration) + @num_invalid_posts = 6 + end + + teardown do + self.class.send(:remove_const, :MyGenerator) if defined?(MyGenerator) + end + + should "have an empty tag hash by default" do + assert_equal({}, @site.tags) + end + + should "give site with parsed pages and posts to generators" do + class MyGenerator < Generator + def generate(site) + site.pages.dup.each do |page| + raise "#{page} isn't a page" unless page.is_a?(Page) + raise "#{page} doesn't respond to :name" unless page.respond_to?(:name) + end + site.file_read_opts[:secret_message] = "hi" + end + end + @site = Site.new(site_configuration) + @site.read + @site.generate + refute_equal 0, @site.pages.size + assert_equal "hi", @site.file_read_opts[:secret_message] + end + + should "reset data before processing" do + clear_dest + @site.process + before_posts = @site.posts.length + before_layouts = @site.layouts.length + before_categories = @site.categories.length + before_tags = @site.tags.length + before_pages = @site.pages.length + before_static_files = @site.static_files.length + before_time = @site.time + + @site.process + assert_equal before_posts, @site.posts.length + assert_equal before_layouts, @site.layouts.length + assert_equal before_categories, @site.categories.length + assert_equal before_tags, @site.tags.length + assert_equal before_pages, @site.pages.length + assert_equal before_static_files, @site.static_files.length + assert before_time <= @site.time + end + + should "write only modified static files" do + clear_dest + StaticFile.reset_cache + @site.regenerator.clear + + @site.process + some_static_file = @site.static_files[0].path + dest = File.expand_path(@site.static_files[0].destination(@site.dest)) + mtime1 = File.stat(dest).mtime.to_i # first run must generate dest file + + # need to sleep because filesystem timestamps have best resolution in seconds + sleep 1 + @site.process + mtime2 = File.stat(dest).mtime.to_i + assert_equal mtime1, mtime2 + + # simulate file modification by user + FileUtils.touch some_static_file + + sleep 1 + @site.process + mtime3 = File.stat(dest).mtime.to_i + refute_equal mtime2, mtime3 # must be regenerated! + + sleep 1 + @site.process + mtime4 = File.stat(dest).mtime.to_i + assert_equal mtime3, mtime4 # no modifications, so must be the same + end + + should "write static files if not modified but missing in destination" do + clear_dest + StaticFile.reset_cache + @site.regenerator.clear + + @site.process + dest = File.expand_path(@site.static_files[0].destination(@site.dest)) + mtime1 = File.stat(dest).mtime.to_i # first run must generate dest file + + # need to sleep because filesystem timestamps have best resolution in seconds + sleep 1 + @site.process + mtime2 = File.stat(dest).mtime.to_i + assert_equal mtime1, mtime2 + + # simulate destination file deletion + File.unlink dest + refute_path_exists(dest) + + sleep 1 + @site.process + mtime3 = File.stat(dest).mtime.to_i + assert_equal mtime2, mtime3 # must be regenerated and with original mtime! + + sleep 1 + @site.process + mtime4 = File.stat(dest).mtime.to_i + assert_equal mtime3, mtime4 # no modifications, so remain the same + end + + should "setup plugins in priority order" do + assert_equal( + @site.converters.sort_by(&:class).map { |c| c.class.priority }, + @site.converters.map { |c| c.class.priority } + ) + assert_equal( + @site.generators.sort_by(&:class).map { |g| g.class.priority }, + @site.generators.map { |g| g.class.priority } + ) + end + + should "sort pages alphabetically" do + method = Dir.method(:entries) + allow(Dir).to receive(:entries) do |*args, &block| + method.call(*args, &block).reverse + end + @site.process + # exclude files in symlinked directories here and insert them in the + # following step when not on Windows. + # rubocop:disable Style/WordArray + sorted_pages = %w( + %#\ +.md + .htaccess + about.html + application.coffee + bar.html + coffeescript.coffee + contacts.html + deal.with.dots.html + dynamic_file.php + environment.html + exploit.md + foo.md + foo.md + humans.txt + index.html + index.html + info.md + main.css.map + main.scss + properties.html + sitemap.xml + static_files.html + test-styles.css.map + test-styles.scss + trailing-dots...md + ) + # rubocop:enable Style/WordArray + unless Utils::Platforms.really_windows? + # files in symlinked directories may appear twice + sorted_pages.push("main.css.map", "main.scss", "symlinked-file").sort! + end + assert_equal sorted_pages, @site.pages.map(&:name).sort! + end + + should "read posts" do + posts = read_posts + assert_equal posts.size - @num_invalid_posts, @site.posts.size + end + + should "skip posts with invalid encoding" do + with_image_as_post do + posts = read_posts + num_invalid_posts = @num_invalid_posts + 1 + assert_equal posts.size - num_invalid_posts, @site.posts.size + end + end + + should "read pages with YAML front matter" do + abs_path = File.expand_path("about.html", @site.source) + assert Utils.has_yaml_header?(abs_path) + end + + should "enforce a strict 3-dash limit on the start of the YAML front matter" do + abs_path = File.expand_path("pgp.key", @site.source) + refute Utils.has_yaml_header?(abs_path) + end + + should "expose jekyll version to site payload" do + assert_equal Jekyll::VERSION, @site.site_payload["jekyll"]["version"] + end + + should "expose list of static files to site payload" do + assert_equal @site.static_files, @site.site_payload["site"]["static_files"] + end + + should "deploy payload" do + clear_dest + @site.process + + posts = Dir[source_dir("**", "_posts", "**", "*")] + posts.delete_if do |post| + File.directory?(post) && post !~ Document::DATE_FILENAME_MATCHER + end + categories = %w( + 2013 bar baz category foo z_category MixedCase Mixedcase publish_test win + ).sort + + assert_equal posts.size - @num_invalid_posts, @site.posts.size + assert_equal categories, @site.categories.keys.sort + assert_equal 5, @site.categories["foo"].size + end + + context "error handling" do + should "raise if destination is included in source" do + assert_raises Jekyll::Errors::FatalException do + Site.new(site_configuration("destination" => source_dir)) + end + end + + should "raise if destination is source" do + assert_raises Jekyll::Errors::FatalException do + Site.new(site_configuration("destination" => File.join(source_dir, ".."))) + end + end + + should "raise for bad frontmatter if strict_front_matter is set" do + site = Site.new(site_configuration( + "collections" => ["broken"], + "strict_front_matter" => true + )) + assert_raises(Psych::SyntaxError) do + site.process + end + end + + should "not raise for bad frontmatter if strict_front_matter is not set" do + site = Site.new(site_configuration( + "collections" => ["broken"], + "strict_front_matter" => false + )) + site.process + end + end + + context "with orphaned files in destination" do + setup do + clear_dest + @site.regenerator.clear + @site.process + # generate some orphaned files: + # single file + FileUtils.touch(dest_dir("obsolete.html")) + # single file in sub directory + FileUtils.mkdir(dest_dir("qux")) + FileUtils.touch(dest_dir("qux/obsolete.html")) + # empty directory + FileUtils.mkdir(dest_dir("quux")) + FileUtils.mkdir(dest_dir(".git")) + FileUtils.mkdir(dest_dir(".svn")) + FileUtils.mkdir(dest_dir(".hg")) + # single file in repository + FileUtils.touch(dest_dir(".git/HEAD")) + FileUtils.touch(dest_dir(".svn/HEAD")) + FileUtils.touch(dest_dir(".hg/HEAD")) + end + + teardown do + FileUtils.rm_f(dest_dir("obsolete.html")) + FileUtils.rm_rf(dest_dir("qux")) + FileUtils.rm_f(dest_dir("quux")) + FileUtils.rm_rf(dest_dir(".git")) + FileUtils.rm_rf(dest_dir(".svn")) + FileUtils.rm_rf(dest_dir(".hg")) + end + + should "remove orphaned files in destination" do + @site.process + refute_exist dest_dir("obsolete.html") + refute_exist dest_dir("qux") + refute_exist dest_dir("quux") + assert_exist dest_dir(".git") + assert_exist dest_dir(".git", "HEAD") + end + + should "remove orphaned files in destination - keep_files .svn" do + config = site_configuration("keep_files" => %w(.svn)) + @site = Site.new(config) + @site.process + refute_exist dest_dir(".htpasswd") + refute_exist dest_dir("obsolete.html") + refute_exist dest_dir("qux") + refute_exist dest_dir("quux") + refute_exist dest_dir(".git") + refute_exist dest_dir(".git", "HEAD") + assert_exist dest_dir(".svn") + assert_exist dest_dir(".svn", "HEAD") + end + end + + context "using a non-default markdown processor in the configuration" do + should "use the non-default markdown processor" do + class Jekyll::Converters::Markdown::CustomMarkdown + def initialize(*args) + @args = args + end + + def convert(*_args) + "" + end + end + + custom_processor = "CustomMarkdown" + s = Site.new(site_configuration("markdown" => custom_processor)) + s.process + + # Do some cleanup, we don't like straggling stuff. + Jekyll::Converters::Markdown.send(:remove_const, :CustomMarkdown) + end + + should "ignore, if there are any bad characters in the class name" do + module Jekyll::Converters::Markdown::Custom + class Markdown + def initialize(*args) + @args = args + end + + def convert(*_args) + "" + end + end + end + + bad_processor = "Custom::Markdown" + s = Site.new(site_configuration( + "markdown" => bad_processor, + "incremental" => false + )) + assert_raises Jekyll::Errors::FatalException do + s.process + end + + # Do some cleanup, we don't like straggling stuff. + Jekyll::Converters::Markdown.send(:remove_const, :Custom) + end + end + + context "with an invalid markdown processor in the configuration" do + should "not throw an error at initialization time" do + bad_processor = "not a processor name" + assert Site.new(site_configuration("markdown" => bad_processor)) + end + + should "throw FatalException at process time" do + bad_processor = "not a processor name" + s = Site.new(site_configuration( + "markdown" => bad_processor, + "incremental" => false + )) + assert_raises Jekyll::Errors::FatalException do + s.process + end + end + end + + context "data directory" do + should "auto load yaml files" do + site = Site.new(site_configuration) + site.process + + file_content = SafeYAML.load_file(File.join(source_dir, "_data", "members.yaml")) + + assert_equal site.data["members"], file_content + assert_equal site.site_payload["site"]["data"]["members"], file_content + end + + should "load yaml files from extracted method" do + site = Site.new(site_configuration) + site.process + + file_content = DataReader.new(site) + .read_data_file(source_dir("_data", "members.yaml")) + + assert_equal site.data["members"], file_content + assert_equal site.site_payload["site"]["data"]["members"], file_content + end + + should "auto load yml files" do + site = Site.new(site_configuration) + site.process + + file_content = SafeYAML.load_file(File.join(source_dir, "_data", "languages.yml")) + + assert_equal site.data["languages"], file_content + assert_equal site.site_payload["site"]["data"]["languages"], file_content + end + + should "auto load json files" do + site = Site.new(site_configuration) + site.process + + file_content = SafeYAML.load_file(File.join(source_dir, "_data", "members.json")) + + assert_equal site.data["members"], file_content + assert_equal site.site_payload["site"]["data"]["members"], file_content + end + + should "auto load yaml files in subdirectory" do + site = Site.new(site_configuration) + site.process + + file_content = SafeYAML.load_file(File.join( + source_dir, "_data", "categories", "dairy.yaml" + )) + + assert_equal site.data["categories"]["dairy"], file_content + assert_equal( + site.site_payload["site"]["data"]["categories"]["dairy"], + file_content + ) + end + + should "auto load yaml files in subdirectory with a period in the name" do + site = Site.new(site_configuration) + site.process + + file_content = SafeYAML.load_file(File.join( + source_dir, "_data", "categories.01", "dairy.yaml" + )) + + assert_equal site.data["categories01"]["dairy"], file_content + assert_equal( + site.site_payload["site"]["data"]["categories01"]["dairy"], + file_content + ) + end + + should "load symlink files in unsafe mode" do + site = Site.new(site_configuration("safe" => false)) + site.process + + file_content = SafeYAML.load_file(File.join(source_dir, "_data", "products.yml")) + + assert_equal site.data["products"], file_content + assert_equal site.site_payload["site"]["data"]["products"], file_content + end + + should "load the symlink files in safe mode, " \ + "as they resolve to inside site.source" do + site = Site.new(site_configuration("safe" => true)) + site.process + file_content = SafeYAML.load_file(File.join(source_dir, "_data", "products.yml")) + assert_equal site.data["products"], file_content + assert_equal site.site_payload["site"]["data"]["products"], file_content + end + end + + context "manipulating the Jekyll environment" do + setup do + @site = Site.new(site_configuration( + "incremental" => false + )) + @site.process + @page = @site.pages.find { |p| p.name == "environment.html" } + end + + should "default to 'development'" do + assert_equal "development", @page.content.strip + end + + context "in production" do + setup do + ENV["JEKYLL_ENV"] = "production" + @site = Site.new(site_configuration( + "incremental" => false + )) + @site.process + @page = @site.pages.find { |p| p.name == "environment.html" } + end + + teardown do + ENV.delete("JEKYLL_ENV") + end + + should "be overridden by JEKYLL_ENV" do + assert_equal "production", @page.content.strip + end + end + end + + context "when setting theme" do + should "set no theme if config is not set" do + expect($stderr).not_to receive(:puts) + expect($stdout).not_to receive(:puts) + site = fixture_site("theme" => nil) + assert_nil site.theme + end + + should "set no theme if config is a hash" do + output = capture_output do + site = fixture_site("theme" => {}) + assert_nil site.theme + end + expected_msg = "Theme: value of 'theme' in config should be String to use " \ + "gem-based themes, but got Hash\n" + assert_includes output, expected_msg + end + + should "set a theme if the config is a string" do + [:debug, :info, :warn, :error].each do |level| + if level == :info + expect(Jekyll.logger.writer).to receive(level) + else + expect(Jekyll.logger.writer).not_to receive(level) + end + end + site = fixture_site("theme" => "test-theme") + assert_instance_of Jekyll::Theme, site.theme + assert_equal "test-theme", site.theme.name + end + end + + context "with liquid profiling" do + setup do + @site = Site.new(site_configuration("profile" => true)) + end + + # Suppress output while testing + setup do + $stdout = StringIO.new + end + teardown do + $stdout = STDOUT + end + + should "print profile table" do + expect(@site.liquid_renderer).to receive(:stats_table) + @site.process + end + end + + context "incremental build" do + setup do + @site = Site.new(site_configuration( + "incremental" => true + )) + @site.read + end + + should "build incrementally" do + contacts_html = @site.pages.find { |p| p.name == "contacts.html" } + @site.process + + source = @site.in_source_dir(contacts_html.path) + dest = File.expand_path(contacts_html.destination(@site.dest)) + mtime1 = File.stat(dest).mtime.to_i # first run must generate dest file + + # need to sleep because filesystem timestamps have best resolution in seconds + sleep 1 + @site.process + mtime2 = File.stat(dest).mtime.to_i + assert_equal mtime1, mtime2 # no modifications, so remain the same + + # simulate file modification by user + FileUtils.touch source + + sleep 1 + @site.process + mtime3 = File.stat(dest).mtime.to_i + refute_equal mtime2, mtime3 # must be regenerated + + sleep 1 + @site.process + mtime4 = File.stat(dest).mtime.to_i + assert_equal mtime3, mtime4 # no modifications, so remain the same + end + + should "regenerate files that have had their destination deleted" do + contacts_html = @site.pages.find { |p| p.name == "contacts.html" } + @site.process + + dest = File.expand_path(contacts_html.destination(@site.dest)) + mtime1 = File.stat(dest).mtime.to_i # first run must generate dest file + + # simulate file modification by user + File.unlink dest + refute File.file?(dest) + + sleep 1 # sleep for 1 second, since mtimes have 1s resolution + @site.process + assert File.file?(dest) + mtime2 = File.stat(dest).mtime.to_i + refute_equal mtime1, mtime2 # must be regenerated + end + end + + context "#in_cache_dir method" do + setup do + @site = Site.new( + site_configuration( + "cache_dir" => "../../custom-cache-dir" + ) + ) + end + + should "create sanitized paths within the cache directory" do + assert_equal File.join(@site.source, "custom-cache-dir"), @site.cache_dir + assert_equal( + File.join(@site.source, "custom-cache-dir", "foo.md.metadata"), + @site.in_cache_dir("../../foo.md.metadata") + ) + end + end + end + + context "site process phases" do + should "return nil as documented" do + site = fixture_site + [:reset, :read, :generate, :render, :cleanup, :write].each do |phase| + assert_nil site.send(phase) + end + end + end + + context "static files in a collection" do + should "be exposed via site instance" do + site = fixture_site("collections" => ["methods"]) + site.read + + assert_includes site.static_files.map(&:relative_path), "_methods/extensionless_static_file" + end + + should "not be revisited in `Site#each_site_file`" do + site = fixture_site("collections" => { "methods" => { "output" => true } }) + site.read + + visited_files = [] + site.each_site_file { |file| visited_files << file } + assert_equal visited_files.count, visited_files.uniq.count + end + end +end diff --git a/test/test_site_drop.rb b/test/test_site_drop.rb new file mode 100644 index 0000000..6d0b149 --- /dev/null +++ b/test/test_site_drop.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +require "helper" + +class TestSiteDrop < JekyllUnitTest + context "a site drop" do + setup do + @site = fixture_site( + "collections" => ["thanksgiving"] + ) + @site.process + @drop = @site.to_liquid.site + end + + should "respond to `key?`" do + assert_respond_to @drop, :key? + end + + should "find a key if it's in the collection of the drop" do + assert @drop.key?("thanksgiving") + end + end +end diff --git a/test/test_static_file.rb b/test/test_static_file.rb new file mode 100644 index 0000000..e87ff4a --- /dev/null +++ b/test/test_static_file.rb @@ -0,0 +1,189 @@ +# frozen_string_literal: true + +require "helper" + +class TestStaticFile < JekyllUnitTest + def make_dummy_file(filename) + File.write(source_dir(filename), "some content") + end + + def modify_dummy_file(filename) + string = "some content" + offset = string.size + File.write(source_dir(filename), "more content", offset) + end + + def remove_dummy_file(filename) + File.delete(source_dir(filename)) + end + + def setup_static_file(base, dir, name) + Dir.chdir(@site.source) { StaticFile.new(@site, base, dir, name) } + end + + def setup_static_file_with_collection(base, dir, name, metadata) + site = fixture_site("collections" => { "foo" => metadata }) + Dir.chdir(site.source) do + StaticFile.new(site, base, dir, name, site.collections["foo"]) + end + end + + def setup_static_file_with_defaults(base, dir, name, defaults) + site = fixture_site("defaults" => defaults) + Dir.chdir(site.source) do + StaticFile.new(site, base, dir, name) + end + end + + context "A StaticFile" do + setup do + clear_dest + @site = fixture_site + @filename = "static_file.txt" + make_dummy_file(@filename) + @static_file = setup_static_file(@site.source, "", @filename) + end + + teardown do + remove_dummy_file(@filename) if File.exist?(source_dir(@filename)) + end + + should "return a simple string on inspection" do + static_file = setup_static_file("root", "dir", @filename) + assert_equal "#<Jekyll::StaticFile @relative_path=\"dir/#{@filename}\">", static_file.inspect + end + + should "have a source file path" do + static_file = setup_static_file("root", "dir", @filename) + assert_equal "root/dir/#{@filename}", static_file.path + end + + should "ignore a nil base or dir" do + assert_equal "dir/#{@filename}", setup_static_file(nil, "dir", @filename).path + assert_equal "base/#{@filename}", setup_static_file("base", nil, @filename).path + end + + should "have a destination relative directory without a collection" do + static_file = setup_static_file("root", "dir/subdir", "file.html") + assert_nil static_file.type + assert_equal "dir/subdir/file.html", static_file.url + assert_equal "dir/subdir", static_file.destination_rel_dir + end + + should "have a destination relative directory with a collection" do + static_file = setup_static_file_with_collection( + "root", + "_foo/dir/subdir", + "file.html", + "output" => true + ) + assert_equal :foo, static_file.type + assert_equal "/foo/dir/subdir/file.html", static_file.url + assert_equal "/foo/dir/subdir", static_file.destination_rel_dir + end + + should "use its collection's permalink template for destination relative directory" do + static_file = setup_static_file_with_collection( + "root", + "_foo/dir/subdir", + "file.html", + "output" => true, "permalink" => "/:path/" + ) + assert_equal :foo, static_file.type + assert_equal "/dir/subdir/file.html", static_file.url + assert_equal "/dir/subdir", static_file.destination_rel_dir + end + + should "be writable by default" do + static_file = setup_static_file("root", "dir/subdir", "file.html") + assert(static_file.write?, + "static_file.write? should return true by default") + end + + should "use the _config.yml defaults to determine writability" do + defaults = [{ + "scope" => { "path" => "private" }, + "values" => { "published" => false }, + }] + static_file = setup_static_file_with_defaults( + "root", + "private/dir/subdir", + "file.html", + defaults + ) + refute(static_file.write?, + "static_file.write? should return false when _config.yml sets " \ + "`published: false`") + end + + should "respect front matter defaults" do + defaults = [{ + "scope" => { "path" => "" }, + "values" => { "front-matter" => "default" }, + }] + + static_file = setup_static_file_with_defaults "", "", "file.pdf", defaults + assert_equal "default", static_file.data["front-matter"] + end + + should "include front matter defaults in to_liquid" do + defaults = [{ + "scope" => { "path" => "" }, + "values" => { "front-matter" => "default" }, + }] + + static_file = setup_static_file_with_defaults "", "", "file.pdf", defaults + hash = static_file.to_liquid + assert hash.key? "front-matter" + assert_equal "default", hash["front-matter"] + end + + should "know its last modification time" do + assert_equal File.stat(@static_file.path).mtime.to_i, @static_file.mtime + end + + should "only set modified time if not a symlink" do + expect(File).to receive(:symlink?).and_return(true) + expect(File).not_to receive(:utime) + @static_file.write(dest_dir) + + allow(File).to receive(:symlink?).and_call_original + end + + should "known if the source path is modified, when it is" do + sleep 1 + modify_dummy_file(@filename) + assert @static_file.modified? + end + + should "known if the source path is modified, when it's not" do + @static_file.write(dest_dir) + sleep 1 # wait, else the times are still the same + refute @static_file.modified? + end + + should "known whether to write the file to the filesystem" do + assert @static_file.write?, "always true, with current implementation" + end + + should "be able to write itself to the destination directory" do + assert @static_file.write(dest_dir) + end + + should "be able to convert to liquid" do + expected = { + "basename" => "static_file", + "name" => "static_file.txt", + "extname" => ".txt", + "modified_time" => @static_file.modified_time, + "path" => "/static_file.txt", + "collection" => nil, + } + assert_equal expected, @static_file.to_liquid.to_h + end + + should "jsonify its liquid drop instead of itself" do + assert_equal @static_file.to_liquid.to_json, @static_file.to_json + end + end +end diff --git a/test/test_tags.rb b/test/test_tags.rb new file mode 100644 index 0000000..a82a6f2 --- /dev/null +++ b/test/test_tags.rb @@ -0,0 +1,1191 @@ +# frozen_string_literal: true + +require "helper" + +class TestTags < JekyllUnitTest + def setup + FileUtils.mkdir_p("tmp") + end + + def create_post(content, override = {}, converter_class = Jekyll::Converters::Markdown) + site = fixture_site({ "highlighter" => "rouge" }.merge(override)) + + site.posts.docs.concat(PostReader.new(site).read_posts("")) if override["read_posts"] + CollectionReader.new(site).read if override["read_collections"] + site.read if override["read_all"] + + info = { :filters => [Jekyll::Filters], :registers => { :site => site } } + @converter = site.converters.find { |c| c.instance_of?(converter_class) } + payload = { "highlighter_prefix" => @converter.highlighter_prefix, + "highlighter_suffix" => @converter.highlighter_suffix, } + + @result = Liquid::Template.parse(content).render!(payload, info) + @result = @converter.convert(@result) + end + + def fill_post(code, override = {}) + content = <<~CONTENT + --- + title: This is a test + --- + + This document has some highlighted code in it. + + {% highlight text %} + #{code} + {% endhighlight %} + {% highlight text linenos %} + #{code} + {% endhighlight %} + CONTENT + create_post(content, override) + end + + def highlight_block_with_opts(options_string) + Jekyll::Tags::HighlightBlock.parse( + "highlight", + options_string, + Liquid::Tokenizer.new("test{% endhighlight %}\n"), + Liquid::ParseContext.new + ) + end + + context "language name" do + should "match only the required set of chars" do + r = Jekyll::Tags::HighlightBlock::SYNTAX + assert_match r, "ruby" + assert_match r, "c#" + assert_match r, "xml+cheetah" + assert_match r, "x.y" + assert_match r, "coffee-script" + assert_match r, "shell_session" + + refute_match r, "blah^" + + assert_match r, "ruby key=val" + assert_match r, "ruby a=b c=d" + end + end + + context "highlight tag in unsafe mode" do + should "set the no options with just a language name" do + tag = highlight_block_with_opts("ruby ") + assert_equal({}, tag.instance_variable_get(:@highlight_options)) + end + + should "set the linenos option as 'inline' if no linenos value" do + tag = highlight_block_with_opts("ruby linenos ") + assert_equal( + { :linenos => "inline" }, + tag.instance_variable_get(:@highlight_options) + ) + end + + should "set the linenos option to 'table' " \ + "if the linenos key is given the table value" do + tag = highlight_block_with_opts("ruby linenos=table ") + assert_equal( + { :linenos => "table" }, + tag.instance_variable_get(:@highlight_options) + ) + end + + should "recognize nowrap option with linenos set" do + tag = highlight_block_with_opts("ruby linenos=table nowrap ") + assert_equal( + { :linenos => "table", :nowrap => true }, + tag.instance_variable_get(:@highlight_options) + ) + end + + should "recognize the cssclass option" do + tag = highlight_block_with_opts("ruby linenos=table cssclass=hl ") + assert_equal( + { :cssclass => "hl", :linenos => "table" }, + tag.instance_variable_get(:@highlight_options) + ) + end + + should "recognize the hl_linenos option and its value" do + tag = highlight_block_with_opts("ruby linenos=table cssclass=hl hl_linenos=3 ") + assert_equal( + { :cssclass => "hl", :linenos => "table", :hl_linenos => "3" }, + tag.instance_variable_get(:@highlight_options) + ) + end + + should "recognize multiple values of hl_linenos" do + tag = highlight_block_with_opts 'ruby linenos=table cssclass=hl hl_linenos="3 5 6" ' + assert_equal( + { :cssclass => "hl", :linenos => "table", :hl_linenos => %w(3 5 6) }, + tag.instance_variable_get(:@highlight_options) + ) + end + + should "treat language name as case insensitive" do + tag = highlight_block_with_opts("Ruby ") + assert_equal( + "ruby", + tag.instance_variable_get(:@lang), + "lexers should be case insensitive" + ) + end + end + + context "with the rouge highlighter" do + context "post content has highlight tag" do + setup do + fill_post("test") + end + + should "render markdown with rouge" do + assert_match( + %(<pre><code class="language-text" data-lang="text">test</code></pre>), + @result + ) + end + + should "render markdown with rouge with line numbers" do + assert_match( + %(<table class="rouge-table"><tbody>) + + %(<tr><td class="gutter gl">) + + %(<pre class="lineno">1\n</pre></td>) + + %(<td class="code"><pre>test\n</pre></td></tr>) + + %(</tbody></table>), + @result + ) + end + end + + context "post content has raw tag" do + setup do + content = <<~CONTENT + --- + title: This is a test + --- + + ```liquid + {% raw %} + {{ site.baseurl }}{% link _collection/name-of-document.md %} + {% endraw %} + ``` + CONTENT + create_post(content) + end + + should "render markdown with rouge" do + assert_match( + %(<div class="language-liquid highlighter-rouge">) + + %(<div class="highlight"><pre class="highlight"><code>), + @result + ) + end + end + + context "post content has highlight with file reference" do + setup do + fill_post("./jekyll.gemspec") + end + + should "not embed the file" do + assert_match( + '<pre><code class="language-text" data-lang="text">' \ + "./jekyll.gemspec</code></pre>", + @result + ) + end + end + + context "post content has highlight tag with UTF character" do + setup do + fill_post("Æ") + end + + should "render markdown with pygments line handling" do + assert_match( + '<pre><code class="language-text" data-lang="text">Æ</code></pre>', + @result + ) + end + end + + context "post content has highlight tag with preceding spaces & lines" do + setup do + fill_post <<~EOS + + + [,1] [,2] + [1,] FALSE TRUE + [2,] FALSE TRUE + EOS + end + + should "only strip the preceding newlines" do + assert_match( + '<pre><code class="language-text" data-lang="text"> [,1] [,2]', + @result + ) + end + end + + context "post content has highlight tag with " \ + "preceding spaces & lines in several places" do + setup do + fill_post <<~EOS + + + [,1] [,2] + + + [1,] FALSE TRUE + [2,] FALSE TRUE + + + EOS + end + + should "only strip the newlines which precede and succeed the entire block" do + assert_match( + "<pre><code class=\"language-text\" data-lang=\"text\"> [,1] [,2]\n\n\n" \ + "[1,] FALSE TRUE\n[2,] FALSE TRUE</code></pre>", + @result + ) + end + end + + context "post content has highlight tag with linenumbers" do + setup do + create_post <<~EOS + --- + title: This is a test + --- + + This is not yet highlighted + {% highlight php linenos %} + test + {% endhighlight %} + + This should not be highlighted, right? + EOS + end + + should "should stop highlighting at boundary with rouge" do + expected = <<~EOS + <p>This is not yet highlighted</p>\n + <figure class="highlight"><pre><code class="language-php" data-lang="php"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1 + </pre></td><td class="code"><pre><span class="n">test</span>\n</pre></td></tr></tbody></table></code></pre></figure>\n + <p>This should not be highlighted, right?</p> + EOS + assert_match(expected, @result) + end + end + + context "post content has highlight tag with " \ + "preceding spaces & Windows-style newlines" do + setup do + fill_post "\r\n\r\n\r\n [,1] [,2]" + end + + should "only strip the preceding newlines" do + assert_match( + '<pre><code class="language-text" data-lang="text"> [,1] [,2]', + @result + ) + end + end + + context "post content has highlight tag with only preceding spaces" do + setup do + fill_post <<~EOS + [,1] [,2] + [1,] FALSE TRUE + [2,] FALSE TRUE + EOS + end + + should "only strip the preceding newlines" do + assert_match( + '<pre><code class="language-text" data-lang="text"> [,1] [,2]', + @result + ) + end + end + end + + context "simple post with markdown and pre tags" do + setup do + @content = <<~CONTENT + --- + title: Kramdown post with pre + --- + + _FIGHT!_ + + {% highlight ruby %} + puts "3..2..1.." + {% endhighlight %} + + *FINISH HIM* + CONTENT + end + + context "using Kramdown" do + setup do + create_post(@content, "markdown" => "kramdown") + end + + should "parse correctly" do + assert_match %r{<em>FIGHT!</em>}, @result + assert_match %r!<em>FINISH HIM</em>!, @result + end + end + end + + context "simple page with post linking" do + setup do + content = <<~CONTENT + --- + title: Post linking + --- + + {% post_url 2008-11-21-complex %} + CONTENT + create_post(content, + "permalink" => "pretty", + "source" => source_dir, + "destination" => dest_dir, + "read_posts" => true) + end + + should "not cause an error" do + refute_match(%r!markdown-html-error!, @result) + end + + should "have the URL to the 'complex' post from 2008-11-21" do + assert_match %r!/2008/11/21/complex/!, @result + end + end + + context "simple page with post linking containing special characters" do + setup do + content = <<~CONTENT + --- + title: Post linking + --- + + {% post_url 2016-11-26-special-chars-(+) %} + CONTENT + create_post(content, + "permalink" => "pretty", + "source" => source_dir, + "destination" => dest_dir, + "read_posts" => true) + end + + should "not cause an error" do + refute_match(%r!markdown-html-error!, @result) + end + + should "have the URL to the 'special-chars' post from 2016-11-26" do + assert_match %r!/2016/11/26/special-chars-\(\+\)/!, @result + end + end + + context "simple page with nested post linking" do + setup do + content = <<~CONTENT + --- + title: Post linking + --- + + - 1 {% post_url 2008-11-21-complex %} + - 2 {% post_url /2008-11-21-complex %} + - 3 {% post_url es/2008-11-21-nested %} + - 4 {% post_url /es/2008-11-21-nested %} + CONTENT + create_post(content, + "permalink" => "pretty", + "source" => source_dir, + "destination" => dest_dir, + "read_posts" => true) + end + + should "not cause an error" do + refute_match(%r!markdown-html-error!, @result) + end + + should "have the URL to the 'complex' post from 2008-11-21" do + assert_match %r!1\s/2008/11/21/complex/!, @result + assert_match %r!2\s/2008/11/21/complex/!, @result + end + + should "have the URL to the 'nested' post from 2008-11-21" do + assert_match %r!3\s/2008/11/21/nested/!, @result + assert_match %r!4\s/2008/11/21/nested/!, @result + end + end + + context "simple page with nested post linking and path not used in `post_url`" do + setup do + content = <<~CONTENT + --- + title: Deprecated Post linking + --- + + - 1 {% post_url 2008-11-21-nested %} + CONTENT + create_post(content, + "permalink" => "pretty", + "source" => source_dir, + "destination" => dest_dir, + "read_posts" => true) + end + + should "not cause an error" do + refute_match(%r!markdown-html-error!, @result) + end + + should "have the url to the 'nested' post from 2008-11-21" do + assert_match %r!1\s/2008/11/21/nested/!, @result + end + + should "throw a deprecation warning" do + deprecation_warning = " Deprecation: A call to '{% post_url 2008-11-21-nested %}' " \ + "did not match a post using the new matching method of checking " \ + "name (path-date-slug) equality. Please make sure that you change " \ + "this tag to match the post's name exactly." + assert_includes Jekyll.logger.messages, deprecation_warning + end + end + + context "simple page with invalid post name linking" do + should "cause an error" do + content = <<~CONTENT + --- + title: Invalid post name linking + --- + + {% post_url abc2008-11-21-complex %} + CONTENT + + assert_raises Jekyll::Errors::PostURLError do + create_post(content, + "permalink" => "pretty", + "source" => source_dir, + "destination" => dest_dir, + "read_posts" => true) + end + end + + should "cause an error with a bad date" do + content = <<~CONTENT + --- + title: Invalid post name linking + --- + + {% post_url 2008-42-21-complex %} + CONTENT + + assert_raises Jekyll::Errors::InvalidDateError do + create_post(content, + "permalink" => "pretty", + "source" => source_dir, + "destination" => dest_dir, + "read_posts" => true) + end + end + end + + context "simple page with linking to a page" do + setup do + content = <<~CONTENT + --- + title: linking + --- + + {% link contacts.html %} + {% link info.md %} + {% link /css/screen.css %} + CONTENT + create_post(content, + "source" => source_dir, + "destination" => dest_dir, + "read_all" => true) + end + + should "not cause an error" do + refute_match(%r!markdown-html-error!, @result) + end + + should "have the URL to the 'contacts' item" do + assert_match(%r!/contacts\.html!, @result) + end + + should "have the URL to the 'info' item" do + assert_match(%r!/info\.html!, @result) + end + + should "have the URL to the 'screen.css' item" do + assert_match(%r!/css/screen\.css!, @result) + end + end + + context "simple page with dynamic linking to a page" do + setup do + content = <<~CONTENT + --- + title: linking + --- + + {% assign contacts_filename = 'contacts' %} + {% assign contacts_ext = 'html' %} + {% link {{contacts_filename}}.{{contacts_ext}} %} + {% assign info_path = 'info.md' %} + {% link {{\ info_path\ }} %} + {% assign screen_css_path = '/css' %} + {% link {{ screen_css_path }}/screen.css %} + CONTENT + create_post(content, + "source" => source_dir, + "destination" => dest_dir, + "read_all" => true) + end + + should "not cause an error" do + refute_match(%r!markdown-html-error!, @result) + end + + should "have the URL to the 'contacts' item" do + assert_match(%r!/contacts\.html!, @result) + end + + should "have the URL to the 'info' item" do + assert_match(%r!/info\.html!, @result) + end + + should "have the URL to the 'screen.css' item" do + assert_match(%r!/css/screen\.css!, @result) + end + end + + context "simple page with linking" do + setup do + content = <<~CONTENT + --- + title: linking + --- + + {% link _methods/yaml_with_dots.md %} + CONTENT + create_post(content, + "source" => source_dir, + "destination" => dest_dir, + "collections" => { "methods" => { "output" => true } }, + "read_collections" => true) + end + + should "not cause an error" do + refute_match(%r!markdown-html-error!, @result) + end + + should "have the URL to the 'yaml_with_dots' item" do + assert_match(%r!/methods/yaml_with_dots\.html!, @result) + end + end + + context "simple page with dynamic linking" do + setup do + content = <<~CONTENT + --- + title: linking + --- + + {% assign yaml_with_dots_path = '_methods/yaml_with_dots.md' %} + {% link {{yaml_with_dots_path}} %} + CONTENT + create_post(content, + "source" => source_dir, + "destination" => dest_dir, + "collections" => { "methods" => { "output" => true } }, + "read_collections" => true) + end + + should "not cause an error" do + refute_match(%r!markdown-html-error!, @result) + end + + should "have the URL to the 'yaml_with_dots' item" do + assert_match(%r!/methods/yaml_with_dots\.html!, @result) + end + end + + context "simple page with nested linking" do + setup do + content = <<~CONTENT + --- + title: linking + --- + + - 1 {% link _methods/sanitized_path.md %} + - 2 {% link _methods/site/generate.md %} + CONTENT + create_post(content, + "source" => source_dir, + "destination" => dest_dir, + "collections" => { "methods" => { "output" => true } }, + "read_collections" => true) + end + + should "not cause an error" do + refute_match(%r!markdown-html-error!, @result) + end + + should "have the URL to the 'sanitized_path' item" do + assert_match %r!1\s/methods/sanitized_path\.html!, @result + end + + should "have the URL to the 'site/generate' item" do + assert_match %r!2\s/methods/site/generate\.html!, @result + end + end + + context "simple page with invalid linking" do + should "cause an error" do + content = <<~CONTENT + --- + title: Invalid linking + --- + + {% link non-existent-collection-item %} + CONTENT + + assert_raises ArgumentError do + create_post(content, + "source" => source_dir, + "destination" => dest_dir, + "collections" => { "methods" => { "output" => true } }, + "read_collections" => true) + end + end + end + + context "simple page with invalid dynamic linking" do + should "cause an error" do + content = <<~CONTENT + --- + title: Invalid linking + --- + + {% assign non_existent_path = 'non-existent-collection-item' %} + {% link {{\ non_existent_path\ }} %} + CONTENT + + assert_raises ArgumentError do + create_post(content, + "source" => source_dir, + "destination" => dest_dir, + "collections" => { "methods" => { "output" => true } }, + "read_collections" => true) + end + end + end + + context "include tag with parameters" do + context "with symlink'd include" do + should "not allow symlink includes" do + File.write("tmp/pages-test", "SYMLINK TEST") + assert_raises IOError do + content = <<~CONTENT + --- + title: Include symlink + --- + + {% include tmp/pages-test %} + + CONTENT + create_post(content, + "permalink" => "pretty", + "source" => source_dir, + "destination" => dest_dir, + "read_posts" => true, + "safe" => true) + end + @result ||= "" + refute_match(%r!SYMLINK TEST!, @result) + end + + should "not expose the existence of symlinked files" do + ex = assert_raises IOError do + content = <<~CONTENT + --- + title: Include symlink + --- + + {% include tmp/pages-test-does-not-exist %} + + CONTENT + create_post(content, + "permalink" => "pretty", + "source" => source_dir, + "destination" => dest_dir, + "read_posts" => true, + "safe" => true) + end + assert_match( + "Could not locate the included file 'tmp/pages-test-does-not-exist' " \ + "in any of [\"#{source_dir}/_includes\"]. Ensure it exists in one of " \ + "those directories and is not a symlink as those are not allowed in " \ + "safe mode.", + ex.message + ) + end + end + + context "with one parameter" do + setup do + content = <<~CONTENT + --- + title: Include tag parameters + --- + + {% include sig.markdown myparam="test" %} + + {% include params.html param="value" %} + CONTENT + create_post(content, + "permalink" => "pretty", + "source" => source_dir, + "destination" => dest_dir, + "read_posts" => true) + end + + should "correctly output include variable" do + assert_match "<span id=\"include-param\">value</span>", @result.strip + end + + should "ignore parameters if unused" do + assert_match "<hr />\n<p>Tom Preston-Werner\ngithub.com/mojombo</p>\n", @result + end + end + + context "with simple syntax but multiline markup" do + setup do + content = <<~CONTENT + --- + title: Include tag parameters + --- + + {% include sig.markdown myparam="test" %} + + {% include params.html + param="value" %} + CONTENT + create_post(content, + "permalink" => "pretty", + "source" => source_dir, + "destination" => dest_dir, + "read_posts" => true) + end + + should "correctly output include variable" do + assert_match "<span id=\"include-param\">value</span>", @result.strip + end + + should "ignore parameters if unused" do + assert_match "<hr />\n<p>Tom Preston-Werner\ngithub.com/mojombo</p>\n", @result + end + end + + context "with variable syntax but multiline markup" do + setup do + content = <<~CONTENT + --- + title: Include tag parameters + --- + + {% include sig.markdown myparam="test" %} + {% assign path = "params" | append: ".html" %} + {% include {{ path }} + param="value" %} + CONTENT + create_post(content, + "permalink" => "pretty", + "source" => source_dir, + "destination" => dest_dir, + "read_posts" => true) + end + + should "correctly output include variable" do + assert_match "<span id=\"include-param\">value</span>", @result.strip + end + + should "ignore parameters if unused" do + assert_match "<hr />\n<p>Tom Preston-Werner\ngithub.com/mojombo</p>\n", @result + end + end + + context "with invalid parameter syntax" do + should "throw a ArgumentError" do + content = <<~CONTENT + --- + title: Invalid parameter syntax + --- + + {% include params.html param s="value" %} + CONTENT + assert_raises ArgumentError, "Did not raise exception on invalid " \ + '"include" syntax' do + create_post(content, + "permalink" => "pretty", + "source" => source_dir, + "destination" => dest_dir, + "read_posts" => true) + end + + content = <<~CONTENT + --- + title: Invalid parameter syntax + --- + + {% include params.html params="value %} + CONTENT + assert_raises ArgumentError, "Did not raise exception on invalid " \ + '"include" syntax' do + create_post(content, + "permalink" => "pretty", + "source" => source_dir, + "destination" => dest_dir, + "read_posts" => true) + end + end + end + + context "with several parameters" do + setup do + content = <<~CONTENT + --- + title: multiple include parameters + --- + + {% include params.html param1="new_value" param2="another" %} + CONTENT + create_post(content, + "permalink" => "pretty", + "source" => source_dir, + "destination" => dest_dir, + "read_posts" => true) + end + + should "list all parameters" do + assert_match "<li>param1 = new_value</li>", @result + assert_match "<li>param2 = another</li>", @result + end + + should "not include previously used parameters" do + assert_match "<span id=\"include-param\"></span>", @result + end + end + + context "without parameters" do + setup do + content = <<~CONTENT + --- + title: without parameters + --- + + {% include params.html %} + CONTENT + create_post(content, + "permalink" => "pretty", + "source" => source_dir, + "destination" => dest_dir, + "read_posts" => true) + end + + should "include file with empty parameters" do + assert_match "<span id=\"include-param\"></span>", @result + end + end + + context "with include file with special characters without params" do + setup do + content = <<~CONTENT + --- + title: special characters + --- + + {% include params@2.0.html %} + CONTENT + create_post(content, + "permalink" => "pretty", + "source" => source_dir, + "destination" => dest_dir, + "read_posts" => true) + end + + should "include file with empty parameters" do + assert_match "<span id=\"include-param\"></span>", @result + end + end + + context "with include file with special characters with params" do + setup do + content = <<~CONTENT + --- + title: special characters + --- + + {% include params@2.0.html param1="foobar" param2="bazbar" %} + CONTENT + create_post(content, + "permalink" => "pretty", + "source" => source_dir, + "destination" => dest_dir, + "read_posts" => true) + end + + should "include file with empty parameters" do + assert_match "<li>param1 = foobar</li>", @result + assert_match "<li>param2 = bazbar</li>", @result + end + end + + context "with custom includes directory" do + setup do + content = <<~CONTENT + --- + title: custom includes directory + --- + + {% include custom.html %} + CONTENT + create_post(content, + "includes_dir" => "_includes_custom", + "permalink" => "pretty", + "source" => source_dir, + "destination" => dest_dir, + "read_posts" => true) + end + + should "include file from custom directory" do + assert_match "custom_included", @result + end + end + + context "without parameters within if statement" do + setup do + content = <<~CONTENT + --- + title: without parameters within if statement + --- + + {% if true %}{% include params.html %}{% endif %} + CONTENT + create_post(content, + "permalink" => "pretty", + "source" => source_dir, + "destination" => dest_dir, + "read_posts" => true) + end + + should "include file with empty parameters within if statement" do + assert_match "<span id=\"include-param\"></span>", @result + end + end + + context "include missing file" do + setup do + @content = <<~CONTENT + --- + title: missing file + --- + + {% include missing.html %} + CONTENT + end + + should "raise error relative to source directory" do + exception = assert_raises IOError do + create_post(@content, + "permalink" => "pretty", + "source" => source_dir, + "destination" => dest_dir, + "read_posts" => true) + end + assert_match( + "Could not locate the included file 'missing.html' in any of " \ + "[\"#{source_dir}/_includes\"].", + exception.message + ) + end + end + + context "include tag with variable and liquid filters" do + setup do + site = fixture_site("pygments" => true).tap(&:read).tap(&:render) + post = site.posts.docs.find do |p| + p.basename.eql? "2013-12-17-include-variable-filters.markdown" + end + @content = post.output + end + + should "include file as variable with liquid filters" do + assert_match(%r!1 included!, @content) + assert_match(%r!2 included!, @content) + assert_match(%r!3 included!, @content) + end + + should "include file as variable and liquid filters with arbitrary whitespace" do + assert_match(%r!4 included!, @content) + assert_match(%r!5 included!, @content) + assert_match(%r!6 included!, @content) + end + + should "include file as variable and filters with additional parameters" do + assert_match("<li>var1 = foo</li>", @content) + assert_match("<li>var2 = bar</li>", @content) + end + + should "include file as partial variable" do + assert_match(%r!8 included!, @content) + end + end + end + + context "relative include tag with variable and liquid filters" do + setup do + site = fixture_site("pygments" => true).tap(&:read).tap(&:render) + post = site.posts.docs.find do |p| + p.basename.eql? "2014-09-02-relative-includes.markdown" + end + @content = post.output + end + + should "include file as variable with liquid filters" do + assert_match(%r!1 relative_include!, @content) + assert_match(%r!2 relative_include!, @content) + assert_match(%r!3 relative_include!, @content) + end + + should "include file as variable and liquid filters with arbitrary whitespace" do + assert_match(%r!4 relative_include!, @content) + assert_match(%r!5 relative_include!, @content) + assert_match(%r!6 relative_include!, @content) + end + + should "include file as variable and filters with additional parameters" do + assert_match("<li>var1 = foo</li>", @content) + assert_match("<li>var2 = bar</li>", @content) + end + + should "include file as partial variable" do + assert_match(%r!8 relative_include!, @content) + end + + should "include files relative to self" do + assert_match(%r!9 —\ntitle: Test Post Where YAML!, @content) + end + + context "trying to do bad stuff" do + context "include missing file" do + setup do + @content = <<~CONTENT + --- + title: missing file + --- + + {% include_relative missing.html %} + CONTENT + end + + should "raise error relative to source directory" do + exception = assert_raises IOError do + create_post(@content, + "permalink" => "pretty", + "source" => source_dir, + "destination" => dest_dir, + "read_posts" => true) + end + assert_match "Could not locate the included file 'missing.html' in any of " \ + "[\"#{source_dir}\"].", exception.message + end + end + + context "include existing file above you" do + setup do + @content = <<~CONTENT + --- + title: higher file + --- + + {% include_relative ../README.markdown %} + CONTENT + end + + should "raise error relative to source directory" do + exception = assert_raises ArgumentError do + create_post(@content, + "permalink" => "pretty", + "source" => source_dir, + "destination" => dest_dir, + "read_posts" => true) + end + assert_equal( + "Invalid syntax for include tag. File contains invalid characters or " \ + "sequences:\n\n ../README.markdown\n\nValid syntax:\n\n " \ + "{% include_relative file.ext param='value' param2='value' %}\n\n", + exception.message + ) + end + end + end + + context "with symlink'd include" do + should "not allow symlink includes" do + File.write("tmp/pages-test", "SYMLINK TEST") + assert_raises IOError do + content = <<~CONTENT + --- + title: Include symlink + --- + + {% include_relative tmp/pages-test %} + + CONTENT + create_post(content, + "permalink" => "pretty", + "source" => source_dir, + "destination" => dest_dir, + "read_posts" => true, + "safe" => true) + end + @result ||= "" + refute_match(%r!SYMLINK TEST!, @result) + end + + should "not expose the existence of symlinked files" do + ex = assert_raises IOError do + content = <<~CONTENT + --- + title: Include symlink + --- + + {% include_relative tmp/pages-test-does-not-exist %} + + CONTENT + create_post(content, + "permalink" => "pretty", + "source" => source_dir, + "destination" => dest_dir, + "read_posts" => true, + "safe" => true) + end + assert_match( + "Ensure it exists in one of those directories and is not a symlink " \ + "as those are not allowed in safe mode.", + ex.message + ) + end + end + end +end diff --git a/test/test_theme.rb b/test/test_theme.rb new file mode 100644 index 0000000..5604a1b --- /dev/null +++ b/test/test_theme.rb @@ -0,0 +1,85 @@ +# frozen_string_literal: true + +require "helper" + +class TestTheme < JekyllUnitTest + def setup + @theme = Theme.new("test-theme") + end + + context "initializing" do + should "normalize the theme name" do + theme = Theme.new(" Test-Theme ") + assert_equal "test-theme", theme.name + end + + should "know the theme root" do + assert_equal theme_dir, @theme.root + end + + should "know the theme version" do + assert_equal Gem::Version.new("0.1.0"), @theme.version + end + + should "raise an error for invalid themes" do + assert_raises Jekyll::Errors::MissingDependencyException do + Theme.new("foo").version + end + end + end + + context "path generation" do + [:assets, :_data, :_layouts, :_includes, :_sass].each do |folder| + should "know the #{folder} path" do + expected = theme_dir(folder.to_s) + assert_equal expected, @theme.public_send("#{folder.to_s.tr("_", "")}_path") + end + end + + should "generate folder paths" do + expected = theme_dir("_sass") + assert_equal expected, @theme.send(:path_for, :_sass) + end + + should "not allow paths outside of the theme root" do + assert_nil @theme.send(:path_for, "../../source") + end + + should "return nil for paths that don't exist" do + assert_nil @theme.send(:path_for, "foo") + end + + should "return the resolved path when a symlink & resolved path exists" do + # no support for symlinks on Windows + skip_if_windows "Jekyll does not currently support symlinks on Windows." + + expected = theme_dir("_layouts") + assert_equal expected, @theme.send(:path_for, :_symlink) + end + end + + context "invalid theme" do + context "initializing" do + setup do + stub_gemspec = Object.new + + # the directory for this theme should not exist + allow(stub_gemspec).to receive(:full_gem_path) + .and_return(File.expand_path("test/fixtures/test-non-existent-theme", __dir__)) + + allow(Gem::Specification).to receive(:find_by_name) + .with("test-non-existent-theme") + .and_return(stub_gemspec) + end + + should "raise when getting theme root" do + error = assert_raises(RuntimeError) { Theme.new("test-non-existent-theme") } + assert_match(%r!fixtures/test-non-existent-theme does not exist!, error.message) + end + end + end + + should "retrieve the gemspec" do + assert_equal "test-theme-0.1.0", @theme.send(:gemspec).full_name + end +end diff --git a/test/test_theme_assets_reader.rb b/test/test_theme_assets_reader.rb new file mode 100644 index 0000000..f45503e --- /dev/null +++ b/test/test_theme_assets_reader.rb @@ -0,0 +1,102 @@ +# frozen_string_literal: true + +require "helper" + +class TestThemeAssetsReader < JekyllUnitTest + def setup + @site = fixture_site( + "theme" => "test-theme", + "theme-color" => "black" + ) + assert @site.theme + end + + def assert_file_with_relative_path(haystack, relative_path) + assert haystack.any? { |f| + f.relative_path == relative_path + }, "Site should read in the #{relative_path} file, but it was not found in #{haystack.inspect}" + end + + def refute_file_with_relative_path(haystack, relative_path) + refute haystack.any? { |f| + f.relative_path == relative_path + }, "Site should not have read in the #{relative_path} file, but it was found in " \ + "#{haystack.inspect}" + end + + context "with a valid theme" do + should "read all assets" do + @site.reset + ThemeAssetsReader.new(@site).read + assert_file_with_relative_path @site.static_files, "/assets/img/logo.png" + assert_file_with_relative_path @site.pages, "assets/style.scss" + end + + should "convert pages" do + @site.process + + file = @site.pages.find { |f| f.relative_path == "assets/style.scss" } + refute_nil file + assert_equal @site.in_dest_dir("assets/style.css"), file.destination(@site.dest) + assert_includes file.output, ".sample { color: black; }" + end + + should "not overwrite site content with the same relative path" do + @site.reset + @site.read + + file = @site.pages.find { |f| f.relative_path == "assets/application.coffee" } + static_script = File.read( + @site.static_files.find { |f| f.relative_path == "/assets/base.js" }.path + ) + refute_nil file + refute_nil static_script + assert_includes file.content, "alert \"From your site.\"" + assert_includes static_script, "alert(\"From your site.\");" + end + end + + context "with a valid theme without an assets dir" do + should "not read any assets" do + site = fixture_site("theme" => "test-theme") + allow(site.theme).to receive(:assets_path).and_return(nil) + ThemeAssetsReader.new(site).read + refute_file_with_relative_path site.static_files, "/assets/img/logo.png" + refute_file_with_relative_path site.pages, "assets/style.scss" + end + end + + context "with no theme" do + should "not read any assets" do + site = fixture_site("theme" => nil) + ThemeAssetsReader.new(site).read + refute_file_with_relative_path site.static_files, "/assets/img/logo.png" + refute_file_with_relative_path site.pages, "assets/style.scss" + end + end + + context "symlinked theme" do + should "not read assets from symlinked theme" do + skip_if_windows "Jekyll does not currently support symlinks on Windows." + + begin + tmp_dir = Dir.mktmpdir("jekyll-theme-test") + File.binwrite(File.join(tmp_dir, "test.txt"), "content") + + theme_dir = File.join(__dir__, "fixtures", "test-theme-symlink") + File.symlink(tmp_dir, File.join(theme_dir, "assets")) + + site = fixture_site( + "theme" => "test-theme-symlink", + "theme-color" => "black" + ) + ThemeAssetsReader.new(site).read + + assert_empty site.static_files, "static file should not have been picked up" + ensure + FileUtils.rm_rf(tmp_dir) + FileUtils.rm_rf(File.join(theme_dir, "assets")) + end + end + end +end diff --git a/test/test_theme_data_reader.rb b/test/test_theme_data_reader.rb new file mode 100644 index 0000000..1580269 --- /dev/null +++ b/test/test_theme_data_reader.rb @@ -0,0 +1,90 @@ +# frozen_string_literal: true + +require "helper" + +class TestThemeDataReader < JekyllUnitTest + context "site without a theme" do + setup do + @site = fixture_site("theme" => nil) + @site.reader.read_data + assert @site.data["greetings"] + assert @site.data["categories"]["dairy"] + end + + should "should read data from source" do + assert_equal "Hello! I’m foo. And who are you?", @site.data["greetings"]["foo"] + assert_equal "Dairy", @site.data["categories"]["dairy"]["name"] + end + end + + context "site with a theme without _data" do + setup do + @site = fixture_site("theme" => "test-theme-skinny") + @site.reader.read_data + assert @site.data["greetings"] + assert @site.data["categories"]["dairy"] + end + + should "should read data from source" do + assert_equal "Hello! I’m foo. And who are you?", @site.data["greetings"]["foo"] + assert_equal "Dairy", @site.data["categories"]["dairy"]["name"] + end + end + + context "site with a theme with empty _data directory" do + setup do + @site = fixture_site("theme" => "test-theme-w-empty-data") + @site.reader.read_data + assert @site.data["greetings"] + assert @site.data["categories"]["dairy"] + end + + should "should read data from source" do + assert_equal "Hello! I’m foo. And who are you?", @site.data["greetings"]["foo"] + assert_equal "Dairy", @site.data["categories"]["dairy"]["name"] + end + end + + context "site with a theme with data at root of _data" do + setup do + @site = fixture_site("theme" => "test-theme") + @site.reader.read_data + assert @site.data["greetings"] + assert @site.data["categories"]["dairy"] + assert @site.data["cars"] + end + + should "should merge nested keys" do + refute_equal "Hello! I’m bar. What’s up so far?", @site.data["greetings"]["foo"] + assert_equal "Hello! I’m foo. And who are you?", @site.data["greetings"]["foo"] + assert_equal "Mercedes", @site.data["cars"]["manufacturer"] + end + end + + context "site with a theme with data at root of _data and in a subdirectory" do + setup do + @site = fixture_site("theme" => "test-theme") + @site.reader.read_data + assert @site.data["greetings"] + assert @site.data["categories"]["dairy"] + assert @site.data["cars"] + end + + should "should merge nested keys" do + refute_equal "Cheese Dairy", @site.data["categories"]["dairy"]["name"] + expected_names = %w(cheese milk) + product_names = @site.data["categories"]["dairy"]["products"].map do |product| + product["name"] + end + expected_names.each do |expected_name| + assert_includes product_names, expected_name + end + assert_equal "Dairy", @site.data["categories"]["dairy"]["name"] + end + + should "should illustrate the documented sample" do + assert_equal "Kundenstimmen", @site.data["i18n"]["testimonials"]["header"] + assert_equal "Design by FTC", @site.data["i18n"]["testimonials"]["footer"] + end + end +end diff --git a/test/test_theme_drop.rb b/test/test_theme_drop.rb new file mode 100644 index 0000000..6ebbcf8 --- /dev/null +++ b/test/test_theme_drop.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +require "helper" + +class TestThemeDrop < JekyllUnitTest + should "be initialized only for gem-based themes" do + assert_nil fixture_site.to_liquid.theme + end + + context "a theme drop" do + setup do + @drop = fixture_site("theme" => "test-theme").to_liquid.theme + end + + should "respond to `key?`" do + assert_respond_to @drop, :key? + end + + should "export relevant data to Liquid templates" do + expected = { + "authors" => "Jekyll", + "dependencies" => [], + "description" => "This is a theme used to test Jekyll", + "metadata" => {}, + "root" => theme_dir, + "version" => "0.1.0", + } + expected.each_key do |key| + assert @drop.key?(key) + assert_equal expected[key], @drop[key] + end + end + end +end diff --git a/test/test_url.rb b/test/test_url.rb new file mode 100644 index 0000000..859babd --- /dev/null +++ b/test/test_url.rb @@ -0,0 +1,84 @@ +# frozen_string_literal: true + +require "helper" + +class TestURL < JekyllUnitTest + context "The URL class" do + should "throw an exception if neither permalink or template is specified" do + assert_raises ArgumentError do + URL.new(:placeholders => {}) + end + end + + should "replace placeholders in templates" do + assert_equal "/foo/bar", URL.new( + :template => "/:x/:y", + :placeholders => { :x => "foo", :y => "bar" } + ).to_s + end + + should "handle multiple of the same key in the template" do + assert_equal "/foo/bar/foo/", URL.new( + :template => "/:x/:y/:x/", + :placeholders => { :x => "foo", :y => "bar" } + ).to_s + end + + should "use permalink if given" do + assert_equal "/le/perma/link", URL.new( + :template => "/:x/:y", + :placeholders => { :x => "foo", :y => "bar" }, + :permalink => "/le/perma/link" + ).to_s + end + + should "replace placeholders in permalinks" do + assert_equal "/foo/bar", URL.new( + :template => "/baz", + :permalink => "/:x/:y", + :placeholders => { :x => "foo", :y => "bar" } + ).to_s + end + + should "handle multiple of the same key in the permalink" do + assert_equal "/foo/bar/foo/", URL.new( + :template => "/baz", + :permalink => "/:x/:y/:x/", + :placeholders => { :x => "foo", :y => "bar" } + ).to_s + end + + should "handle nil values for keys in the template" do + assert_equal "/foo/bar/", URL.new( + :template => "/:x/:y/:z/", + :placeholders => { :x => "foo", :y => "bar", :z => nil } + ).to_s + end + + should "handle UrlDrop as a placeholder in addition to a hash" do + _, matching_doc = fixture_document("_methods/escape-+ #%20[].md") + assert_equal "/methods/escape-+-20/escape-20.html", URL.new( + :template => "/methods/:title/:name:output_ext", + :placeholders => matching_doc.url_placeholders + ).to_s + end + + should "check for key without trailing underscore" do + _, matching_doc = fixture_document("_methods/configuration.md") + assert_equal "/methods/configuration-configuration_methods_configuration", URL.new( + :template => "/methods/:name-:slug_:collection_:title", + :placeholders => matching_doc.url_placeholders + ).to_s + end + + should "raise custom error when URL placeholder doesn't have key" do + _, matching_doc = fixture_document("_methods/escape-+ #%20[].md") + assert_raises NoMethodError do + URL.new( + :template => "/methods/:headline", + :placeholders => matching_doc.url_placeholders + ).to_s + end + end + end +end diff --git a/test/test_utils.rb b/test/test_utils.rb new file mode 100644 index 0000000..7f230c3 --- /dev/null +++ b/test/test_utils.rb @@ -0,0 +1,438 @@ +# frozen_string_literal: true + +require "helper" + +class TestUtils < JekyllUnitTest + context "The \`Utils.deep_merge_hashes\` method" do + setup do + clear_dest + @site = fixture_site + @site.process + end + + should "merge a drop into a hash" do + data = { "page" => {} } + merged = Utils.deep_merge_hashes(data, @site.site_payload) + assert merged.is_a? Hash + assert merged["site"].is_a? Drops::SiteDrop + assert_equal data["page"], merged["page"] + end + + should "merge a hash into a drop" do + data = { "page" => {} } + assert_nil @site.site_payload["page"] + merged = Utils.deep_merge_hashes(@site.site_payload, data) + assert merged.is_a? Drops::UnifiedPayloadDrop + assert merged["site"].is_a? Drops::SiteDrop + assert_equal data["page"], merged["page"] + end + end + + context "hash" do + context "pluralized_array" do + should "return empty array with no values" do + data = {} + assert_equal [], Utils.pluralized_array_from_hash(data, "tag", "tags") + end + + should "return empty array with no matching values" do + data = { "foo" => "bar" } + assert_equal [], Utils.pluralized_array_from_hash(data, "tag", "tags") + end + + should "return plural array with nil singular" do + data = { "foo" => "bar", "tag" => nil, "tags" => %w(dog cat) } + assert_equal %w(dog cat), Utils.pluralized_array_from_hash(data, "tag", "tags") + end + + should "return single value array with matching singular" do + data = { "foo" => "bar", "tag" => "dog", "tags" => %w(dog cat) } + assert_equal ["dog"], Utils.pluralized_array_from_hash(data, "tag", "tags") + end + + should "return single value array with matching singular with spaces" do + data = { "foo" => "bar", "tag" => "dog cat", "tags" => %w(dog cat) } + assert_equal ["dog cat"], Utils.pluralized_array_from_hash(data, "tag", "tags") + end + + should "return empty array with matching nil plural" do + data = { "foo" => "bar", "tags" => nil } + assert_equal [], Utils.pluralized_array_from_hash(data, "tag", "tags") + end + + should "return empty array with matching empty array" do + data = { "foo" => "bar", "tags" => [] } + assert_equal [], Utils.pluralized_array_from_hash(data, "tag", "tags") + end + + should "return single value array with matching plural with single string value" do + data = { "foo" => "bar", "tags" => "dog" } + assert_equal ["dog"], Utils.pluralized_array_from_hash(data, "tag", "tags") + end + + should "return multiple value array with matching plural with " \ + "single string value with spaces" do + data = { "foo" => "bar", "tags" => "dog cat" } + assert_equal %w(dog cat), Utils.pluralized_array_from_hash(data, "tag", "tags") + end + + should "return single value array with matching plural with single value array" do + data = { "foo" => "bar", "tags" => ["dog"] } + assert_equal ["dog"], Utils.pluralized_array_from_hash(data, "tag", "tags") + end + + should "return multiple value array with matching plural with " \ + "multiple value array" do + data = { "foo" => "bar", "tags" => %w(dog cat) } + assert_equal %w(dog cat), Utils.pluralized_array_from_hash(data, "tag", "tags") + end + end + end + + context "The \`Utils.parse_date\` method" do + should "parse a properly formatted date" do + assert Utils.parse_date("2014-08-02 14:43:06 PDT").is_a? Time + end + + should "throw an error if the input contains no date data" do + assert_raises Jekyll::Errors::InvalidDateError do + Utils.parse_date("Blah") + end + end + + should "throw an error if the input is out of range" do + assert_raises Jekyll::Errors::InvalidDateError do + Utils.parse_date("9999-99-99") + end + end + + should "throw an error with the default message if no message is passed in" do + date = "Blah this is invalid" + assert_raises( + Jekyll::Errors::InvalidDateError, + "Invalid date '#{date}': Input could not be parsed." + ) do + Utils.parse_date(date) + end + end + + should "throw an error with the provided message if a message is passed in" do + date = "Blah this is invalid" + message = "Aaaah, the world has exploded!" + assert_raises( + Jekyll::Errors::InvalidDateError, + "Invalid date '#{date}': #{message}" + ) do + Utils.parse_date(date, message) + end + end + end + + context "The \`Utils.slugify\` method" do + should "return nil if passed nil" do + assert_nil Utils.slugify(nil) + rescue NoMethodError + assert false, "Threw NoMethodError" + end + + should "replace whitespace with hyphens" do + assert_equal "working-with-drafts", Utils.slugify("Working with drafts") + end + + should "replace consecutive whitespace with a single hyphen" do + assert_equal "basic-usage", Utils.slugify("Basic Usage") + end + + should "trim leading and trailing whitespace" do + assert_equal "working-with-drafts", Utils.slugify(" Working with drafts ") + end + + should "drop trailing punctuation" do + assert_equal( + "so-what-is-jekyll-exactly", + Utils.slugify("So what is Jekyll, exactly?") + ) + assert_equal "كيف-حالك", Utils.slugify("كيف حالك؟") + end + + should "ignore hyphens" do + assert_equal "pre-releases", Utils.slugify("Pre-releases") + end + + should "replace underscores with hyphens" do + assert_equal "the-config-yml-file", Utils.slugify("The _config.yml file") + end + + should "combine adjacent hyphens and spaces" do + assert_equal( + "customizing-git-git-hooks", + Utils.slugify("Customizing Git - Git Hooks") + ) + end + + should "replace punctuation in any scripts by hyphens" do + assert_equal "5時-6時-三-一四", Utils.slugify("5時〜6時 三・一四") + end + + should "not replace Unicode 'Mark', 'Letter', or 'Number: Decimal Digit' category characters" do + assert_equal "மல்லிப்பூ-வகைகள்", Utils.slugify("மல்லிப்பூ வகைகள்") + assert_equal "மல்லிப்பூ-வகைகள்", Utils.slugify("மல்லிப்பூ வகைகள்", :mode => "pretty") + end + + should "not modify the original string" do + title = "Quick-start guide" + Utils.slugify(title) + assert_equal "Quick-start guide", title + end + + should "not change behaviour if mode is default" do + assert_equal( + "the-config-yml-file", + Utils.slugify("The _config.yml file?", :mode => "default") + ) + end + + should "not change behaviour if mode is nil" do + assert_equal "the-config-yml-file", Utils.slugify("The _config.yml file?") + end + + should "not replace period and underscore if mode is pretty" do + assert_equal( + "the-_config.yml-file", + Utils.slugify("The _config.yml file?", :mode => "pretty") + ) + end + + should "replace everything else but ASCII characters" do + assert_equal "the-config-yml-file", + Utils.slugify("The _config.yml file?", :mode => "ascii") + assert_equal "f-rtive-glance", + Utils.slugify("fürtive glance!!!!", :mode => "ascii") + end + + should "map accented latin characters to ASCII characters" do + assert_equal "the-config-yml-file", + Utils.slugify("The _config.yml file?", :mode => "latin") + assert_equal "furtive-glance", + Utils.slugify("fürtive glance!!!!", :mode => "latin") + assert_equal "aaceeiioouu", + Utils.slugify("àáçèéíïòóúü", :mode => "latin") + assert_equal "a-z", + Utils.slugify("Aあわれ鬱господинZ", :mode => "latin") + end + + should "only replace whitespace if mode is raw" do + assert_equal( + "the-_config.yml-file?", + Utils.slugify("The _config.yml file?", :mode => "raw") + ) + end + + should "return the given string if mode is none" do + assert_equal( + "the _config.yml file?", + Utils.slugify("The _config.yml file?", :mode => "none") + ) + end + + should "Keep all uppercase letters if cased is true" do + assert_equal( + "Working-with-drafts", + Utils.slugify("Working with drafts", :cased => true) + ) + assert_equal( + "Basic-Usage", + Utils.slugify("Basic Usage", :cased => true) + ) + assert_equal( + "Working-with-drafts", + Utils.slugify(" Working with drafts ", :cased => true) + ) + assert_equal( + "So-what-is-Jekyll-exactly", + Utils.slugify("So what is Jekyll, exactly?", :cased => true) + ) + assert_equal( + "Pre-releases", + Utils.slugify("Pre-releases", :cased => true) + ) + assert_equal( + "The-config-yml-file", + Utils.slugify("The _config.yml file", :cased => true) + ) + assert_equal( + "Customizing-Git-Git-Hooks", + Utils.slugify("Customizing Git - Git Hooks", :cased => true) + ) + assert_equal( + "The-config-yml-file", + Utils.slugify("The _config.yml file?", :mode => "default", :cased => true) + ) + assert_equal( + "The-config-yml-file", + Utils.slugify("The _config.yml file?", :cased => true) + ) + assert_equal( + "The-_config.yml-file", + Utils.slugify("The _config.yml file?", :mode => "pretty", :cased => true) + ) + assert_equal( + "The-_config.yml-file?", + Utils.slugify("The _config.yml file?", :mode => "raw", :cased => true) + ) + assert_equal( + "The _config.yml file?", + Utils.slugify("The _config.yml file?", :mode => "none", :cased => true) + ) + end + + should "records a warning in the log if the returned slug is empty" do + expect(Jekyll.logger).to receive(:warn) + assert_equal "", Utils.slugify("💎") + end + end + + context "The \`Utils.titleize_slug\` method" do + should "capitalize all words and not drop any words" do + assert_equal( + "This Is A Long Title With Mixed Capitalization", + Utils.titleize_slug("This-is-a-Long-title-with-Mixed-capitalization") + ) + assert_equal( + "This Is A Title With Just The Initial Word Capitalized", + Utils.titleize_slug("This-is-a-title-with-just-the-initial-word-capitalized") + ) + assert_equal( + "This Is A Title With No Capitalization", + Utils.titleize_slug("this-is-a-title-with-no-capitalization") + ) + end + end + + context "The \`Utils.add_permalink_suffix\` method" do + should "handle built-in permalink styles" do + assert_equal( + "/:basename/", + Utils.add_permalink_suffix("/:basename", :pretty) + ) + assert_equal( + "/:basename:output_ext", + Utils.add_permalink_suffix("/:basename", :date) + ) + assert_equal( + "/:basename:output_ext", + Utils.add_permalink_suffix("/:basename", :ordinal) + ) + assert_equal( + "/:basename:output_ext", + Utils.add_permalink_suffix("/:basename", :none) + ) + end + + should "handle custom permalink styles" do + assert_equal( + "/:basename/", + Utils.add_permalink_suffix("/:basename", "/:title/") + ) + assert_equal( + "/:basename:output_ext", + Utils.add_permalink_suffix("/:basename", "/:title:output_ext") + ) + assert_equal( + "/:basename", + Utils.add_permalink_suffix("/:basename", "/:title") + ) + end + end + + context "The \`Utils.safe_glob\` method" do + should "not apply pattern to the dir" do + dir = "test/safe_glob_test[" + assert_equal [], Dir.glob(dir + "/*") unless jruby? + assert_equal ["test/safe_glob_test[/find_me.txt"], Utils.safe_glob(dir, "*") + end + + should "return the same data to #glob" do + dir = "test" + assert_equal Dir.glob(dir + "/*"), Utils.safe_glob(dir, "*") + assert_equal Dir.glob(dir + "/**/*"), Utils.safe_glob(dir, "**/*") + end + + should "return the same data to #glob if dir is not found" do + dir = "dir_not_exist" + assert_equal [], Utils.safe_glob(dir, "*") + assert_equal Dir.glob(dir + "/*"), Utils.safe_glob(dir, "*") + end + + should "return the same data to #glob if pattern is blank" do + dir = "test" + assert_equal [dir], Utils.safe_glob(dir, "") + assert_equal Dir.glob(dir), Utils.safe_glob(dir, "") + assert_equal Dir.glob(dir), Utils.safe_glob(dir, nil) + end + + should "return the same data to #glob if flag is given" do + dir = "test" + assert_equal Dir.glob(dir + "/*", File::FNM_DOTMATCH), + Utils.safe_glob(dir, "*", File::FNM_DOTMATCH) + end + + should "support pattern as an array to support windows" do + dir = "test" + assert_equal Dir.glob(dir + "/**/*"), Utils.safe_glob(dir, ["**", "*"]) + end + end + + context "The \`Utils.has_yaml_header?\` method" do + should "accept files with YAML front matter" do + file = source_dir("_posts", "2008-10-18-foo-bar.markdown") + assert_equal "---\n", File.open(file, "rb") { |f| f.read(4) } + assert Utils.has_yaml_header?(file) + end + should "accept files with extraneous spaces after YAML front matter" do + file = source_dir("_posts", "2015-12-27-extra-spaces.markdown") + assert_equal "--- \n", File.open(file, "rb") { |f| f.read(6) } + assert Utils.has_yaml_header?(file) + end + should "reject pgp files and the like which resemble front matter" do + file = source_dir("pgp.key") + assert_equal "-----B", File.open(file, "rb") { |f| f.read(6) } + refute Utils.has_yaml_header?(file) + end + end + + context "The \`Utils.merged_file_read_opts\` method" do + should "ignore encoding if it's not there" do + opts = Utils.merged_file_read_opts(nil, {}) + assert_nil opts["encoding"] + assert_nil opts[:encoding] + end + + should "add bom to utf-encoding" do + opts = { "encoding" => "utf-8", :encoding => "utf-8" } + merged = Utils.merged_file_read_opts(nil, opts) + assert_equal "bom|utf-8", merged["encoding"] + assert_equal "bom|utf-8", merged[:encoding] + end + + should "not add bom to non-utf encoding" do + opts = { "encoding" => "ISO-8859-1", :encoding => "ISO-8859-1" } + merged = Utils.merged_file_read_opts(nil, opts) + assert_equal "ISO-8859-1", merged["encoding"] + assert_equal "ISO-8859-1", merged[:encoding] + end + + should "preserve bom in encoding" do + opts = { "encoding" => "bom|another", :encoding => "bom|another" } + merged = Utils.merged_file_read_opts(nil, opts) + assert_equal "bom|another", merged["encoding"] + assert_equal "bom|another", merged[:encoding] + end + end + + context "Utils::Internet.connected?" do + should "return true if there's internet" do + assert Utils::Internet.connected? + end + end +end diff --git a/test/test_win_tz.rb b/test/test_win_tz.rb new file mode 100644 index 0000000..5e7eb89 --- /dev/null +++ b/test/test_win_tz.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +require "helper" + +class TestWinTz < JekyllUnitTest + [["America/New_York", "WTZ+05:00"], ["Europe/Paris", "WTZ-01:00"]].each do |tz, expected| + should "use base offset in winter for #{tz}" do + result = Jekyll::Utils::WinTZ.calculate(tz, Time.utc(2021, 1, 1)) + assert_equal expected, result + end + end + + [["America/New_York", "WTZ+04:00"], ["Europe/Paris", "WTZ-02:00"]].each do |tz, expected| + should "apply DST in summer for #{tz}" do + result = Jekyll::Utils::WinTZ.calculate(tz, Time.utc(2021, 7, 1)) + assert_equal expected, result + end + end + + [["Australia/Eucla", "WTZ-08:45"], ["Pacific/Marquesas", "WTZ+09:30"]].each do |tz, expected| + should "handle non zero minutes for #{tz}" do + result = Jekyll::Utils::WinTZ.calculate(tz, Time.utc(2021, 1, 1)) + assert_equal expected, result + end + end + + should "return zero for UTC" do + result = Jekyll::Utils::WinTZ.calculate("UTC") + assert_equal "WTZ+00:00", result + end +end -- GitLab