Codebase list jekyll / 9c87206
New upstream version 4.3.1+dfsg Antonio Terceiro 1 year, 3 months ago
561 changed file(s) with 23055 addition(s) and 11226 deletion(s). Raw diff Collapse all Expand all
0 engines:
0 version: "2"
1 checks:
2 argument-count:
3 enabled: true
4 config:
5 threshold: 5
6 file-lines:
7 enabled: true
8 config:
9 threshold: 300
10 method-complexity:
11 enabled: true
12 config:
13 threshold: 15
14 method-count:
15 enabled: true
16 config:
17 threshold: 50
18 method-lines:
19 enabled: true
20 config:
21 threshold: 30
22 plugins:
123 fixme:
224 enabled: false
325 rubocop:
426 enabled: true
5 channel: rubocop-0-54
27 channel: rubocop-0-60
628
7 exclude_paths:
8 - .codeclimate.yml
9 - .gitignore
10 - .rspec
11 - .rubocop.yml
12 - .travis.yml
29 exclude_patterns:
30 - "*.*"
31 - ".*"
1332
14 - Gemfile.lock
15 - CHANGELOG.{md,markdown,txt,textile}
16 - CONTRIBUTING.{md,markdown,txt,textile}
17 - readme.{md,markdown,txt,textile}
18 - README.{md,markdown,txt,textile}
19 - Readme.{md,markdown,txt,textile}
20 - ReadMe.{md,markdown,txt,textile}
21 - COPYING
33 - Gemfile
2234 - LICENSE
35 - Rakefile
2336
24 - features/**/*
25 - script/**/*
26 - docs/**/*
27 - spec/**/*
28 - test/**/*
29 - vendor/**/*
37 - benchmark/
38 - docs/
39 - exe/
40 - features/
41 - rake/
42 - rubocop/
43 - script/
44 - spec/
45 - test/
46 - vendor/
3047
48 - lib/blank_template/
49 - lib/site_template/
50 - lib/theme_template/
51 - lib/jekyll/mime.types
3152 - lib/jekyll/commands/serve/livereload_assets/livereload.js
32
33 ratings:
34 paths:
35 - lib/**/*.rb
0 #-------------------------------------------------------------------------------------------------------------
1 # Copyright (c) Microsoft Corporation. All rights reserved.
2 # Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information.
3 #-------------------------------------------------------------------------------------------------------------
4
5 FROM ruby:2
6
7 # Avoid warnings by switching to noninteractive
8 ENV DEBIAN_FRONTEND=noninteractive
9
10 # This Dockerfile adds a non-root user with sudo access. Use the "remoteUser"
11 # property in devcontainer.json to use it. On Linux, the container user's GID/UIDs
12 # will be updated to match your local UID/GID (when using the dockerFile property).
13 # See https://aka.ms/vscode-remote/containers/non-root-user for details.
14 ARG USERNAME=vscode
15 ARG USER_UID=1000
16 ARG USER_GID=$USER_UID
17
18 # Configure apt and install packages
19 RUN apt-get update \
20 && apt-get -y install --no-install-recommends apt-utils dialog locales 2>&1 \
21 # Verify git, process tools installed
22 && apt-get -y install git openssh-client iproute2 procps lsb-release \
23 #
24 # Install ruby-debug-ide and debase
25 && gem install ruby-debug-ide \
26 && gem install debase \
27 #
28 # Install node.js
29 && apt-get -y install curl software-properties-common \
30 && curl -sL https://deb.nodesource.com/setup_13.x | bash - \
31 && apt-get -y install nodejs \
32 #
33 # Create a non-root user to use if preferred - see https://aka.ms/vscode-remote/containers/non-root-user.
34 && groupadd --gid $USER_GID $USERNAME \
35 && useradd -s /bin/bash --uid $USER_UID --gid $USER_GID -m $USERNAME \
36 # [Optional] Add sudo support for the non-root user
37 && apt-get install -y sudo \
38 && echo $USERNAME ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/$USERNAME\
39 && chmod 0440 /etc/sudoers.d/$USERNAME \
40 #
41 # Clean up
42 && apt-get autoremove -y \
43 && apt-get clean -y \
44 && rm -rf /var/lib/apt/lists/*
45
46 # Set the locale
47 RUN sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen && \
48 dpkg-reconfigure --frontend=noninteractive locales && \
49 update-locale LANG=en_US.UTF-8
50
51 ENV LANG en_US.UTF-8
52
53 # Switch back to dialog for any ad-hoc use of apt-get
54 ENV DEBIAN_FRONTEND=dialog
0 // For format details, see https://aka.ms/vscode-remote/devcontainer.json or this file's README at:
1 // https://github.com/microsoft/vscode-dev-containers/tree/v0.101.1/containers/ruby-2
2 {
3 "name": "Ruby 2",
4 "dockerFile": "Dockerfile",
5
6 // Set *default* container specific settings.json values on container create.
7 "settings": {
8 "terminal.integrated.shell.linux": "/bin/bash"
9 },
10
11 // Add the IDs of extensions you want installed when the container is created.
12 "extensions": [
13 "rebornix.Ruby"
14 ]
15
16 // Use 'forwardPorts' to make a list of ports inside the container available locally.
17 // "forwardPorts": [],
18
19 // Use 'postCreateCommand' to run commands after the container is created.
20 "postCreateCommand": "bundle install",
21
22 // Uncomment to connect as a non-root user. See https://aka.ms/vscode-remote/containers/non-root.
23 // "remoteUser": "vscode"
24
25 }
0 * text=auto
0 # Code of Conduct
1
2 ## Our Pledge
3
4 In the interest of fostering an open and welcoming environment, we as
5 contributors and maintainers pledge to making participation in our project and
6 our community a harassment-free experience for everyone, regardless of age, body
7 size, disability, ethnicity, sex characteristics, gender identity and expression,
8 level of experience, education, socio-economic status, nationality, personal
9 appearance, race, religion, or sexual identity and orientation.
10
11 ## Our Standards
12
13 Examples of behavior that contributes to creating a positive environment
14 include:
15
16 * Using welcoming and inclusive language
17 * Being respectful of differing viewpoints and experiences
18 * Gracefully accepting constructive criticism
19 * Focusing on what is best for the community
20 * Showing empathy towards other community members
21
22 Examples of unacceptable behavior by participants include:
23
24 * The use of sexualized language or imagery and unwelcome sexual attention or
25 advances
26 * Trolling, insulting/derogatory comments, and personal or political attacks
27 * Public or private harassment
28 * Publishing others' private information, such as a physical or electronic
29 address, without explicit permission
30 * Other conduct which could reasonably be considered inappropriate in a
31 professional setting
32
33 ## Our Responsibilities
34
35 Project maintainers are responsible for clarifying the standards of acceptable
36 behavior and are expected to take appropriate and fair corrective action in
37 response to any instances of unacceptable behavior.
38
39 Project maintainers have the right and responsibility to remove, edit, or
40 reject comments, commits, code, wiki edits, issues, and other contributions
41 that are not aligned to this Code of Conduct, or to ban temporarily or
42 permanently any contributor for other behaviors that they deem inappropriate,
43 threatening, offensive, or harmful.
44
45 ## Scope
46
47 This Code of Conduct applies both within project spaces and in public spaces
48 when an individual is representing the project or its community. Examples of
49 representing a project or community include using an official project e-mail
50 address, posting via an official social media account, or acting as an appointed
51 representative at an online or offline event. Representation of a project may be
52 further defined and clarified by project maintainers.
53
54 ## Enforcement
55
56 Instances of abusive, harassing, or otherwise unacceptable behavior may be
57 reported by contacting the project team at [olivia@jekyllrb.com](mailto:olivia@jekyllrb.com). All
58 complaints will be reviewed and investigated and will result in a response that
59 is deemed necessary and appropriate to the circumstances. The project team is
60 obligated to maintain confidentiality with regard to the reporter of an incident.
61 Further details of specific enforcement policies may be posted separately.
62
63 Project maintainers who do not follow or enforce the Code of Conduct in good
64 faith may face temporary or permanent repercussions as determined by other
65 members of the project's leadership.
66
67 ## Attribution
68
69 This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
70 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)
71
72 [homepage]: https://www.contributor-covenant.org
73
74 For answers to common questions about this code of conduct, see
75 [https://www.contributor-covenant.org/faq](https://www.contributor-covenant.org/faq)
33
44 ## Where to get help or report a problem
55
6 See [the support guidelines](https://jekyllrb.com/docs/support/)
6 See the [support guidelines](https://jekyllrb.com/docs/support/)
77
88 ## Ways to contribute
99
1010 Whether you're a developer, a designer, or just a Jekyll devotee, there are lots of ways to contribute. Here's a few ideas:
1111
12 * [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.
13 * 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?
14 * 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.
15 * 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.
16 * 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.
17 * Help evaluate [open pull requests](https://github.com/jekyll/jekyll/pulls), by testing the changes locally and reviewing what's proposed.
12 - [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.
13 - 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?
14 - 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.
15 - 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.
16 - 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.
17 - Help evaluate [open pull requests](https://github.com/jekyll/jekyll/pulls), by testing the changes locally and reviewing what's proposed.
1818
1919 ## Submitting a pull request
2020
2121 ### Pull requests generally
2222
23 * The smaller the proposed change, the better. If you'd like to propose two unrelated changes, submit two pull requests.
23 - The smaller the proposed change, the better. If you'd like to propose two unrelated changes, submit two pull requests.
2424
25 * 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.
25 - 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.
2626
27 * Pull requests are easy and fun. If this is your first pull request, it may help to [understand GitHub Flow](https://guides.github.com/introduction/flow/).
27 - If this is your first pull request, it may help to [understand GitHub Flow](https://guides.github.com/introduction/flow/).
2828
29 * If you're submitting a code contribution, be sure to read the [code contributions](#code-contributions) section below.
29 - If you're submitting a code contribution, be sure to read the [code contributions](#code-contributions) section below.
3030
3131 ### Submitting a pull request via github.com
3232
4848 2. Clone the repository locally `git clone https://github.com/<you-username>/jekyll`.
4949 3. Create a new, descriptively named branch to contain your change ( `git checkout -b my-awesome-feature` ).
5050 4. Hack away, add tests. Not necessarily in that order.
51 5. Make sure everything still passes by running `script/cibuild` (see [the tests section](#running-tests-locally) below)
51 5. Make sure everything still passes by running `script/cibuild` (see the [tests section](#running-tests-locally) below)
5252 6. Push the branch up ( `git push origin my-awesome-feature` ).
5353 7. Create a pull request by visiting `https://github.com/<your-username>/jekyll` and following the instructions at the top of the screen.
5454
7575 5. Click `Generate Font` on the bottom-horizontal-bar.
7676 6. Inspect the included icons and proceed by clicking `Download`.
7777 7. Extract the font files and adapt the CSS to the paths we use in Jekyll:
78 - Copy the entire `fonts` directory over and overwrite existing ones at `<jekyll>/docs/`.
79 - Copy the contents of `selection.json` and overwrite existing content inside `<jekyll>/docs/icomoon-selection.json`.
80 - Copy the entire `@font-face {}` declaration and only the **new-icon(s)' css declarations** further below, to update the
78
79 - Copy the entire `fonts` directory over and overwrite existing ones at `<jekyll>/docs/`.
80 - Copy the contents of `selection.json` and overwrite existing content inside `<jekyll>/docs/icomoon-selection.json`.
81 - Copy the entire `@font-face {}` declaration and only the **new-icon(s)' css declarations** further below, to update the
8182 `<jekyll>/docs/_sass/_font-awesome.scss` sass partial.
82 - Fix paths in the `@font-face {}` declaration by adding `../` before `fonts/FontAwesome.*` like so:
83 - Fix paths in the `@font-face {}` declaration by adding `../` before `fonts/FontAwesome.*` like so:
8384 `('../fonts/Fontawesome.woff?9h6hxj')`.
8485
8586 ### Adding plugins
8889
8990 ## Code Contributions
9091
91 Interesting in submitting a pull request? Awesome. Read on. There's a few common gotchas that we'd love to help you avoid.
92 Interested in submitting a pull request? Awesome. Read on. There's a few common gotchas that we'd love to help you avoid.
9293
9394 ### Tests and documentation
9495
100101
101102 #### Tests
102103
103 * 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).
104 - 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).
104105
105 * If it's a brand new feature, create a new [Cucumber](https://github.com/cucumber/cucumber/) feature, reusing existing steps where appropriate.
106 - If it's a brand new feature, create a new [Cucumber](https://github.com/cucumber/cucumber/) feature, reusing existing steps where appropriate.
106107
107108 ### Code contributions generally
108109
109 * 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.
110 - 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.
110111
111 * Don't bump the Gem version in your pull request (if you don't know what that means, you probably didn't).
112 - Don't bump the Gem version in your pull request (if you don't know what that means, you probably didn't).
112113
113 * You can use the command `script/console` to start a REPL to explore the result of
114 Jekyll's methods. It also provides you with helpful methods to quickly create a
115 site or configuration. [Feel free to check it out!](https://github.com/jekyll/jekyll/blob/master/script/console)
114 - You can use the command `script/console` to start a REPL to explore the result of
115 Jekyll's methods. It also provides you with helpful methods to quickly create a
116 site or configuration. [Feel free to check it out!](https://github.com/jekyll/jekyll/blob/master/script/console)
117
118 - 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)
116119
117120 ## Running tests locally
118121
145148 Both `script/test` and `script/cucumber` can be run without arguments to
146149 run its entire respective suite.
147150
151 ## Visual Studio Code Development Container
152
153 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.
154
148155 ## A thank you
149156
150157 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!
0 # These are supported funding model platforms
1
2 # github: jekyll
3 open_collective: jekyll
4 tidelift: rubygems/jekyll
0 name: Bug Report
1 description: "Is something not working as expected?"
2 title: "[Bug]: "
3 body:
4 - type: markdown
5 attributes:
6 value: |
7 Hi! Thank you for taking the time to report a bug with Jekyll.
8
9 Please consider asking your question at https://talk.jekyllrb.com if one or more of the following is applicable to your situation:
10
11 - You are not sure if the issue is a bug in Jekyll.
12 - The issue is caused by a third-party plugin.
13 - This is just a generic usage question.
14
15 Additionally, please note that this platform is meant for bugs in Jekyll core only.
16 Issues regarding dependencies and plugins should be reported in their respective repositories.
17 - type: input
18 id: os
19 attributes:
20 label: Operating System
21 description: The operating system of your computer.
22 placeholder: "Ubuntu 21.10"
23 validations:
24 required: true
25 - type: input
26 id: ruby-version
27 attributes:
28 label: Ruby Version
29 description: |
30 The Ruby version you were using at the time.
31 Run `ruby -v` in your terminal and paste the output in the input field.
32 placeholder: "ruby 2.7.3p183 (2021-04-05 revision 6847ee089d) [x64-mingw32]"
33 validations:
34 required: true
35 - type: input
36 id: jekyll-version
37 attributes:
38 label: Jekyll Version
39 description: |
40 The version of Jekyll used in your project.
41 Run `bundle exec jekyll -v` and paste the output in the input field.
42 *If you are not using a Gemfile, run `jekyll -v` instead.*
43 placeholder: "jekyll 4.2.1"
44 validations:
45 required: true
46 - type: input
47 id: ghp-version
48 attributes:
49 label: GitHub Pages Version
50 description: |
51 Are you deploying your site using GitHub Pages?
52 If yes, then we need to know the `github-pages` version used by your project. Proceed ahead otherwise.
53 If you're using the `github-pages` gem in your Gemfile, paste the output from running the following:
54 ```
55 bundle exec github-pages -v
56 ```
57 Otherwise, enter `Latest` in the input field and proceed ahead.
58 - type: textarea
59 id: expected
60 attributes:
61 label: Expected Behavior
62 description: Briefly describe what you expected to see or get with a certain functionality.
63 placeholder: |
64 I expected my site to be built successfully when I run the following:
65 ```
66 bundle exec jekyll build
67 ```
68 validations:
69 required: true
70 - type: textarea
71 id: actual
72 attributes:
73 label: Current Behavior
74 description: >
75 Describe the details of the bug.
76 Be sure to include any steps you took for the problem to exist, such as the directories
77 you created and the full command you ran.
78 Include any plugins you have configured for use in the site.
79 validations:
80 required: true
81 - type: textarea
82 id: logs
83 attributes:
84 label: Relevant log output
85 description: |
86 Please copy and paste any relevant log output from your terminal.
87 *Note: This will be automatically formatted into code, so no need for backticks.*
88 render: shell
89 - type: textarea
90 id: sample
91 attributes:
92 label: Code Sample
93 description: >
94 The easiest way for someone to understand an issue is if they could reproduce your issue
95 in their environment. Therefore, please provide a link to your project repository alongwith
96 instructions to reproduce your issue. If your project is not publicly accessible, please
97 consider setting up a minimal test repository complete with necessary instructions.
98 placeholder: |
99 ### Steps to reproduce issue
100
101 - Clone [my repo](https://github.com/owner/repo)
102 - Install site dependencies
103 - Run `bundle exec jekyll build -s src -d src/dist`
0 blank_issues_enabled: true
1 contact_links:
2 - name: Jekyll Community Forum
3 url: https://talk.jekyllrb.com/
4 about: Please ask and answer questions here.
0 ---
1 name: Documentation
2 about: Found a typo or something that isn't crystal clear in our docs?
3 title: '[Docs]: '
4 labels: documentation
5 assignees: ''
6
7 ---
8
9 <!-- Thanks for taking the time to open an issue and help us make Jekyll better! -->
10
11 ## Motivation
12
13 <!-- Why should we update our docs? -->
14
15
16
17 ## Suggestion
18
19 <!-- What should we do instead? -->
20
21
22
23 <!-- Thanks for taking the time to open an issue and help us make Jekyll better! -->
0 ---
1 name: Feature Request
2 about: Want us to add any features to Jekyll?
3 title: 'feat: '
4 labels: feature
5 assignees: ''
6
7 ---
8
9 <!--
10 Hi! Thanks for considering to file a feature request with Jekyll. Please take the time to
11 answer the basic questions. Please try to be as detailed as possible.
12
13 Thanks!
14 -->
15
16 ## Summary
17
18 <!--
19 A one-paragraph explanation of the feature.
20 -->
21
22 ## Motivation
23
24 <!--
25 Why do you want to see this feature in Jekyll? What makes you sure that it should not be
26 implemented at the plugin level, but in Jekyll core? What use cases does it support?
27
28 NOTE: Please be mindful of the Jekyll philosophy (https://jekyllrb.com/philosophy/),
29 particularly Section 5. Think about if 90% of the users would benefit from your
30 feature request, and whether your feature would be better off in a plugin.
31 -->
32
33 ## Guide-level explanation
34
35 <!--
36 Explain the proposal as if it was already included in the project and you
37 were teaching it to another programmer. That generally means:
38
39 - Introducing new named concepts.
40 - Explaining the feature largely in terms of examples.
41 - If applicable, provide sample error messages, deprecation warnings, or
42 migration guidance.
43
44 If this is a small feature, you may omit this section.
45 -->
46
47 ## Reference-level explanation
48
49 <!--
50 This is the technical portion of the feature request. Explain the design in
51 sufficient detail that:
52
53 - Its interaction with other features is clear.
54 - It is reasonably clear how the feature would be implemented.
55 - Corner cases are dissected by example.
56
57 If you do not know how to answer this, you can omit it. No worries!
58 -->
59
60 ## Drawbacks
61
62 <!--
63 Why should we *not* do this?
64 -->
65
66 ## Unresolved Questions
67
68 <!--
69 What related issues do you consider out of scope for this feature that could be
70 addressed in the future independently of the solution that comes out of this
71 feature?
72 -->
+0
-78
.github/ISSUE_TEMPLATE.md less more
0 <!--
1 Hi! Thanks for considering to file a bug with Jekyll. Please take the time to
2 answer the basic questions. You can convert `[ ]` into `[x]` to check boxes (or submit
3 and check.) If there is no need for certain fields like output and redirection, please delete
4 those headers before submitting. We know not all tickets require those steps.
5 Otherwise, please try to be as detailed as possible.
6
7 If you are unsure this is a bug in Jekyll, or this is a bug caused
8 by a plugin that isn't directly related to Jekyll, or if this is just
9 a generic usage question, please consider asking your question at
10 https://talk.jekyllrb.com where non-bug questions go.
11
12 Thanks!
13 -->
14
15 - [ ] I believe this to be a bug, not a question about using Jekyll.
16 - [ ] I updated to the latest Jekyll (or) if on GitHub Pages to the latest `github-pages`
17 - [ ] I ran `jekyll doctor` to check my configuration
18 - [ ] I read the CONTRIBUTION file at https://jekyllrb.com/docs/contributing/
19 - [ ] This is a feature request.
20
21 ---
22
23 - [ ] I am on (or have tested on) ***macOS*** 10+
24 - [ ] I am on (or have tested on) ***Debian/Ubuntu*** GNU/Linux
25 - [ ] I am on (or have tested on) ***Fedora*** GNU/Linux
26 - [ ] I am on (or have tested on) ***Arch*** GNU/Linux
27 - [ ] I am on (or have tested on) ***Other*** GNU/Linux
28 - [ ] I am on (or have tested on) ***Windows*** 10+
29
30 <!--
31 Other GNU/Linux includes Scientific GNU/Linux, CentOS GNU/Linux, and others.
32 If you are on a minor sub-distro (such as ElementaryOS which does not diverge from
33 Ubuntu much, please check the parent distro. Kubuntu, Edubuntu, Lubuntu should
34 also be flagged as Ubuntu as their packages come from upstream Ubuntu.
35 -->
36
37 ---
38
39 - [ ] I was trying to install.
40 - [ ] There is a broken Plugin API.
41 - [ ] I had an error on GitHub Pages, and I have reproduced it locally.
42 - [ ] I had an error on GitHub Pages, and GitHub Support said it was a Jekyll Bug.
43 - [ ] I had an error on GitHub Pages and I did not test it locally.
44 - [ ] I was trying to build.
45 - [ ] It was another bug.
46
47 ## My Reproduction Steps
48
49 <!--
50 If this error occurred on GitHub Pages, please try to provide us with logs,
51 and look at them yourself, to determine if this is an actual Jekyll bug. In
52 the event you are unsure, file a ticket, however, when you do please provide
53 the logs (strip them of personal information.)
54
55 If you have trouble finding your logs, please email support@github.com and
56 they will happily help you. If you cannot find logs, please try your best to
57 replicate it locally because we cannot fix a problem if we do not know
58 exactly what caused it, or within a relatively close distance.
59 -->
60
61 <!--
62 Insert the steps you took to for this problem to exist. Such as the
63 directories you created and, the full command you ran, and include any
64 plugins you have installed, this is very important.
65
66 If your steps are complicated, you can also submit a GitHub
67 repository (please no zips, they will be removed and rejected by maintainers,)
68 and just supply a command for us to reproduce it ourselves.
69 -->
70
71 ## The Output I Wanted
72
73 <!--
74 Insert the output from the command. Alter it as little as you can.
75 The minimum should be personal information. Though we normally don't log
76 anything like that so there should be no need to alter it.
77 -->
0 <!--
1 Thanks for creating a Pull Request! Before you submit, please make sure
2 you've done the following:
3
4 - I read the contributing document at https://jekyllrb.com/docs/contributing/
5 -->
6
7 <!--
8 Make our lives easier! Choose one of the following by uncommenting it:
9 -->
10
11 <!-- This is a 🐛 bug fix. -->
12 <!-- This is a 🙋 feature or enhancement. -->
13 <!-- This is a 🔦 documentation change. -->
14 <!-- This is a 🔨 code refactoring. -->
15
16 <!--
17 Before you submit this pull request, make sure to have a look at the following
18 checklist. If you don't know how to do some of these, that's fine! Submit
19 your pull request and we will help you out on the way.
20
21 - I've added tests (if it's a bug, feature or enhancement)
22 - I've adjusted the documentation (if it's a feature or enhancement)
23 - The test suite passes locally (run `script/cibuild` to verify this)
24 -->
25
26 ## Summary
27
28 <!--
29 Provide a description of what your pull request changes.
30 -->
31
32 ## Context
33
34 <!--
35 Is this related to any GitHub issue(s)?
36
37 You can use keywords to automatically close the related issue.
38 For example, (all of) the following will close issue #4567 when your PR is merged.
39
40 Closes #4567
41 Fixes #4567
42 Resolves #4567
43
44 Use any one of the above as applicable.
45 -->
0 # Security Policy
1
2 ## Supported Versions
3
4 Security updates are applied to the latest MINOR version of Jekyll, and the version used by GitHub Pages, v3.9.x.
5
6 | Version | Supported |
7 | ------- | ------------------ |
8 | 4.2.x | :white_check_mark: |
9 | 3.9.x | :white_check_mark: |
10 | < 3.9.x | :x: |
11
12 ## Reporting a Vulnerability
13
14 Please report vulnerabilities by sending an email to security@jekyllrb.com with the following information:
15
16 1. A description of the vulnerability
17 2. Reproduction steps and/or a sample site (share a private repo to the [Jekyll Security Team](docs/pages/team.md))
18 3. Your contact information
19
20 The Jekyll security team will respond to your submission and notify you whether it has been confirmed by the team.
21 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
22 been closed.
23
24 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
25 use it. If we use your patch, we will attribute authorship to you either as the commit author, or as a co-author.
26
27 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
28 credit in our release post.
29
30 Once the patched version has been released, we will no longer request you to maintain confidentiality and you may choose to share details on
31 how you found the vulnerability with the community.
0 # check-spelling/check-spelling configuration
1
2 File | Purpose | Format | Info
3 -|-|-|-
4 [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)
5 [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)
6 [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)
7 [excludes.txt](excludes.txt) | Files to ignore entirely | perl regular expression | [excludes](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-excludes)
8 [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)
9 [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)
10 [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)
11 [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)
12
13 Note: you can replace any of these files with a directory by the same name (minus the `.txt` extension) and
14 then include multiple files (with a `.txt` extension) inside that directory to merge multiple files together.
0 <!-- See https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-advice --> <!-- markdownlint-disable MD033 MD041 -->
1
2 <details><summary>If you see a bunch of garbage</summary>
3
4 If it relates to a ...
5 <details><summary>well-formed pattern</summary>
6
7 See if there's a [pattern](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples:-patterns) that would match it.
8
9 If not, try writing one and adding it to the `patterns.txt` file.
10
11 Patterns are Perl 5 Regular Expressions - you can [test](
12 https://www.regexplanet.com/advanced/perl/) yours before committing to verify it will match your lines.
13
14 Note that patterns can't match multiline strings.
15 </details>
16 <details><summary>binary-ish string</summary>
17
18 Please add a file path to the `excludes.txt` file instead of just accepting the garbage.
19
20 File paths are Perl 5 Regular Expressions - you can [test](
21 https://www.regexplanet.com/advanced/perl/) yours before committing to verify it will match your files.
22
23 `^` refers to the file's path from the root of the repository, so `^README\.md$` would exclude [README.md](
24 ../tree/HEAD/README.md) (on whichever branch you're using).
25 </details>
26
27 </details>
0 statictastic
1 Statictastic
2 Linting
3 hakiri
4 built-ins
0 # See https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples:-excludes
1
2 (?:^|/)(?i)COPYRIGHT
3 (?:^|/)(?i)LICEN[CS]E
4 (?:^|/)package(?:-lock|)\.json$
5 (?:^|/)vendor/
6
7 /fonts/
8 ignore$
9
10 \.avi$
11 \.eot$
12 \.ico$
13 \.jpe?g$
14 \.lock$
15 \.map$
16 \.min\.
17 \.mod$
18 \.mp[34]$
19 \.png$
20 \.svg$
21 \.ttf$
22 \.wav$
23 \.woff$
24 \.woff2$
25
26 ^docs/pages/redirects/github\.html$
27 ^lib/jekyll/mime\.types$
28 ^lib/theme_template/example/index\.html$
29 ^lib/theme_template/example/_post\.md$
30 ^test/fixtures/empty_permalink\.erb$
31 ^test/fixtures/webrick/bar/baz\.html$
32 ^test/fixtures/webrick/bar/foo\.xhtml$
33 ^test/source/_posts/2009-06-22-no-yaml\.markdown$
34 ^\.github/
0 acl
1 activesupport
2 adaoraul
3 addons
4 aeiou
5 AFile
6 afterall
7 Alexey
8 alfredxing
9 algolia
10 allowfullscreen
11 Anatoliy
12 andreyvit
13 Ankit
14 Anning
15 apps
16 appveyor
17 arengu
18 args
19 ariejan
20 arounds
21 asciinema
22 asdf
23 ashmaroli
24 attr
25 Autobuild
26 autocompletion
27 autogenerated
28 Autolink
29 autoload
30 autoreconf
31 autosave
32 awood
33 aws
34 awscli
35 backend
36 backport
37 backtick
38 barcamp
39 baseurl
40 bashrc
41 baz
42 bbatsov
43 bdimcheff
44 bellvat
45 benbalter
46 Beney
47 binstubs
48 bip
49 bitbucket
50 blog
51 Blogger
52 blogging
53 bonafide
54 Bou
55 breadcrumbs
56 briandoll
57 bridgetown
58 bridgetownrb
59 brightbox
60 brighterplanet
61 buddyworks
62 Bugfix
63 Burela
64 byparker
65 cachegrind
66 calavera
67 callgraphs
68 cartera
69 cavalle
70 CDNs
71 cgi
72 changefreq
73 changelog
74 chango
75 charset
76 Chayoung
77 chcp
78 chdir
79 Cheatsheet
80 Checkoway
81 chmod
82 chown
83 Chrononaut
84 chruby
85 cibuild
86 cimg
87 circleci
88 CJK
89 classname
90 cloudcannon
91 Cloudinary
92 cloudsh
93 CLT
94 codebase
95 codeclimate
96 CODEOWNERS
97 coderay
98 codeslinger
99 coffeescript
100 colorator
101 commandline
102 commonmark
103 compat
104 compatibilize
105 concat
106 config
107 configyml
108 contentblocks
109 CORS
110 Cov
111 CRLFs
112 cron
113 crontab
114 cruft
115 css
116 csv
117 Currin
118 CVE
119 CWD
120 cygwin
121 daringfireball
122 Dassonville
123 datafiles
124 datetime
125 DCEU
126 Debian
127 debuggability
128 defunkt
129 delegators
130 dependabot
131 deployer
132 deps
133 dest
134 Devkit
135 devops
136 digitalocean
137 dirs
138 disqus
139 ditaa
140 dnf
141 doclist
142 doctype
143 doeorg
144 dommmel
145 dotfile
146 Dousse
147 downcase
148 downcased
149 duckduckgo
150 duritong
151 Dusseau
152 dysinger
153 ecf
154 editorconfig
155 eduardoboucas
156 Elasticsearch
157 elsif
158 Emacs
159 emails
160 emoji
161 endcapture
162 endcomment
163 endfor
164 endhighlight
165 endif
166 endraw
167 endrender
168 endtablerow
169 Enumerables
170 EOL
171 erb
172 errordocument
173 Espinaco
174 eugenebolshakov
175 evaled
176 exe
177 execjs
178 extensionpack
179 extname
180 exts
181 favicon
182 Fengyun
183 ffi
184 figcaption
185 filesystem
186 Finazzo
187 firstimage
188 FIXME
189 flakey
190 flickr
191 fnmatch
192 fontello
193 forloop
194 formcake
195 formcarry
196 formester
197 formingo
198 formkeep
199 formspark
200 formspree
201 formx
202 Forwardable
203 frameborder
204 freenode
205 frontmatter
206 fsnotify
207 ftp
208 fullstory
209 Gaudino
210 gcc
211 gcnovus
212 gemfile
213 gemset
214 gemspec
215 getform
216 getset
217 getsimpleform
218 gettalong
219 gfm
220 ghp
221 ghpages
222 giraffeacademy
223 github
224 githubcom
225 githubusercontent
226 gitignore
227 gitlab
228 gjtorikian
229 globbed
230 globbing
231 google
232 gotcha
233 Goulven
234 gridism
235 GSo
236 gsub
237 gsubbing
238 Hakiri
239 hardcode
240 hashbang
241 hashmap
242 helaili
243 henrik
244 heredoc
245 heroku
246 highlighter
247 hilighting
248 Hoizey
249 homepage
250 hostman
251 hostname
252 href
253 htaccess
254 htm
255 html
256 htmlproofer
257 http
258 httpd
259 httpdocs
260 hyperlinks
261 Iaa
262 ial
263 ico
264 icomoon
265 iconset
266 ified
267 iframe
268 img
269 Impl
270 Inlining
271 invokables
272 irc
273 ivey
274 ize
275 jalali
276 jameshamann
277 jamstackthemes
278 jan
279 javascript
280 Jax
281 jayferd
282 jcon
283 jdoe
284 jeffreytse
285 jeffrydegrande
286 Jekpack
287 jekyllbot
288 jekyllconf
289 Jekyllers
290 Jekyllin
291 Jekylling
292 jekyllized
293 jekylllayoutconcept
294 jekyllrb
295 jekyllthemes
296 jemoji
297 jmcglone
298 jneen
299 johnreilly
300 jpg
301 jqr
302 jruby
303 json
304 jsonify
305 juretta
306 jwarby
307 Kacper
308 Kasberg
309 kbd
310 Kentico
311 Kewin
312 keycdn
313 kickster
314 Kinnula
315 kiwifruit
316 Kolesky
317 konklone
318 kontent
319 Kotvinsky
320 kramdown
321 Kulig
322 Kwokfu
323 Lamprecht
324 laquo
325 lastmod
326 launchctl
327 launchy
328 laurilehmijoki
329 ldquo
330 learnxinyminutes
331 lexer
332 LGTM
333 libcurl
334 libffi
335 lifecycle
336 lightgray
337 limjh
338 linenos
339 linkify
340 linux
341 liufengyun
342 livereload
343 localheinz
344 localhost
345 localtime
346 Locher
347 loglevel
348 Losslessly
349 lovin
350 lsi
351 lsquo
352 lstrip
353 lyche
354 macos
355 macromates
356 mademistakes
357 mailto
358 Manmeet
359 markdownify
360 Maroli
361 Marsceill
362 maruku
363 mathjax
364 mathml
365 mattr
366 Maximiliano
367 mchung
368 mdash
369 memberspace
370 Memoize
371 memoized
372 memoizing
373 mentoring
374 mergable
375 Mertcan
376 mertkahyaoglu
377 metadata
378 microdata
379 microsoft
380 mimetype
381 mingw
382 minibundle
383 minifier
384 minitest
385 Mittal
386 mixin
387 mkasberg
388 mkd
389 mkdir
390 mkdn
391 mkdown
392 mmistakes
393 modernizr
394 mojombo
395 moncefbelyamani
396 moz
397 mreid
398 msdn
399 mswin
400 MSYS
401 mtime
402 multiline
403 munging
404 Mvvm
405 myblog
406 mycontent
407 mydata
408 mydoc
409 myimage
410 mypage
411 myposts
412 myproject
413 myrepo
414 mysite
415 myvalue
416 myvar
417 myvariable
418 Nadjib
419 nakanishi
420 namespace
421 namespaced
422 navbar
423 nbsp
424 nearlyfreespeech
425 nethack
426 netlify
427 netlifycms
428 Neue
429 nginx
430 ngx
431 nielsenramon
432 nior
433 nodejs
434 noifniof
435 nokogiri
436 notextile
437 onclick
438 onebox
439 oneclick
440 onschedule
441 opensource
442 openssl
443 Optim
444 orderofinterpretation
445 orgs
446 OSVDB
447 osx
448 packagecontrol
449 pacman
450 paginator
451 pandoc
452 pantulis
453 params
454 parkr
455 parseable
456 paspagon
457 passthrough
458 pathawks
459 Pathutil
460 paywall
461 pdf
462 Pelykh
463 permalink
464 PHP
465 pinboard
466 Piwigo
467 pjhyett
468 pkill
469 pkpass
470 placeholders
471 planetjekyll
472 plantuml
473 plugin
474 png
475 podcasts
476 popen
477 Porcel
478 Posterous
479 postfiles
480 postlayout
481 postmodern
482 preinstalled
483 prepends
484 Prioritise
485 Probot
486 projectlist
487 pubstorm
488 pufuwozu
489 pwa
490 pwd
491 pygments
492 qrush
493 Quaid
494 quickstart
495 rackup
496 Rakefile
497 raquo
498 razorops
499 rbenv
500 rdiscount
501 rdoc
502 rdquo
503 readme
504 realz
505 rebund
506 redcarpet
507 redcloth
508 redgreen
509 redhat
510 refactor
511 refactoring
512 Refheap
513 regen
514 regex
515 regexp
516 remi
517 reqs
518 Responsify
519 revertable
520 rfc
521 rfelix
522 RHEL
523 ridk
524 roadmap
525 rowspan
526 rspec
527 rsquo
528 rss
529 rstrip
530 rsync
531 rtomayko
532 Rubo
533 rubocop
534 rubychan
535 rubygem
536 rubyinstaller
537 rubyprof
538 Ruparelia
539 Rusiczki
540 rvm
541 ryanflorence
542 saas
543 samplelist
544 samrayner
545 sandboxed
546 Sassc
547 sassify
548 schemastore
549 Schroers
550 Schwartzian
551 scp
552 screenshot
553 scrollbar
554 scroller
555 scss
556 scssify
557 sdk
558 SDKROOT
559 sectore
560 semver
561 seo
562 serverless
563 setenv
564 SFTP
565 shingo
566 shopify
567 shortlog
568 shortlinks
569 shoulda
570 sieversii
571 sigpipe
572 simplecov
573 Singhaniya
574 siteleaf
575 sitemap
576 SITENAME
577 Slicehost
578 slugified
579 slugify
580 smartforms
581 smartify
582 snipcart
583 socio
584 somedir
585 sonnym
586 Sonomy
587 sourced
588 sourcemaps
589 spam
590 spotify
591 src
592 ssg
593 ssh
594 SSL
595 stackoverflow
596 standalone
597 staticfiles
598 staticman
599 statictastic
600 STDERR
601 stdout
602 Stickyposts
603 strftime
604 stringified
605 Stringify
606 styleguide
607 stylesheet
608 subdir
609 subdomain
610 subfolder
611 subfolderitems
612 subnav
613 subpages
614 subpath
615 subpiece
616 subsubfolderitems
617 subthing
618 subvalues
619 subwidget
620 sudo
621 superdirectories
622 superdirs
623 SUSE
624 sverrirs
625 svg
626 svn
627 swfobject
628 swupd
629 symlink
630 symlinking
631 tablerow
632 tada
633 Taillandier
634 talkyard
635 tbody
636 technicalpickles
637 templating
638 templatize
639 Termux
640 textilize
641 textpattern
642 thead
643 therubyracer
644 Theunissen
645 Thornquest
646 thoughtbot
647 throughs
648 Tidelift
649 timeago
650 timezone
651 titleize
652 TLS
653 tmm
654 tmp
655 toc
656 tok
657 tomjoht
658 toml
659 tomo
660 toolset
661 toshimaru
662 triaged
663 triaging
664 truncatewords
665 tsv
666 ttf
667 Tudou
668 Tumblr
669 Tweetsert
670 txtpen
671 Tyborska
672 tzinfo
673 ubuntu
674 uby
675 ujh
676 ultron
677 undumpable
678 unencode
679 Unescape
680 unescaping
681 unicode
682 uniq
683 upcase
684 uppercasing
685 uri
686 url
687 urlset
688 username
689 usr
690 utf
691 utils
692 utime
693 utm
694 vanpelt
695 Vasovi
696 vendored
697 vercel
698 versioned
699 versioning
700 vertycal
701 Veyor
702 vilcans
703 Vishesh
704 visualstudio
705 vnd
706 vohedge
707 vps
708 vscode
709 vwochnik
710 Walkthroughs
711 wdm
712 We'd
713 webfont
714 webhook
715 webhosting
716 webmentions
717 webrick
718 website
719 weekdate
720 whitelist
721 whitelisting
722 wiki
723 wikipedia
724 wildcards
725 willcodeforfoo
726 woff
727 wordpress
728 Workaround
729 workflow
730 wsl
731 www
732 xcode
733 xcrun
734 xdg
735 Xhmikos
736 xhtml
737 Xiaoiver
738 XMinutes
739 xml
740 xmlns
741 xmlschema
742 yajl
743 yaml
744 Yarp
745 Yashu
746 Yastreb
747 yml
748 Youku
749 youtube
750 yunbox
751 zeropadding
752 Zlatan
753 zlib
754 zoneinfo
755 zpinter
756 Zsh
757 zshrc
758 zypper
759 zzot
760 frontend
761 prefetching
0 # See https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples:-patterns
1
2 # data urls
3 (['"])data:.*?\g{-1}
4 data:[-a-zA-Z=;:/0-9+]*,\S*
5
6 # YouTube
7 https?://(?:(?:www\.|)youtube\.com|youtu.be)/(?:channel/|embed/|playlist\?list=|watch\?v=|v/|)[-a-zA-Z0-9?&=_]*
8 <\s*youtube\s+id=['"][-a-zA-Z0-9?_]*['"]
9 \bimg\.youtube\.com/vi/[-a-zA-Z0-9?&=_]*
10 youtube_id:\s*[-a-zA-Z0-9?&=_]*
11
12 # Google Analytics
13 \bgoogle-analytics\.com/collect.[-0-9a-zA-Z?%=&_.~]*
14
15 # Google APIs
16 \bgoogleapis\.com/[a-z]+/v\d+/[a-z]+/[@./?=\w]+
17 \b[-a-zA-Z0-9.]*\bstorage\d*\.googleapis\.com(?:/\S*|)
18
19 # Google Calendar
20 \bcalendar\.google\.com/calendar(?:/u/\d+|)/embed\?src=[@./?=\w&%]+
21 \w+\@group\.calendar\.google\.com\b
22
23 # Google DataStudio
24 \bdatastudio\.google\.com/(?:(?:c/|)u/\d+/|)(?:embed/|)(?:open|reporting|datasources|s)/[-0-9a-zA-Z]+(?:/page/[-0-9a-zA-Z]+|)
25
26 # The leading `/` here is as opposed to the `\b` above
27 # ... a short way to match `https://` or `http://` since most urls have one of those prefixes
28 # Google Docs
29 /docs\.google\.com/[a-z]+/d/(?:e/|)[0-9a-zA-Z_-]+/?
30
31 # Google Groups
32 https://groups\.google\.com/d/topic/[^/]+/[a-zA-Z0-9]+/discussion
33 https://groups\.google\.com/d/msg/[^/]+/[a-zA-Z0-9]+/[a-zA-Z0-9]+
34
35 # Google themes
36 themes\.googleusercontent\.com/static/fonts/[^/]+/v\d+/[^.]+.
37
38 # Google CDN
39 \bclients2\.google(?:usercontent|)\.com[-0-9a-zA-Z/.]*
40
41 # Goo.gl
42 /goo\.gl/[a-zA-Z0-9]+
43
44 # Google Chrome Store
45 \bchrome\.google\.com/webstore/detail/\w*(?:/\w*|)
46
47 # google_site_verification:
48 google_site_verification: [-a-zA-Z=;:/0-9+]*
49
50 # Ruby-doc.org
51 https://ruby-doc\.org/.*
52
53 # Contributors
54 alphabetical order.*:.*
55 twitter_handle: .*
56
57 # apiKey
58 apiKey: '[a-f0-9]+'
59
60 # FontAwesome
61 /(?:(?i)FontAwesome\.\w+\?\w+)
62
63 # Lorem
64 (?:\w|\s|[,.])*\b(?i)(?:amet|consectetur|cursus|dolor|eros|ipsum|lacus|libero|ligula|lorem|magna|neque|nulla|suscipit|tempus|ultrices)\b(?:\w|\s|[,.])*
65
66 # URL escaped characters
67 \%[0-9A-F]{2}
68 # c99 hex digits (not the full format, just one I've seen)
69
70 # hex digits including css/html color classes:
71 (?:[\\0][xX]|\\u|[uU]\+|#x?|\%23)[0-9a-fA-FgGrR_]{2,}(?:[uU]?[lL]{0,2}|u\d+)\b
72
73 # ignore long runs of a single character:
74 \b([A-Za-z])\g{-1}{3,}\b
0 ^attache$
1 benefitting
2 occurence
3 Sorce
4 ^[Ss]pae
5 ^untill
6 ^wether
0 updateDocsComment: >
1 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.
2
3 updateDocsWhiteList:
4 - bug
5 - fix
6 - Backport
7 - dev
8 - Update
9 - WIP
10 - chore
11
12 updateDocsTargetFiles:
13 - README
14 - docs/
0 version: 2
1 updates:
2 - package-ecosystem: "github-actions"
3 directory: "/"
4 schedule:
5 interval: "weekly"
11
22 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 💝
33
4 [About First Timers Only](http://www.firsttimersonly.com/).
4 [About First Timers Only](https://www.firsttimersonly.com/).
55
66 ### 🤔 What you will need to know.
77
0 # frozen_string_literal: true
1
2 require 'jekyll'
3 require 'memory_profiler'
4
5 MemoryProfiler.report(allow_files: ['lib/jekyll/', 'lib/jekyll.rb']) do
6 Jekyll::PluginManager.require_from_bundler
7 Jekyll::Commands::Build.process({
8 "source" => File.expand_path(ARGV[0]),
9 "destination" => File.expand_path("#{ARGV[0]}/_site"),
10 "disable_disk_cache" => true,
11 })
12 puts ''
13 end.pretty_print(scale_bytes: true, normalize_paths: true)
0 name: Micro Benchmark Runs
1
2 on:
3 workflow_dispatch:
4 inputs:
5 path:
6 description: "Path to benchmark script relative to 'benchmark' directory."
7 required: true
8 default: "capture-assign.rb"
9 ruby_version:
10 description: "Ruby version to use (via `ruby/setup-ruby@v1`) action."
11 required: false
12 default: "2.7"
13
14 jobs:
15 benchmark:
16 name: "Benchmark (${{ github.event.inputs.path }}) (Ruby ${{ github.event.inputs.ruby_version }})"
17 runs-on: "ubuntu-latest"
18 env:
19 BENCHMARK: true
20 steps:
21 - name: Checkout Jekyll
22 uses: actions/checkout@v3
23 - name: Set up Ruby
24 uses: ruby/setup-ruby@v1
25 with:
26 ruby-version: ${{ github.event.inputs.ruby_version }}
27 bundler-cache: true
28 - name: Run Benchmark
29 run: "bundle exec ruby benchmark/${{ github.event.inputs.path }}"
0 name: Continuous Integration
1
2 on:
3 push:
4 branches:
5 - master
6 - "*-stable"
7 pull_request:
8 branches:
9 - master
10 - "*-stable"
11
12 jobs:
13 ci:
14 name: "Run Tests (${{ matrix.label }})"
15 runs-on: "ubuntu-latest"
16 strategy:
17 fail-fast: false
18 matrix:
19 include:
20 - label: Ruby 2.7
21 ruby_version: "2.7"
22 - label: Ruby 3.0
23 ruby_version: "3.0"
24 - label: Ruby 3.1
25 ruby_version: "3.1"
26 - label: JRuby 9.3.4.0
27 ruby_version: "jruby-9.3.4.0"
28 steps:
29 - name: Checkout Repository
30 uses: actions/checkout@v3
31 - name: "Set up ${{ matrix.label }}"
32 uses: ruby/setup-ruby@v1
33 with:
34 ruby-version: ${{ matrix.ruby_version }}
35 bundler-cache: true
36 - name: Run Minitest based tests
37 run: bash script/test
38 - name: Run Cucumber based tests
39 run: bash script/cucumber
40 - name: Generate and Build a new site
41 run: bash script/default-site
42
43 xtras:
44 name: "${{ matrix.job_name }} (Ruby ${{ matrix.ruby_version }})"
45 runs-on: "ubuntu-latest"
46 strategy:
47 fail-fast: false
48 matrix:
49 include:
50 - job_name: "Profile Docs Site"
51 step_name: "Build and Profile docs site"
52 script_file: "profile-docs"
53 ruby_version: "2.7"
54 - job_name: "Style Check"
55 step_name: "Run RuboCop"
56 script_file: "fmt"
57 ruby_version: "2.7"
58 steps:
59 - name: Checkout Repository
60 uses: actions/checkout@v3
61 - name: "Set up Ruby ${{ matrix.ruby_version }}"
62 uses: ruby/setup-ruby@v1
63 with:
64 ruby-version: ${{ matrix.ruby_version }}
65 bundler-cache: true
66 - name: ${{ matrix.step_name }}
67 run: bash script/${{ matrix.script_file }}
0 name: Build and deploy Jekyll documentation site
1
2 on:
3 push:
4 branches:
5 - master
6
7 env:
8 RUBY_VERSION: 2.7
9
10 jobs:
11 deploy_docs:
12 if: "!contains(github.event.commits[0].message, '[ci skip]')"
13 runs-on: 'ubuntu-latest'
14 env:
15 BUNDLE_PATH: "vendor/bundle"
16 BUNDLE_JOBS: 4
17 BUNDLE_RETRY: 3
18 steps:
19 - uses: actions/checkout@v3
20 - uses: ruby/setup-ruby@v1
21 with:
22 ruby-version: ${{ env.RUBY_VERSION }}
23 bundler-cache: true
24 - name: Clone target branch
25 run: |
26 REMOTE_BRANCH="${REMOTE_BRANCH:-gh-pages}"
27 REMOTE_REPO="https://${GITHUB_ACTOR}:${{ secrets.GITHUB_TOKEN }}@github.com/${GITHUB_REPOSITORY}.git"
28
29 echo "Publishing to ${GITHUB_REPOSITORY} on branch ${REMOTE_BRANCH}"
30 rm -rf docs/_site/
31 git clone --depth=1 --branch="${REMOTE_BRANCH}" --single-branch --no-checkout \
32 "${REMOTE_REPO}" docs/_site/
33 - name: Build site
34 run: bundle exec jekyll build --source docs --destination docs/_site --verbose --trace
35 env:
36 # For jekyll-github-metadata
37 JEKYLL_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
38 - name: Deploy to GitHub Pages
39 run: |
40 SOURCE_COMMIT="$(git log -1 --pretty="%an: %B" "$GITHUB_SHA")"
41 pushd docs/_site &>/dev/null
42 : > .nojekyll
43
44 git add --all
45 git -c user.name="${GITHUB_ACTOR}" -c user.email="${GITHUB_ACTOR}@users.noreply.github.com" \
46 commit --quiet \
47 --message "Deploy docs from ${GITHUB_SHA}" \
48 --message "$SOURCE_COMMIT"
49 git push
50
51 popd &>/dev/null
0 name: Release Gem
1
2 on:
3 push:
4 branches:
5 - master
6 - "*-stable"
7 paths:
8 - "lib/**/version.rb"
9
10 jobs:
11 release:
12 if: "github.repository_owner == 'jekyll'"
13 name: "Release Gem (Ruby ${{ matrix.ruby_version }})"
14 runs-on: "ubuntu-latest"
15 strategy:
16 fail-fast: true
17 matrix:
18 ruby_version:
19 - 2.7
20 steps:
21 - name: Checkout Repository
22 uses: actions/checkout@v3
23 - name: "Set up Ruby ${{ matrix.ruby_version }}"
24 uses: ruby/setup-ruby@v1
25 with:
26 ruby-version: ${{ matrix.ruby_version }}
27 bundler-cache: true
28 - name: Build and Publish Gem
29 uses: ashmaroli/release-gem@dist
30 with:
31 gemspec_name: jekyll
32 env:
33 GEM_HOST_API_KEY: ${{ secrets.RUBYGEMS_GEM_PUSH_API_KEY }}
0 name: Spell Check
1 on:
2 push:
3 branches:
4 - master
5 - "*-stable"
6 # Switch from `pull_request_target` event to reduce distraction from comments
7 # regarding errors reported in unmodified files.
8 pull_request:
9 branches:
10 - master
11 - "*-stable"
12
13 jobs:
14 spelling:
15 name: Spell Check
16 permissions:
17 contents: read
18 pull-requests: read
19 actions: read
20 outputs:
21 followup: ${{ steps.spelling.outputs.followup }}
22 runs-on: ubuntu-latest
23 if: "contains(github.event_name, 'pull_request') || github.event_name == 'push'"
24 concurrency:
25 group: spelling-${{ github.event.pull_request.number || github.ref }}
26 # note: If you use only_check_changed_files, you do not want cancel-in-progress
27 cancel-in-progress: true
28 steps:
29 - name: check-spelling
30 id: spelling
31 uses: check-spelling/check-spelling@v0.0.20
32 with:
33 # 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:
34 suppress_push_for_open_pull_request: 1
35 # The action will manage checking out the repository itself instead of requiring the workflow to use `actions/checkout...`:
36 checkout: true
37 # 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:
38 post_comment: 0
0 name: Third-Party Repository Profiling
1
2 on:
3 push:
4 branches:
5 - master
6 pull_request:
7 branches:
8 - master
9 jobs:
10 build_n_profile:
11 if: "!contains(github.event.commits[0].message, '[ci skip]')"
12 runs-on: 'ubuntu-latest'
13 env:
14 BUNDLE_GEMFILE: "sandbox/Gemfile"
15 BUNDLE_PATH: "vendor/bundle"
16 BUNDLE_JOBS: 4
17 BUNDLE_RETRY: 3
18 steps:
19 - name: Checkout Jekyll
20 uses: actions/checkout@v3
21 with:
22 fetch-depth: 5
23 path: jekyll
24 - name: Checkout Third-Party Repository
25 uses: actions/checkout@v3
26 with:
27 repository: ashmaroli/tomjoht.github.io
28 path: sandbox
29 - name: Set up Ruby
30 uses: ruby/setup-ruby@v1
31 with:
32 ruby-version: 2.7
33 bundler-cache: true
34 - name: Run Jekyll Build 3 times
35 run: |
36 bundle exec jekyll build -s sandbox -d sandbox/_site --trace
37 bundle exec jekyll build -s sandbox -d sandbox/_site --trace
38 bundle exec jekyll build -s sandbox -d sandbox/_site --trace
39 - name: Memory Analysis of Jekyll Build
40 run: bundle exec ruby jekyll/.github/workflows/actions/memprof.rb sandbox
0 # Jekyll
1 _site/
2 *-cache/
3 .jekyll-metadata
4
5 # Ruby
6 .bundle/
7 .byebug_history
8 .ruby-gemset
9 .ruby-version
010 *.gem
11 Gemfile.lock
12
13 # Files
14 .analysis
15 .DS_Store
116 *.swp
217 *~
3 .DS_Store
4 .analysis
5 .bundle/
6 .byebug_history
7 .jekyll-metadata
8 .ruby-gemset
9 .ruby-version
10 .sass-cache
11 /test/source/file_name.txt
18
19 # Folders
1220 /vendor
13 Gemfile.lock
14 _site/
21 bbin/
1522 bin/
16 bbin/
1723 coverage
1824 gh-pages/
1925 pkg/
20 site/_site/
2126 test/dest
2227 tmp/*
00 ---
1 inherit_from: .rubocop_todo.yml
12
23 require:
4 - rubocop-minitest
5 - rubocop-performance
6 - rubocop-rake
7 - rubocop-rspec
38 - ./rubocop/jekyll
49
510 Jekyll/NoPutsAllowed:
712 - rake/*.rake
813
914 AllCops:
10 TargetRubyVersion: 2.1
15 TargetRubyVersion: 2.7
1116 Include:
1217 - lib/**/*.rb
18 - test/**/*.rb
1319 Exclude:
1420 - bin/**/*
1521 - exe/**/*
1723 - script/**/*
1824 - vendor/**/*
1925 - tmp/**/*
20 Layout/AlignArray:
21 Enabled: false
22 Layout/AlignHash:
26
27 Gemspec/DeprecatedAttributeAssignment:
28 Enabled: true
29 Gemspec/RequireMFA:
30 Enabled: false
31
32 Layout/BeginEndAlignment:
33 Enabled: true
34 Layout/EmptyComment:
35 Enabled: false
36 Layout/EmptyLinesAroundAttributeAccessor:
37 Enabled: true
38 Layout/EndAlignment:
39 Severity: error
40 Layout/FirstArrayElementIndentation:
41 EnforcedStyle: consistent
42 Layout/FirstHashElementIndentation:
43 EnforcedStyle: consistent
44 Layout/HashAlignment:
2345 EnforcedHashRocketStyle: table
24 Layout/AlignParameters:
25 Enabled: false
26 Layout/EmptyLinesAroundAccessModifier:
27 Enabled: false
28 Layout/EmptyLinesAroundModuleBody:
29 Enabled: false
30 Layout/EndOfLine:
31 EnforcedStyle: native
32 Layout/ExtraSpacing:
33 AllowForAlignment: true
34 Layout/FirstParameterIndentation:
35 EnforcedStyle: consistent
3646 Layout/IndentationWidth:
3747 Severity: error
38 Layout/IndentArray:
39 EnforcedStyle: consistent
40 Layout/IndentHash:
41 EnforcedStyle: consistent
42 Layout/IndentHeredoc:
43 Enabled: false
48 Layout/LineContinuationLeadingSpace:
49 Enabled: true
50 Layout/LineContinuationSpacing:
51 Enabled: true
52 Layout/LineEndStringConcatenationIndentation:
53 Enabled: true
54 Layout/LineLength:
55 Exclude:
56 - !ruby/regexp /features\/.*.rb/
57 - Rakefile
58 - rake/*.rake
59 - Gemfile
60 Max: 100
61 Severity: warning
4462 Layout/MultilineMethodCallIndentation:
4563 EnforcedStyle: indented
4664 Layout/MultilineOperationIndentation:
4765 EnforcedStyle: indented
66 Layout/SpaceAroundMethodCallOperator:
67 Enabled: true
68 Layout/SpaceBeforeBrackets:
69 Enabled: true
70 Layout/SpaceInsideHashLiteralBraces:
71 Enabled: true
72 Exclude:
73 - test/**/*.rb
74
75 Lint/AmbiguousAssignment:
76 Enabled: true
77 Lint/AmbiguousOperatorPrecedence:
78 Enabled: true
79 Lint/AmbiguousRange:
80 Enabled: true
81 Lint/BinaryOperatorWithIdenticalOperands:
82 Enabled: true
83 Lint/ConstantDefinitionInBlock:
84 Enabled: true
85 Exclude:
86 - test/**/*.rb
87 Lint/ConstantOverwrittenInRescue:
88 Enabled: true
89 Lint/DeprecatedConstants:
90 Enabled: true
91 Lint/DeprecatedOpenSSLConstant:
92 Enabled: true
93 Lint/DuplicateBranch:
94 Enabled: true
95 Lint/DuplicateElsifCondition:
96 Enabled: true
97 Lint/DuplicateRegexpCharacterClassElement:
98 Enabled: true
99 Lint/DuplicateRequire:
100 Enabled: true
101 Lint/DuplicateRescueException:
102 Enabled: true
103 Lint/EmptyBlock:
104 Enabled: true
105 Lint/EmptyClass:
106 Enabled: true
107 Lint/EmptyConditionalBody:
108 Enabled: true
109 Lint/EmptyFile:
110 Enabled: true
111 Lint/FloatComparison:
112 Enabled: true
113 Lint/HashCompareByIdentity:
114 Enabled: true
115 Lint/IdentityComparison:
116 Enabled: true
117 Lint/LambdaWithoutLiteralBlock:
118 Enabled: true
119 Lint/MissingSuper:
120 Enabled: false
121 Lint/MixedRegexpCaptureTypes:
122 Enabled: false
48123 Lint/NestedPercentLiteral:
49124 Exclude:
50125 - test/test_site.rb
51 Layout/EmptyComment:
52 Enabled: false
53 Layout/EndAlignment:
54 Severity: error
55 Lint/SplatKeywordArguments:
56 Enabled: false
126 Lint/NoReturnInBeginEndBlocks:
127 Enabled: true
128 Lint/NumberedParameterAssignment:
129 Enabled: true
130 Lint/OrAssignmentToConstant:
131 Enabled: true
132 Lint/OutOfRangeRegexpRef:
133 Enabled: true
134 Lint/RaiseException:
135 Enabled: true
136 Lint/RedundantDirGlobSort:
137 Enabled: true
138 Lint/RedundantSafeNavigation:
139 Enabled: true
140 Lint/RequireRangeParentheses:
141 Enabled: true
142 Lint/RequireRelativeSelfPath:
143 Enabled: true
144 Lint/SelfAssignment:
145 Enabled: true
146 Lint/StructNewOverride:
147 Enabled: true
148 Lint/SymbolConversion:
149 Enabled: true
150 Lint/ToEnumArguments:
151 Enabled: false
152 Lint/TopLevelReturnWithArgument:
153 Enabled: true
154 Lint/TrailingCommaInAttributeDeclaration:
155 Enabled: true
156 Lint/TripleQuotes:
157 Enabled: true
158 Lint/UnexpectedBlockArity:
159 Enabled: true
160 Lint/UnmodifiedReduceAccumulator:
161 Enabled: true
57162 Lint/UnreachableCode:
58163 Severity: error
59 Lint/UselessAccessModifier:
60 Enabled: false
164 Lint/UnreachableLoop:
165 Enabled: true
166 Lint/UselessMethodDefinition:
167 Enabled: true
168 Lint/UselessTimes:
169 Enabled: true
61170 Lint/Void:
62 Enabled: false
171 Exclude:
172 - lib/jekyll/site.rb
173
63174 Metrics/AbcSize:
64 Max: 21
175 Max: 23
65176 Metrics/BlockLength:
66177 Exclude:
67178 - test/**/*.rb
68179 - lib/jekyll/configuration.rb
69180 - rake/*.rake
70 - jekyll.gemspec
71181 Metrics/ClassLength:
72182 Exclude:
73183 - !ruby/regexp /features\/.*.rb$/
74184 - !ruby/regexp /test\/.*.rb$/
75 Max: 300
185 - lib/jekyll/document.rb
186 - lib/jekyll/site.rb
187 - lib/jekyll/commands/serve.rb
188 - lib/jekyll/configuration.rb
189 Max: 240
76190 Metrics/CyclomaticComplexity:
77 Max: 9
78 Metrics/LineLength:
79 Exclude:
80 - !ruby/regexp /features\/.*.rb/
81 - Rakefile
82 - rake/*.rake
83 - Gemfile
84 - jekyll.gemspec
85 Max: 90
86 Severity: warning
191 Exclude:
192 - lib/jekyll/utils.rb
193 - lib/jekyll/commands/serve.rb
194 Max: 11
87195 Metrics/MethodLength:
88196 CountComments: false
89197 Max: 20
90198 Severity: error
91199 Metrics/ModuleLength:
200 Exclude:
201 - lib/jekyll/filters.rb
92202 Max: 240
93203 Metrics/ParameterLists:
94204 Max: 4
95205 Metrics/PerceivedComplexity:
96 Max: 8
206 Max: 13
207
208 Minitest/AssertEmptyLiteral:
209 Enabled: false
210 Minitest/AssertInDelta:
211 Enabled: true
212 Minitest/AssertionInLifecycleHook:
213 Enabled: true
214 Minitest/AssertKindOf:
215 Enabled: true
216 Minitest/AssertOutput:
217 Enabled: true
218 Minitest/AssertPathExists:
219 Enabled: true
220 Minitest/AssertSilent:
221 Enabled: true
222 Minitest/AssertWithExpectedArgument:
223 Enabled: true
224 Minitest/LiteralAsActualArgument:
225 Enabled: true
226 Minitest/TestMethodName:
227 Enabled: false
228 Minitest/MultipleAssertions:
229 Enabled: true
230 Minitest/RefuteInDelta:
231 Enabled: true
232 Minitest/RefuteKindOf:
233 Enabled: true
234 Minitest/RefutePathExists:
235 Enabled: true
236 Minitest/UnreachableAssertion:
237 Enabled: true
238 Minitest/UnspecifiedException:
239 Enabled: true
240
97241 Naming/FileName:
98242 Enabled: false
99243 Naming/HeredocDelimiterNaming:
100 Enabled: false
244 Exclude:
245 - test/**/*.rb
101246 Naming/MemoizedInstanceVariableName:
102247 Exclude:
248 - lib/jekyll/convertible.rb
249 - lib/jekyll/drops/site_drop.rb
250 - lib/jekyll/drops/unified_payload_drop.rb
103251 - lib/jekyll/page_without_a_file.rb
104 - lib/jekyll/drops/unified_payload_drop.rb
105 - lib/jekyll/drops/site_drop.rb
106 Naming/UncommunicativeMethodParamName:
107 AllowedNames:
108 - _
252
253 Performance/AncestorsInclude:
254 Enabled: false
255 Performance/ArraySemiInfiniteRangeSlice:
256 Enabled: true
257 Performance/BigDecimalWithNumericArgument:
258 Enabled: true
259 Performance/BlockGivenWithExplicitBlock:
260 Enabled: true
261 Performance/ChainArrayAllocation:
262 Enabled: true
263 Performance/CollectionLiteralInLoop:
264 Enabled: true
265 Performance/ConstantRegexp:
266 Enabled: true
267 Performance/MapCompact:
268 Enabled: true
269 Performance/MethodObjectAsBlock:
270 Enabled: true
271 Performance/RedundantEqualityComparisonBlock:
272 Enabled: false
273 Performance/RedundantSortBlock:
274 Enabled: true
275 Performance/RedundantSplitRegexpArgument:
276 Enabled: true
277 Performance/RedundantStringChars:
278 Enabled: true
279 Performance/ReverseFirst:
280 Enabled: true
281 Performance/SortReverse:
282 Enabled: false
283 Performance/Squeeze:
284 Enabled: true
285 Performance/StringIdentifierArgument:
286 Enabled: true
287 Performance/StringInclude:
288 Enabled: true
289 Exclude:
290 - lib/jekyll/utils/platforms.rb
291 Performance/Sum:
292 Enabled: true
293
294 Security/CompoundHash:
295 Enabled: true
296 Security/IoMethods:
297 Enabled: true
109298 Security/MarshalLoad:
110299 Exclude:
111300 - !ruby/regexp /test\/.*.rb$/
114303 Exclude:
115304 - !ruby/regexp /features\/.*.rb/
116305 - !ruby/regexp /test\/.*.rb$/
306
307 Style/AccessModifierDeclarations:
308 Enabled: false
309 Style/AccessorGrouping:
310 Enabled: true
117311 Style/Alias:
118 Enabled: false
312 EnforcedStyle: prefer_alias_method
119313 Style/AndOr:
120314 Severity: error
121 Style/BracesAroundHashParameters:
122 Enabled: false
315 Style/ArgumentsForwarding:
316 Enabled: false
317 Style/ArrayCoercion:
318 Enabled: true
319 Style/BisectedAttrAccessor:
320 Enabled: true
321 Style/CaseLikeIf:
322 Enabled: true
323 Style/StringChars:
324 Enabled: true
123325 Style/ClassAndModuleChildren:
124 Enabled: false
326 Exclude:
327 - test/**/*.rb
328 Style/ClassEqualityComparison:
329 Enabled: true
330 Style/CollectionCompact:
331 Enabled: true
332 Style/CombinableLoops:
333 Enabled: true
334 Style/DocumentDynamicEvalDefinition:
335 Enabled: true
336 Style/Documentation:
337 Enabled: false
338 Style/DoubleNegation:
339 Enabled: false
340 Style/EmptyHeredoc:
341 Enabled: true
342 Style/EndlessMethod:
343 Enabled: true
344 Style/ExplicitBlockArgument:
345 Enabled: false
346 Style/ExponentialNotation:
347 Enabled: true
348 Style/EnvHome:
349 Enabled: true
350 Style/FetchEnvVar:
351 Enabled: false
352 Style/FileRead:
353 Enabled: false
354 Style/FormatStringToken:
355 Exclude:
356 - lib/jekyll/utils/ansi.rb
357 - lib/jekyll/liquid_renderer/table.rb
358 - lib/jekyll/profiler.rb
125359 Style/FrozenStringLiteralComment:
126360 EnforcedStyle: always
127 Style/Documentation:
128 Enabled: false
129 Exclude:
130 - !ruby/regexp /features\/.*.rb$/
131 Style/DoubleNegation:
132 Enabled: false
133 Style/FormatStringToken:
134 Exclude:
135 - lib/jekyll/utils/ansi.rb
361 Style/FileWrite:
362 Enabled: true
363 Style/GlobalStdStream:
364 Enabled: true
136365 Style/GuardClause:
137366 Enabled: false
367 Style/HashAsLastArrayItem:
368 Enabled: true
369 Style/HashConversion:
370 Enabled: true
371 Style/HashEachMethods:
372 Enabled: true
373 Style/HashExcept:
374 Enabled: true
375 Style/HashLikeCase:
376 Enabled: true
138377 Style/HashSyntax:
139378 EnforcedStyle: hash_rockets
140379 Severity: error
141 Style/IfUnlessModifier:
142 Enabled: false
143 Style/InverseMethods:
144 Enabled: false
380 Style/HashTransformKeys:
381 Enabled: false
382 Style/HashTransformValues:
383 Enabled: true
384 Style/IfWithBooleanLiteralBranches:
385 Enabled: true
386 Style/KeywordParametersOrder:
387 Enabled: true
388 Style/MagicCommentFormat:
389 Enabled: true
390 Style/MapCompactWithConditionalBlock:
391 Enabled: true
392 Style/MapToHash:
393 Enabled: true
145394 Style/MixinUsage:
146395 Exclude:
147396 - test/helper.rb
149398 Enabled: false
150399 Style/MultilineTernaryOperator:
151400 Severity: error
401 Style/NegatedIfElseCondition:
402 Enabled: true
403 Style/NestedFileDirname:
404 Enabled: true
405 Style/NilLambda:
406 Enabled: true
407 Style/OptionalBooleanParameter:
408 Enabled: true
409 Exclude:
410 - lib/jekyll/log_adapter.rb
152411 Style/PercentLiteralDelimiters:
153412 PreferredDelimiters:
413 "%Q": "{}"
414 "%W": ()
154415 "%q": "{}"
155 "%Q": "{}"
156416 "%r": "!!"
157 "%s": "()"
158 "%w": "()"
159 "%W": "()"
160 "%x": "()"
161 Style/RedundantReturn:
162 Enabled: false
163 Style/RedundantSelf:
164 Enabled: false
417 "%s": ()
418 "%w": ()
419 "%x": ()
420 Style/QuotedSymbols:
421 Enabled: true
422 Style/RedundantArgument:
423 Enabled: true
424 Style/RedundantAssignment:
425 Enabled: true
426 Style/RedundantFetchBlock:
427 Enabled: false
428 Style/RedundantFileExtensionInRequire:
429 Enabled: true
430 Style/RedundantInitialize:
431 Enabled: true
432 Exclude:
433 - lib/jekyll/plugin.rb
434 Style/RedundantRegexpCharacterClass:
435 Enabled: true
436 Style/RedundantRegexpEscape:
437 Enabled: true
438 Style/RedundantSelfAssignment:
439 Enabled: true
440 Style/RedundantSelfAssignmentBranch:
441 Enabled: true
165442 Style/RegexpLiteral:
166443 EnforcedStyle: percent_r
167444 Style/RescueModifier:
168445 Enabled: false
446 Style/SafeNavigation:
447 Exclude:
448 - lib/jekyll/document.rb
169449 Style/SignalException:
170450 EnforcedStyle: only_raise
171 Style/SingleLineMethods:
172 Enabled: false
451 Style/SingleArgumentDig:
452 Enabled: true
453 Style/SlicingWithRange:
454 Enabled: false
455 Style/SoleNestedConditional:
456 Enabled: true
457 Style/StringConcatenation:
458 Enabled: true
459 Exclude:
460 - lib/jekyll/commands/*.rb
461 - test/**/*.rb
173462 Style/StringLiterals:
174463 EnforcedStyle: double_quotes
175464 Style/StringLiteralsInInterpolation:
176465 EnforcedStyle: double_quotes
466 Style/SwapValues:
467 Enabled: true
177468 Style/SymbolArray:
178 Enabled: false
469 EnforcedStyle: brackets
179470 Style/TrailingCommaInArrayLiteral:
180471 EnforcedStyleForMultiline: consistent_comma
181472 Style/TrailingCommaInHashLiteral:
0 # This configuration was generated by
1 # `rubocop --auto-gen-config --auto-gen-only-exclude`
2 # on 2022-04-06 10:48:47 UTC using RuboCop version 1.26.1.
3 # The point is for the user to remove these configuration records
4 # one by one as the offenses are removed from the code base.
5 # Note that changes in the inspected code, or installation of new
6 # versions of RuboCop, may require this file to be generated again.
7
8 # Offense count: 1
9 # This cop supports safe auto-correction (--auto-correct).
10 Performance/BindCall:
11 Exclude:
12 - 'test/helper.rb'
13
14 # Offense count: 1
15 Style/CombinableLoops:
16 Exclude:
17 - 'lib/jekyll/tags/post_url.rb'
18
19 # Offense count: 1
20 # Configuration parameters: AllowedMethods.
21 # AllowedMethods: respond_to_missing?
22 Style/OptionalBooleanParameter:
23 Exclude:
24 - 'lib/jekyll/log_adapter.rb'
+0
-59
.travis.yml less more
0 bundler_args: --without benchmark:development
1 script: script/cibuild
2 cache: bundler
3 language: ruby
4
5 rvm:
6 - &ruby1 2.7.1
7 - &ruby2 2.6.6
8 - &ruby3 2.5.8
9 - &jruby jruby-9.2.11.1
10
11 matrix:
12 include:
13 - rvm: *ruby1
14 env: TEST_SUITE=fmt
15 - rvm: *ruby1
16 env: TEST_SUITE=default-site
17 - rvm: *ruby1
18 env: TEST_SUITE=profile-docs
19 - rvm: *ruby1
20 env: ROUGE_VERSION=1.11.1 # runs everything with this version
21 - rvm: *ruby1
22 env: KRAMDOWN_VERSION=1.17.0 # runs everything with this version
23 exclude:
24 - rvm: *jruby
25 env: TEST_SUITE=cucumber
26
27 env:
28 matrix:
29 - TEST_SUITE=test
30 - TEST_SUITE=cucumber
31 branches:
32 only:
33 - master
34 - themes
35 - /.*-stable/
36
37 notifications:
38 slack:
39 secure: "\
40 dNdKk6nahNURIUbO3ULhA09/vTEQjK0fNbgjVjeYPEvROHgQBP1cIP3AJy8aWs8rl5Yyow4Y\
41 GEilNRzKPz18AsFptVXofpwyqcBxaCfmHP809NX5PHBaadydveLm+TNVao2XeLXSWu+HUNAY\
42 O1AanCUbJSEyJTju347xCBGzESU=\
43 "
44
45 addons:
46 code_climate:
47 repo_token:
48 secure: "\
49 mAuvDu+nrzB8dOaLqsublDGt423mGRyZYM3vsrXh4Tf1sT+L1PxsRzU4gLmcV27HtX2Oq9\
50 DA4vsRURfABU0fIhwYkQuZqEcA3d8TL36BZcGEshG6MQ2AmnYsmFiTcxqV5bmlElHEqQuT\
51 5SUFXLafgZPBnL0qDwujQcHukID41sE=\
52 "
53 # regular test configuration
54 after_success:
55 - bundle exec codeclimate-test-reporter
56
57 before_install:
58 - gem update --system || true
+0
-49
CODE_OF_CONDUCT.markdown less more
0 # Code of Conduct
1
2 As contributors and maintainers of this project, and in the interest of
3 fostering an open and welcoming community, we pledge to respect all people who
4 contribute through reporting issues, posting feature requests, updating
5 documentation, submitting pull requests or patches, and other activities.
6
7 We are committed to making participation in this project a harassment-free
8 experience for everyone, regardless of level of experience, gender, gender
9 identity and expression, sexual orientation, disability, personal appearance,
10 body size, race, ethnicity, age, religion, or nationality.
11
12 Examples of unacceptable behavior by participants include:
13
14 * The use of sexualized language or imagery
15 * Personal attacks
16 * Trolling or insulting/derogatory comments
17 * Public or private harassment
18 * Publishing other's private information, such as physical or electronic
19 addresses, without explicit permission
20 * Other unethical or unprofessional conduct
21
22 Project maintainers have the right and responsibility to remove, edit, or
23 reject comments, commits, code, wiki edits, issues, and other contributions
24 that are not aligned to this Code of Conduct, or to ban temporarily or
25 permanently any contributor for other behaviors that they deem inappropriate,
26 threatening, offensive, or harmful.
27
28 By adopting this Code of Conduct, project maintainers commit themselves to
29 fairly and consistently applying these principles to every aspect of managing
30 this project. Project maintainers who do not follow or enforce the Code of
31 Conduct may be permanently removed from the project team.
32
33 This Code of Conduct applies both within project spaces and in public spaces
34 when an individual is representing the project or its community.
35
36 Instances of abusive, harassing, or otherwise unacceptable behavior may be
37 reported by opening an issue or contacting a project maintainer. All complaints
38 will be reviewed and investigated and will result in a response that is deemed
39 necessary and appropriate to the circumstances. Maintainers are obligated to
40 maintain confidentiality with regard to the reporter of an incident.
41
42
43 This Code of Conduct is adapted from the [Contributor Covenant][homepage],
44 version 1.3.0, available at
45 [http://contributor-covenant.org/version/1/3/0/][version]
46
47 [homepage]: http://contributor-covenant.org
48 [version]: http://contributor-covenant.org/version/1/3/0/
0 FROM alpine
1
2 # Run locally: `earthly +all` to run full CI process
3 all:
4 BUILD --build-arg RUBY=3.0 +test
5 BUILD --build-arg RUBY=2.7 +test
6 BUILD --build-arg RUBY=2.5 +test
7 BUILD --build-arg RUBY=jruby:9.2.14.0 +test
8 BUILD style-check
9 BUILD profile-docs
10
11 # Run locally: `earthly +test`
12 # Run with specific version: `earthly --build-arg RUBY=2.5 +test`
13 test:
14 FROM +deps
15 RUN script/test
16 RUN script/cucumber
17 RUN script/default-site
18
19 style-check:
20 FROM +deps
21 RUN script/fmt
22
23 profile-docs:
24 FROM +deps
25 RUN bundle install --jobs 4
26 RUN script/profile-docs
27 RUN script/memprof
28
29 # Install dependencies and copy in source
30 # used in above steps
31 deps:
32 ARG RUBY=3.0
33 IF case $RUBY in jruby*) ;; *) false; esac
34 FROM $RUBY
35 ENV JRUBY_OPTS="--dev -J-XX:+TieredCompilation -J-XX:TieredStopAtLevel=1 -J-XX:CompileThreshold=10 -J-XX:ReservedCodeCacheSize=128M"
36 ELSE
37 FROM ruby:$RUBY
38 END
39 WORKDIR /src
40 RUN apt-get update && apt-get install nodejs dnsutils git make coreutils g++ build-essential -y
41 RUN gem install bundler
42 RUN gem install sassc -v '2.4.0' --source 'https://rubygems.org/'
43 COPY Gemfile .
44 COPY jekyll.gemspec .
45 COPY lib/jekyll/version.rb lib/jekyll/version.rb
46 COPY test test
47 RUN bundle install --jobs 4
48 COPY . .
22 source "https://rubygems.org"
33 gemspec :name => "jekyll"
44
5 gem "rake", "~> 12.0"
6
7 gem "rouge", ENV["ROUGE"] if ENV["ROUGE"]
8
9 # Dependency of jekyll-mentions. RubyGems in Ruby 2.1 doesn't shield us from this.
10 gem "activesupport", "~> 4.2", :groups => [:test_legacy, :site] if RUBY_VERSION < "2.2.2"
5 gem "rake", "~> 13.0"
116
127 group :development do
138 gem "launchy", "~> 2.3"
149 gem "pry"
1510
16 unless RUBY_ENGINE == "jruby"
17 gem "pry-byebug"
11 gem "pry-byebug" unless RUBY_ENGINE == "jruby"
12 end
13
14 #
15
16 group :test do
17 gem "cucumber", RUBY_VERSION >= "2.5" ? "~> 5.1.2" : "~> 4.1"
18 gem "httpclient"
19 gem "jekyll_test_plugin"
20 gem "jekyll_test_plugin_malicious"
21 gem "memory_profiler"
22 gem "nokogiri", "~> 1.7"
23 gem "rspec"
24 gem "rspec-mocks"
25 gem "rubocop", "~> 1.37.0"
26 gem "rubocop-minitest"
27 gem "rubocop-performance"
28 gem "rubocop-rake"
29 gem "rubocop-rspec"
30 gem "test-dependency-theme", :path => File.expand_path("test/fixtures/test-dependency-theme", __dir__)
31 gem "test-theme", :path => File.expand_path("test/fixtures/test-theme", __dir__)
32 gem "test-theme-skinny", :path => File.expand_path("test/fixtures/test-theme-skinny", __dir__)
33 gem "test-theme-symlink", :path => File.expand_path("test/fixtures/test-theme-symlink", __dir__)
34 gem "test-theme-w-empty-data", :path => File.expand_path("test/fixtures/test-theme-w-empty-data", __dir__)
35
36 if RUBY_ENGINE == "jruby"
37 gem "http_parser.rb", "~> 0.6.0"
38 gem "jruby-openssl"
1839 end
1940 end
2041
2142 #
2243
23 group :test do
24 gem "codeclimate-test-reporter", "~> 1.0.5"
25 gem "cucumber", RUBY_VERSION >= "2.2" ? "~> 3.0" : "3.0.1"
26 gem "httpclient"
27 gem "jekyll_test_plugin"
28 gem "jekyll_test_plugin_malicious"
29 # nokogiri v1.10 does not work with ruby 2.2 and below
30 gem "nokogiri", RUBY_VERSION >= "2.3" ? "~> 1.9" : "~> 1.9.0"
31 gem "rspec"
32 gem "rspec-mocks"
33 gem "rubocop", "~> 0.56.0"
34 gem "test-dependency-theme", :path => File.expand_path("test/fixtures/test-dependency-theme", __dir__)
35 gem "test-theme", :path => File.expand_path("test/fixtures/test-theme", __dir__)
36 gem "test-theme-skinny", :path => File.expand_path("test/fixtures/test-theme-skinny", __dir__)
37 gem "test-theme-symlink", :path => File.expand_path("test/fixtures/test-theme-symlink", __dir__)
38
39 gem "jruby-openssl", "0.10.1" if RUBY_ENGINE == "jruby"
40 end
41
42 #
43
4444 group :test_legacy do
45 if RUBY_PLATFORM =~ %r!cygwin! || RUBY_VERSION.start_with?("2.2")
46 gem "test-unit"
47 end
45 gem "test-unit" if RUBY_PLATFORM =~ %r!cygwin!
4846
4947 gem "minitest"
5048 gem "minitest-profile"
7371 gem "jekyll-gist"
7472 gem "jekyll-paginate"
7573 gem "jekyll-redirect-from"
74 gem "kramdown-syntax-coderay"
75 gem "matrix"
7676 gem "mime-types", "~> 3.0"
77 gem "rdoc", RUBY_VERSION >= "2.2.2" ? "~> 6.0" : "~> 5.1"
78 gem "tomlrb", "~> 1.2"
77 gem "rdoc", "~> 6.0"
78 gem "tomlrb"
7979
80 if ENV["KRAMDOWN_VERSION"].nil? || ENV["KRAMDOWN_VERSION"].to_i >= 2
81 gem "kramdown-syntax-coderay"
82 gem "kramdown-parser-gfm"
83 else
84 gem "coderay", "~> 1.0"
80 platforms :ruby, :mswin, :mingw, :x64_mingw do
81 gem "classifier-reborn", "~> 2.2"
82 gem "liquid-c", "~> 4.0"
83 gem "yajl-ruby", "~> 1.4"
8584 end
8685
87 platform :ruby, :mswin, :mingw, :x64_mingw do
88 gem "classifier-reborn", "~> 2.2.0"
89 gem "liquid-c", "~> 3.0"
90 gem "pygments.rb", "~> 1.0"
91 gem "rdiscount", "~> 2.0"
92 gem "redcarpet", "~> 3.2", ">= 3.2.3"
93 gem "yajl-ruby", "~> 1.3.1"
86 # Windows and JRuby does not include zoneinfo files, so bundle the tzinfo-data gem
87 # and associated library
88 platforms :jruby, :mswin, :mingw, :x64_mingw do
89 gem "tzinfo", ENV["TZINFO_VERSION"] if ENV["TZINFO_VERSION"]
90 gem "tzinfo-data"
9491 end
95
96 # Windows does not include zoneinfo files, so bundle the tzinfo-data gem
97 gem "tzinfo-data", :platforms => [:mingw, :mswin, :x64_mingw, :jruby]
9892 end
9993
10094 #
10195
10296 group :site do
103 if ENV["PROOF"]
104 gem "html-proofer", "~> 3.4"
105 end
97 gem "html-proofer", "~> 3.4" if ENV["PROOF"]
10698
10799 gem "jekyll-avatar"
108100 gem "jekyll-mentions"
0 ### HEAD
1
2 ### Minor Enhancements
3
4 * Allow use of kramdown v2 (#8322)
5 * Add default language for kramdown syntax highlighting (#8325)
0 ## 4.3.1 / 2022-10-26
1
2 ### Bug Fixes
3
4 * Respect user-defined name attribute in documents (#9167)
5 * Revert &#34;Incrementally rebuild when a data file is changed&#34; (#9170)
6
7 ### Documentation
8
9 * Release post for v4.3.1 (#9171)
10
11 ## 4.3.0 / 2022-10-20
12
13 ### Minor Enhancements
14
15 * Add webrick as a dependency (#8524)
16 * Regenerate supported mime types (#8542)
17 * Update include tag to be more permissive (#8618)
18 * Optimize `Jekyll::Utils.parse_date` (#8425)
19 * Update rubocop from 1.12 to 1.18 and min ruby from 2.4 to 2.5 (#8741)
20 * Always hide cache-dir contents from Git (#8798)
21 * Remove the warning about auto-regeneration on Windows (#8821)
22 * Propagate _data folder from theme (#8815)
23 * Support both tzinfo v1 and v2 along with non-half hour offsets. (#8880)
24 * Run vendor-mimes to update mime.types (#8940)
25 * Expose collection static files via `site.static_files` (#8961)
26 * Expose `basename` from `document.rb` as `name` to Liquid templates (#8761)
27 * Allow Configurable Converters on CSV (#8858)
28 * Introduce `theme` drop to expose theme-gem details (#9129)
29 * Relax version constraint to allow Rouge 4.x (#9134)
30 * Incrementally rebuild when a data file is changed (#8771)
31 * Support jekyll-sass-converter 3.x (#9132)
32
33 ### Bug Fixes
34
35 * fix: pin rubocop to 1.12 due to error with ruby 2.4 (#8651)
36 * Load Jekyll plugins from BUNDLE_GEMFILE location (#8585)
37 * fix(security): CVE-2021-28834 (#8680)
38 * Inject livereload script using `location.protocol` instead of `http:` (#8718)
39 * Respect collections_dir config within include tag (#8756)
40 * Fix regression in Convertible module from v4.2.0 (#8786)
41 * Revert #7253: &#34;Don&#39;t reset site.url to localhost:4000 by default&#34; (#8620)
42 * Improve readability of CI logs (#8877)
43 * Fix deprecation message for missing doc method (#8960)
44 * Fix response header for content served via `jekyll serve` (#8965)
45 * Trigger livereload in sites without pages (#8337)
46 * Only enable BOM encoding option on UTF encodings (#8363)
47 * Ensure theme config is a `Jekyll::Configuration` object (#8988)
48 * Remove misleading totals row from `--profile` table (#9039)
49 * Unlock Psych dependency (#9135)
50 * Fix false positive conflicts for static files in a collection (#9141)
51
52 ### Development Fixes
53
54 * style: enable new cops (#8538)
55 * Allow dependabot to keep github actions up-to-date (#8540)
56 * Update actions/cache requirement to v2.1.3 (#8543)
57 * Pin rubocop version (#8564)
58 * style: add rubocop 1.9 cops (#8567)
59 * Cross Version Testing Locally and Faster CI (#8610)
60 * Use official Ruby setup GH action (#8614)
61 * Spell check action for markdown documentation (#8675)
62 * Update expect to cover docs/_posts (#8677)
63 * Bump check-spelling/check-spelling from 0.0.18 to 0.0.19 (#8740)
64 * Enable Rubocop accessor grouping, fix existing offenses (#8293)
65 * Tags:Highlight: Decomposed HTMLLegacy formatter (#8623)
66 * Relax Rubocop Dependency (#8831)
67 * Add a workflow to build gems consistently (#8830)
68 * Fix random test failures in TestExcerpt #to_liquid (#8884)
69 * Lock gem `psych` to `v3.x` (#8918)
70 * Fix typo in Bug Report template (#8951)
71 * Check symlink outside site_source without Pathutil (#9015)
72 * Stop testing with Rubies older than 2.7 on non-Windows (#8955)
73 * Bump actions/checkout from 2 to 3 (#8986)
74 * Remove git.io shortlinks from repo (#9045)
75 * Bump rubocop to 1.32 (#9093)
76 * Bump RuboCop to `1.36.x` (#9125)
77 * Use check-spelling/check-spelling@v0.0.20 (#9111)
78 * Disable pending cops when running rubocop (#9136)
79 * Relax RDoc version dependency (#9142)
80
81 ### Documentation
82
83 * typo - do instead of don&#39;t (#8518)
84 * Document support for TSV files consistently (#8488)
85 * Add a disclaimer to tutorials involving Ruby code (#8525)
86 * Improve documentation on developing generators (#8527)
87 * Fixes typo in layouts_dir documentation (#8532)
88 * Fix i.e. typos in collections.md (#8529)
89 * Remove GitHub Pages content which is in GitHub docs (#8533)
90 * Step By Step Instructions Review (#8399)
91 * Fix typo in migrating from 3.0 to 4.0 page (#8572)
92 * Fix for important missing step in macOS Installation Docs: Add the Homebrew gems directory to the PATH (#8496)
93 * Use latest Jekyll-action configuration (#8579)
94 * docs: troubleshoot macOS with ARM64 architecture (#8560)
95 * docs: add overview of .jekyll-cache dir (#8648)
96 * docs: clarify where .jekyll-metadata comes from (#8646)
97 * Razorops CI/CD added (#8656)
98 * Specify default port and host for serve commands in docs (#8624)
99 * Update third-party.md (#8652)
100 * Add documentation for Sass configuration options (#8587)
101 * Add formcarry to forms section (#8471)
102 * Add step to set SDKROOT (#8478)
103 * Improve the &#34;Markdown Options&#34; Docs (#8681)
104 * Add &#39;webrick&#39; warning note to &#34;Quickstart&#34; Docs (#8727)
105 * Update windows.md (#8701)
106 * IRC networks - Libera, Freenode (#8706)
107 * Improve GitHub Flavored Markdown Docs (#8684)
108 * Fixing URL in MacOS install for rbenv-doctor (#8693)
109 * Fix adjective in `troubleshooting.md` document (#8777)
110 * Goodbye Frank. We&#39;ll miss you. 💔 (#8807)
111 * Update index.html: Grammar fix. (#8803)
112 * Prefer Libera. Remove Freenode. (#8811)
113 * Update feature_request.md (#8797)
114 * Remove AWS Amplify from the showcase (#8812)
115 * Move Frank to Emeritus Core Team Members (#8813)
116 * Release post for v4.2.1 (#8818)
117 * Update CircleCI example (#8829)
118 * Fix typo (#8835)
119 * Added docs for running locally (#8852)
120 * Linting README.markdown (#8900)
121 * Remove text on GITHUB_TOKEN which is now built-in (#8907)
122 * Add Security Policy document (#8823)
123 * Manage repository meta documents consistently (#8908)
124 * docs: add Layer0 deployment guide (#8915)
125 * docs: Update README generated by `jekyll new-theme` (#8919)
126 * Update resources.md (#8925)
127 * Rewrite documentation on installing plugins (#8921)
128 * Improve maintainers guide on releasing a new version (#8928)
129 * Fix link for &#34;CloudSh&#34; (#8934)
130 * Recommend using `actions/cache` in GitHub Actions documentation (#8948)
131 * Remove references to EOL hakiri.io service (#8946)
132 * Release post for v4.2.2 (#8982)
133 * Document releasing off `*-stable` branches (#8984)
134 * Update document by fix yaml syntax error (#8991)
135 * Enhance option&#39;s case for Jekyll configuration (#8992)
136 * Fix typo in `_docs/deployment/manual.md` (#8997)
137 * Add quiet/verbose options (#8996)
138 * Update README.markdown re IRC Pointer (#9005)
139 * Remove Aerobatic (#9007)
140 * Add Jekyll 3.9.2 release post to &#39;master&#39; branch (#9013)
141 * Simplify macOS installation docs (#8993)
142 * Improve document about Github Actions section (#8853)
143 * Update permalinks.md (#9017)
144 * Add clarity to docs on permalinks placeholders and built-ins (#8995)
145 * Remove Ionic Framework site from showcase (#9057)
146 * Windows: describe which option to choose (#9049)
147 * Improve links (http -&gt; https) (#9064)
148 * Update ruby version for macos guide (#9086)
149 * Update posts.md (#9151)
150 * Release post for v4.3.0 (#9157)
151
152 ### Site Enhancements
153
154 * Improvements to CSS (#7834)
155 * Slightly update lang `sh` code-block styling (#8857)
156
157 ## 4.2.2 / 2022-03-03
158
159 ### Bug Fixes
160
161 * Lock `http_parser.rb` gem to `v0.6.x` on JRuby.
162
163 ### Development Fixes
164
165 * Backport #8830 for v4.2.x: Add a workflow to build gems consistently (#8869)
166 * Lock `rubocop-performance` to `v1.11.x`.
167
168 ## 4.2.1 / 2021-09-27
169
170 ### Bug Fixes
171
172 * Backport #8620 for v4.2.x: Revert #7253: "Don't reset site.url to localhost:4000 by default" (#8808)
173 * Backport #8756 for v4.2.x: Respect collections_dir config within include tag (#8794)
174 * Backport #8786 for v4.2.x: Fix regression in Convertible module from v4.2.0 (#8793)
175
176 ## 4.2.0 / 2020-12-14
177
178 ### Minor Enhancements
179
180 * Warn on command-line with permalink conflict (#8342)
181 * Suppress warning issued for redirect pages (#8347)
182 * Enhance detection of conflicting destination URLs (#8459)
183 * Add `:post_convert` hook to modify HTML content before layout (#8368)
184 * Allow triggering `:post_convert` events atomically (#8465)
185 * Debug reading Page and Layout objects (#8100)
186 * Do not reset `site.url` to `http://localhost:4000` by default (#7253)
187 * Add custom debug strings for Jekyll objects (#8473)
188 * Debug reading data files in a site (#8481)
189
190 ### Bug Fixes
191
192 * Replace nested conditional with guard clauses (#8294)
193 * Fix: security bump (#8349)
194 * Fix path matching regex in post_url Liquid tag (#8375)
195 * Enable `Performance/ChainArrayAllocation` cop (#8404)
196 * Enable Lint/NoReturnInBeginEndBlocks Cop (#8457)
197 * Generate items from `site.include` list only once (#8463)
198 * Explicitly return nil after site process phase (#8472)
199
200 ### Optimization Fixes
201
202 * Implement custom delegators for drop methods (#8183)
203 * Handle `nil` argument to `Jekyll.sanitized_path` (#8415)
204 * Cache `Jekyll.sanitized_path` (#8424)
205 * Memoize array of drop getter method names (#8421)
206 * Reduce string allocations from the `link` tag (#8387)
207 * Optimize parsing of parameters in `include` tag (#8192)
208 * Stash documents `write?` attribute in a variable (#8389)
209 * Reduce string allocations from generating doc URLs (#8392)
210 * Check if site is in incremental mode optimally (#8401)
211 * Utilize flexibility of `Site#in_dest_dir` (#8403)
212 * Reduce allocations from rendering item as liquid (#8406)
213 * Compute relative_path of pages using PathManager (#8408)
214 * Reduce allocation from `normalize_whitespace` filter (#8400)
215 * Use `Regexp#match?` when `MatchData` is not required (#8427)
216 * Check default front matter scope against symbols (#8393)
217 * Stash frequently used `Drop` setter keys for reuse (#8394)
218 * Memoize defaults computed for Convertibles (#8451)
219 * Reduce array allocations from merging categories (#8453)
220 * Memoize destination of pages, documents and staticfiles (#8458)
221 * Reduce allocations from computing item property (#8485)
222 * Optimize `Page#dir` with a private method (#8489)
223 * Stash attribute hash for Liquid computed for pages (#8497)
224
225 ### Development Fixes
226
227 * Update cucumber gem to version 4.1 (#8278)
228 * Move permalink styles data to constant (#8282)
229 * Update rubocop gem to 0.87.1 (#8287)
230 * Update RuboCop to-do file (#8296)
231 * Fix `rake console` generating LoadError (#8312)
232 * Configure Performance cops (#8369)
233 * Update rubocop gem to 0.90.0 (#8313)
234 * Refactor `Jekyll::Utils::Platforms` (#7236)
235 * Bump RuboCop to v0.91.x (#8391)
236 * Add workflow to build and profile third-party repo (#8398)
237 * Bump RuboCop to v0.92.x
238 * Update cucumber gem version to 5.1.2 (#8413)
239 * Fix test suite compatibility with JRuby (#8418)
240 * chore(deps): bump Rubocop to 0.93.0 (#8430)
241 * Use Ruby 2.7.1 in GitHub Actions (#8444)
242 * Test that Liquid expressions are not deeply evaled (#8292)
243 * Test rendering arbitrary Liquid variables by default (#7414)
244 * Migrate TravisCI jobs to GitHub Actions (#8492)
245
246 ### Documentation
247
248 * Update pointer to special permalink variables for collections (#8274)
249 * Fix special treatment for &#39;page 1&#39; in docs of pagination (#8230)
250 * Add Formcake to forms section (#8283)
251 * Add a note on the rendering process in the docs (#8291)
252 * Add refactoring type to PULL_REQUEST_TEMPLATE (#8297)
253 * Update resources.md (#7864)
254 * Extra apostrophes in an URL (#8319)
255 * Clarify target of subordinate clause (#8320)
256 * Cherry-pick commits from conflicting branch `docs-40`
257 * Update documentation on third party site (#8352)
258 * Update default.md with info requested in #8314 (#8353)
259 * Clarify description of `safe` option (#8354)
260 * Simplifying the Git post-receive hook-example (#8358)
261 * Add missing doc for build and serve commands (#8365)
262 * Docs Review: Getting Started (#8372)
263 * Add note about rebooting system after installation (#8359)
264 * Use data file to render table at `/docs/configuration/options/#global-configuration` (#8377)
265 * Use data file(s) to render table(s) at `/docs/configuration/options/` (#8380)
266 * Improve maintainability of config option data (#8383)
267 * Remove CircleCI v1 docs (#8410)
268 * Remove `NOKOGIRI_USE_SYSTEM_LIBRARIES` from Travis CI docs (#8409)
269 * Add links to all Jekyll themes on GitHub tagged with #jekyll-theme (#8447)
270 * Document initializing project Gemfile from scratch (#8450)
271 * Document installation of additional dependencies for installing Jekyll on Fedora (#8456)
272 * Improve documentation on Hooks in Jekyll (#8467)
273 * Build docs site with GitHub Actions (#8201)
274 * Add link to Assets page from `_sass` section in `_docs/structure.md` (#8486)
275
276 ### Site Enhancements
277
278 * Fix rendering of *showcase* images (#8504)
279
280 ## 4.1.1 / 2020-06-24
281
282 ### Bug Fixes
283
284 * Disable page excerpts by default (#8222)
285 * Revert introduction of PageDrop (#8221)
286 * Don&#39;t generate excerpts for non-html pages (#8234)
287 * Make page excerpts consistent with doc excerpts (#8236)
288
289 ### Documentation
290
291 * Replace deprecated &#39;show&#39; command with &#39;info&#39; (#8235)
292 * Change name to ▲Vercel (#8247)
293 * Add language and examples to describe how to use the configuration op… (#8249)
294 * Fix missing yaml front matter colon and adjust/add clarifying language. (#8250)
295 * correct typo (#8261)
296 * Allow hyperlinks to specific filter documentation (#8231)
297 * Update link to Netlify step-by-step guide (#8264)
298 * Fix grammar in documentation section (#8265)
299
300 ### Site Enhancements
301
302 * Including correct Sketch website (#8241)
303 * Release post for v4.1.1 (#8243)
304
305 ### Development Fixes
306
307 * Bump RuboCop to v0.85.x (#8223)
308 * Expect drive letter only on vanilla windows (#8227)
309
310 ## 4.1.0 / 2020-05-27
311
312 ### Bug Fixes
313
314 * Memoize `absolute_url` and `relative_url` filters (#7793)
315 * Fix documentation comment for `Jekyll::Converters::Identity` (#7883)
316 * Optimize `Jekyll::Filters#item_property` (#7696)
317 * Allow multiple binary operators in `where_exp` filter (#8047)
318 * Fix documents custom-ordering logic (#8028)
319 * Use `layout.path` when rendering the Liquid layout (#8069)
320 * Reduce array allocations from `StaticFile#path` (#8083)
321 * Simplify `Jekyll::Renderer#validate_layout` (#8064)
322 * Add static file's basename to its `url_placeholder` (#7908)
323 * Clear cached Liquid template scope before render (#7967)
324 * Cache `URLFilter` results of string inputs per site (#7990)
325 * Use `platforms` instead of `install_if` in Gemfile (#8140)
326 * Config include trailing slash (#8113)
327 * Improve path normalization in liquid_renderer (#8075)
328 * Switch slugify regex to support more Unicode character groups (#8167)
329 * Check if entry is a directory once per enumerator (#8177)
330 * Filter out exclusively excluded entries sooner (#7482)
331 * Return `relative_url` if site.url is an empty string (#7988)
332 * Configure kramdown toc_levels as array by default (#8015)
333 * Reduce `Pathname` objects from front matter defaults (#8067)
334 * Simplify `Jekyll::Hooks.trigger` logic (#8044)
335 * Quicker categories for documents without superdirs (#7987)
336 * Reduce `Jekyll::Renderer` instances during a build (#7570)
337 * Escape regex characters in paths to match (#8138)
338 * Provide invokables for common drop query keys (#8165)
339 * Optimize path sanitization of default front matter (#8154)
340 * Initialize static files' data hash only if needed (#8188)
341 * Initialize include-files as Jekyll objects (#8158)
342
343 ### Minor Enhancements
344
345 * serve: add support for ECC certificates (#7768)
346 * Update `item_property` to recognize integers (#7878)
347 * Include `_config.yml` in a new theme's gemspec (#7865)
348 * Add an option to easily disable disk-cache (#7928)
349 * Optimize markdown parsing with Kramdown by reusing the options and parser objects (#8013)
350 * Add `PageDrop` to provide Liquid templates with data (#7992)
351 * Optimize `Kramdown::JekyllDocument#to_html` calls (#8041)
352 * Configure default language for syntax-highlighting (#8035)
353 * Remove dev dependencies from new theme-gem gemspec (#8042)
354 * Allow disabling import of theme configuration (#8131)
355 * Allow excerpts to be generated for `Page` objects (#7642)
356 * Profile various stages of a site's build process (#6760)
357 * Add find filters to optimize where-first chains (#8171)
358 * Make `number_of_words` filter respect CJK characters (#7813)
359 * Allow extensionless document in a strict site (#7950)
360 * Add `:slugified_categories` URL placeholder (#8094)
361
362 ### Documentation
363
364 * Add dropped 'title: Staff' to the code (#7805)
365 * Clarify docs for static files in collection (#7812)
366 * Rephrase the CircleCI v2 section (#7815)
367 * Update old GitHub wiki URL with new one (#7823)
368 * Update JekyllConf page with 2019 talks (#7826)
369 * link for memberships (#7825)
370 * Doc: minor fix, should be greater or equal to min version (#7856)
371 * Update third-party.md - Fix broken link (#7857)
372 * clarify _config.yml/collections type (#7873)
373 * Replace backticks with HTML tags in data file (#7879)
374 * add new theme source (#7875)
375 * fixed grammatical error (it&#39;s --&gt; its) (#7887)
376 * Docs: Clarify organizing pages into subfolders (#7896)
377 * Disambiguate the placeholder of permalink (#7906)
378 * docs: add short serve command for livereload (#7919)
379 * docs: add options for watch and force polling (#7918)
380 * add install instructions for ArchLinux and openSUSE (#7920)
381 * Improve index page of Jekyll documentation (#7926)
382 * Include path in `jekyll new` commands (Usage docs) (#7931)
383 * Change `affect` to `effect` in the collections docs (#7937)
384 * Changed deprecated command in themes documentation (#7941)
385 * Adds some documentation for the `:clean`, `:on_obsolete` hook (#7954)
386 * docs: fix broken link (#7955)
387 * Corrected typo (#7975)
388 * docs: remove watch option in config (#7940)
389 * Correct a sentence in the documentation (#7978)
390 * Fix YAML representation of `group_by` result (#7979)
391 * Move `--baseurl` to build command options (#7985)
392 * Correct documentation of filters (#7989)
393 * Document sorting two documents by their `date` (#7870)
394 * Fix English grammar error (#7994)
395 * Update 03-front-matter.md (#7996)
396 * Add Kentico Kontent CMS integration to resources (#8000)
397 * Update 07-assets.md (#7413)
398 * Fix file references in Step by Step Tutorial's Assets step (#8007)
399 * docs: improve highlighting of code blocks (#8017)
400 * remove leading slash from Sass file location (#8021)
401 * [Docs] Fix asset link ref in step-by-step tutorial (#8026)
402 * Corrected command to modify PATH (#8029)
403 * Corrected command to modify PATH (#8030)
404 * Docs: Render full contents of just the latest post (#8032)
405 * docs: improvements for note boxes (#8037)
406 * Non-deprecated `vendor/bundle` path configuration (#8048)
407 * Update 09-collections.md (#8060)
408 * Remove extra paragraph tags (#8063)
409 * Add default front matter for tutorials collection (#8081)
410 * Create CSV to table tutorial (#8090)
411 * Add version badge for Custom Sorting of Documents (#8098)
412 * Docs: Fix grammar in `_docs/front-matter.md` (#8097)
413 * Update variables.md (#8106)
414 * Add help about Gentoo/Linux (#8002)
415 * Update documentation on third party site (#8122)
416 * Added Clear Linux (#8132)
417 * Added note about OS specific installation instructions. (#8135)
418 * Fix broken URL in the Resources Page on the Documentation Site (#8136)
419 * Docs: Deploy Jekyll site with GitHub Actions (#8119)
420 * Clarify `bundle config` in Bundler tutorial (#8150)
421 * docs: update your-first-plugin.md (#8147)
422 * Fix typo in documentation on GitHub Actions (#8162)
423 * Ease discovery of CLI commands (in their entirety) (#8178)
424 * Remove `sudo` from Travis CI tutorial (#8187)
425 * Add GitLab Pages to 3rd party list (#8191)
426 * docs: add 21yunbox for deployment (#8193)
427 * Improve documentation on tags and categories (#8196)
428
429 ### Development Fixes
430
431 * Ci/GitHub actions (#7822)
432 * Rubocop version upgrade (#7846)
433 * Split action steps to avoid using `&&` on Windows (#7885)
434 * Upgrade rake to use version 13 (#7910)
435 * Update dependency constraint to allow RuboCop v0.76 (#7893)
436 * Use bash executable consistently (#7909)
437 * Test with JRuby 9.2.9.0 (#7779)
438 * Bump RuboCop to v0.79.x (#7970)
439 * Remove post-install message from gemspec (#7974)
440 * Attain Ruby 3.0 compatibility (#7948)
441 * Test `where` filter handling numeric property values (#7821)
442 * chore(deps): rubocop 0.80.0 (#8012)
443 * Update unit tests for Kramdown-based converter (#8014)
444 * Add Visual Studio Code Development Container (#8016)
445 * chore: simplify require for `Jekyll::VERSION` (#8057)
446 * Remove version-constraint relaxation for i18n gem (#8055)
447 * Mirror `spec.homepage` as `metadata["homepage_uri"]` (#8056)
448 * Bump Ruby versions on Travis builds (#8088)
449 * chore(ci): cache dependencies (#8168)
450
451 ### Site Enhancements
452
453 * Optimize rendering of the documentation site (#8020)
454 * Utilize `relative_url` filter in documentation site (#8089)
455 * Render tutorial metadata in documentation site (#8092)
456 * Improve syntax-highlighting in documentation site (#8079)
457 * Site: Filter through just the *docs* collection (#8170)
458
459 ## 4.0.1 / 2020-05-08
460
461 ### Bug Fixes
462
463 * Prevent console warning with Ruby 2.7 (#8124)
464 * Clear cached Liquid template scope before render (#8141)
465 * Add static file's basename to its url_placeholder (#8142)
466 * Update item_property to recognize integers (#8160)
467
468 ### Development Fixes
469
470 * Fix Kramdown converter based tests for v4.0.x (#8143)
471
472 ## 3.9.2 / 2022-03-27
473
474 ### Bug Fixes
475
476 * Lock `http_parser.rb` gem to `v0.6.x` on JRuby (#8943)
477 * Backport #8756 for v3.9.x: Respect collections_dir config within include tag (#8795)
478 * Backport #8965 for v3.9.x: Fix response header for content served via `jekyll serve` (#8976)
479
480 ### Development Fixes
481
482 * Update and fix CI for `3.9-stable` on Ruby 3.x (#8942)
483 * Fix CI for commits to `3.9-stable` branch (#8788)
484
485 ## 3.9.1 / 2021-04-08
486
487 ### Bug Fixes
488
489 * Backport #8618 for v3.9.x: Update include tag to be more permissive (#8629)
490
491 ## 3.9.0 / 2020-08-05
492
493 ### Minor Enhancements
494
495 * Allow use of kramdown v2 (#8322)
496 * Add default language for kramdown syntax highlighting (#8325)
6497
7498 ## 3.8.7 / 2020-05-08
8499
9500 ### Bug Fixes
10501
11502 * Prevent console warnings with Ruby 2.7 (#8125)
503
504 ## 4.0.0 / 2019-08-19
505
506 ### Major Enhancements
507
508 * Drop ruby 2.3 (#7454)
509 * Drop support for Ruby 2.1 and 2.2 (#6560)
510 * Drop support for older versions of Rouge (#6978)
511 * Drop support for pygments as syntax-highlighter (#7118)
512 * Drop support for Redcarpet (#6987)
513 * Drop support for rdiscount (#6988)
514 * Drop support for `jekyll-watch-1.4.0` and older (#7287)
515 * Incorporate `relative_url` filter in `link` tag (#6727)
516 * Upgrade kramdown dependency to v2.x (#7492)
517 * Upgrade jekyll-sass-converter to v2.x - Sassc + sourcemaps (#7778)
518 * Upgrade i18n to v1.x (#6931)
519 * Add `Jekyll::Cache` class to handle caching on disk (#7169)
520 * Cache converted markdown (#7159)
521 * Cache: Do not dump undumpable objects (#7190)
522 * Cache matched defaults sets for given parameters (#6888)
523 * Ignore cache directory (#7184)
524 * Add `Site#in_cache_dir` helper method (#7160)
525 * Remove &#39;cache_dir&#39; during `jekyll clean` (#7158)
526 * Cache parsed Liquid templates in memory (#7136)
527 * Only read layouts from source_dir or theme_dir (#6788)
528 * Allow custom sorting of collection documents (#7427)
529 * Always exclude certain paths from being processed (#7188)
530 * Remove Jekyll::Utils#strip_heredoc in favor of a Ruby &gt; 2.3 built in (#7584)
531 * Incorporate `relative_url` within `post_url` tag (#7589)
532 * Remove patch to modify config for kramdown (#7699)
533
534 ### Minor Enhancements
535
536 * Enhance `--blank` scaffolding (#7310)
537 * Use `jekyll-compose` if installed (#6932)
538 * Disable Liquid via front matter (#6824)
539 * Configure cache_dir (#7232)
540 * ISO week date drops (#5981)
541 * Fix custom 404 page for GitHub pages (#7132)
542 * Load config file from within current theme-gem (#7304)
543 * Suggest re-running command with `--trace` on fail (#6551)
544 * Support for binary operators in where_exp filter (#6998)
545 * Automatically load `_config.toml` (#7299)
546 * Add vendor folder to a newly installed site&#39;s .gitignore (#6968)
547 * Output Jekyll Version while debugging (#7173)
548 * Memoize computing excerpt&#39;s relative_path (#6951)
549 * Skip processing posts that can not be read (#7302)
550 * Memoize the return value of Site#documents (#7273)
551 * Cache globbed paths in front matter defaults (#7345)
552 * Cache computed item property (#7301)
553 * Cleanup Markdown converter (#7519)
554 * Do not process Liquid in post excerpt when disabled in front matter (#7146)
555 * Liquefied link tag (#6269)
556 * Update item_property to return numbers as numbers instead of strings (#6608)
557 * Use `.markdown` extension for page templates (#7126)
558 * Add support for `*.xhtml` files (#6854)
559 * Allow i18n v0.9.5 and higher (#7044)
560 * Ignore permission error of /proc/version (#7267)
561 * Strip extra slashes via `Jekyll.sanitized_path` (#7182)
562 * Site template: remove default config for markdown (#7285)
563 * Add a custom inspect string for StaticFile objects (#7422)
564 * Remind user to include gem in the Gemfile on error (#7476)
565 * Search Front matter defaults for Page objects with relative_path (#7261)
566 * Lock use of `tzinfo` gem to v1.x (#7521, #7562)
567 * Utilize absolute paths of user-provided file paths (#7450)
568 * Detect `nil` and empty values in objects with `where` filter (#7580)
569 * Initialize mutations for Drops only if necessary (#7657)
570 * Reduce Array allocations via Jekyll::Cleaner (#7659)
571 * Encode and unencode urls only as required (#7654)
572 * Reduce string allocations with better alternatives (#7643)
573 * Reduce allocations from Jekyll::Document instances (#7625)
574 * Add `type` attribute to Document instances (#7406)
575 * Reduce allocations from where-filter (#7653)
576 * Memoize SiteDrop#documents to reduce allocations (#7697)
577 * Add PathManager class to cache interim paths (#7732)
578 * Remove warnings and fixes for deprecated config (#7440)
579 * Delegate --profile tabulation to `terminal-table` (#7627)
580
581 ### Bug Fixes
582
583 * Security: fix `include` bypass of `EntryFilter#filter` symlink check (#7226)
584 * Theme gems: ensure directories aren&#39;t symlinks (#7419)
585 * Add call to unused method `validate_options` in `commands/serve.rb` (#7122)
586 * Check if scope applies to type before given path (#7263)
587 * Document two methods, simplify one of the methods (#7270)
588 * Check key in collections only if it isn&#39;t &#34;posts&#34; (#7277)
589 * Interpolate Jekyll::Page subclass on inspection (#7203)
590 * Measure the no. of times a template gets rendered (#7316)
591 * Reduce array traversal in Jekyll::Reader (#7157)
592 * Re-implement handling Liquid blocks in excerpts (#7250)
593 * Documents should be able to render their date (#7404)
594 * Fix Interpreter warning from Jekyll::Renderer (#7448)
595 * Loggers should accept both numbers and symbols (#6967)
596 * Replace regex arg to :gsub with a string arg (#7189)
597 * Dont write static files from unrendered collection (#7410)
598 * Excerpt handling of custom and intermediate tags (#7382)
599 * Change future post loglevel to warn to help user narrow down issues (#7527)
600 * Handle files with trailing dots in their basename (#7315)
601 * Fix unnecessary allocations via StaticFileReader (#7572)
602 * Don&#39;t check if site URL is absolute if it is nil (#7498)
603 * Avoid unnecessary duplication of pages array (#7272)
604 * Memoize Site#post_attr_hash (#7276)
605 * Memoize Document#excerpt_separator (#7569)
606 * Optimize Document::DATE_FILENAME_MATCHER to match valid filenames (#7292)
607 * Escape valid special chars in a site&#39;s path name (#7568)
608 * Replace `name` in Page#inspect with relative_path (#7434)
609 * Log a warning when the slug is empty (#7357)
610 * Push Markdown link refs to excerpt only as required (#7577)
611 * Fix broken include_relative usage in excerpt (#7633)
612 * Initialize and reset glob_cache only as necessary (#7658)
613 * Revert memoizing Site#docs_to_write and #documents (#7684)
614 * Backport #7684 for v3.8.x: Revert memoizing Site#docs_to_write and refactor #documents (#7689)
615 * Backport #7213 and #7633 for v3.8.x: Fix broken include_relative usage in excerpt (#7690)
616 * Don&#39;t read symlinks in site.include in safe mode (#7711)
617 * Replace `String#=~` with `String#match?` (#7723)
618 * Update log output for an invalid theme directory (#7679)
619 * Remove configuration of theme sass files from Core (#7290)
620 * Actually conditionally include liquid-c (#7792)
621 * Test number_like regex on stringified property (#7788)
622
623 ### Development Fixes
624
625 * Upgrade liquid-c to v4.0 (#7375)
626 * Bump RuboCop to v0.71.0 (#7687)
627 * Target Ruby 2.4 syntax (#7583)
628 * Fix: RuboCop offenses (#7769)
629 * Use communicative method parameters (#7566)
630 * Scan `assert_equal` methods and rectify any offenses with a custom RuboCop cop (#7130)
631 * CI: Test with Ruby 2.6 (#7438)
632 * CI: Test with Ruby 2.6 on AppVeyor (#7518)
633 * CI: Update RuboCop config (#7050)
634 * CI: Add a script to profile docs (#7540)
635 * CI(Appveyor): shallow clone with 5 last commits (#7312)
636 * CI: Test with oldest and latest Ruby only (#7412)
637 * CI: Update excludes for CodeClimate Analyses (#7365)
638 * CI: Lock Travis to Bundler-1.16.2 (#7144)
639 * CI: Bump tested version of JRuby to 9.2.7.0 (#7612)
640 * CI: Do not install docs on updating gems on Travis (#7706)
641 * Update gemspec (#7425)
642 * deps: relax version constraint on classifier-reborn gem (#7471)
643 * deps: update yajl-ruby (#7278)
644 * deps: bump yajl-ruby to v1.4.0 (#6976)
645 * Create symlink only if target is accessible (#7429)
646 * Switch to `:install_if` for wdm gem (#7372)
647 * Add cucumber feature to test include_relative tag (#7213)
648 * Small benchmark refactoring (#7211)
649 * Fix incorrectly passed arguments to assert_equal (#7134)
650 * fix up refute_equal call (#7133)
651 * Fix RuboCop offences in test files (#7128)
652 * Use assert_include (#7093)
653 * Remember to release docs gem (#7066)
654 * Useless privates removed (#6768)
655 * Load Rouge for TestKramdown (#7007)
656 * Update instructions for releasing docs Gem (#6975)
657 * We are not using Ruby 2.2 anymore (#6977)
658 * Remove unnecessary Jekyll::Page constant (#6770)
659 * Remove unused error class (#6511)
660 * Add a Cucumber feature for post_url tag (#7586)
661 * Generate a &#34;TOTAL&#34; row for build-profile table (#7614)
662 * Refactor Jekyll::Cache (#7532)
663 * Store list of expected extnames in a constant (#7638)
664 * Profile allocations from a build session (#7646)
665 * Update small typo in contributing.md (#7671)
666 * Remove override to Jekyll::Document#respond_to? (#7695)
667 * Update TestTags in sync with Rouge v3.4 (#7709)
668 * Use regexp to filter special entries (#7702)
669 * Reduce Array objects generated from utility method (#7749)
670 * Update mime.types (#7756)
671 * Replace redundant Array#map with Array#each (#7761)
672 * Reduce allocations by using #each_with_object (#7758)
673 * Memoize fallback_data for Drop (#7728)
674 * Use String#end_with? to check if entry is a backup (#7701)
675
676 ### Documentation
677
678 * Refactor docs (#7205)
679 * Add a link to Giraffe Academy&#39;s tutorial (#7325)
680 * Do not advise users to install Jekyll outside of Bundler (#6927)
681 * Remove documentation for using Redcarpet (#6990)
682 * Install Docs that Work on MacOS 10.14 (#7561)
683 * Add Installation Instructions for Ubuntu (#6925)
684 * Don&#39;t prompt for sudo when installing with Ubuntu WSL (#6781)
685 * Installation instructions for Fedora (#7198)
686 * Update Windows install docs (#6926)
687 * List all standard liquid filters (#7333)
688 * List all static files variables (#7002)
689 * Improve how to include Rouge stylesheets (#7752)
690 * Mention CommonMark plugins (#7418)
691 * Add TSV to list of supported _data files. (#7168)
692 * How to deploy using pre-push git hook (#7179)
693 * Hosting with AWS Amplify (#7510)
694 * CircleCI deployment through CircleCI v2 (#7024)
695 * GitHub Pages: use themes from other repos (#7112)
696 * Document page.dir and page.name (#7373)
697 * Document custom tag blocks (#7359)
698 * Document converter methods (#7289)
699 * Document `{{ page.collection }}` (#7430)
700 * Document Jekyll Filters with YAML data (#7335)
701 * Document where Jekyll looks for layouts in a site (#7564)
702 * plugin: liquid tag jekyll-flickr (#6946)
703 * plugin: jekyll-target-blank (#7046)
704 * plugin: json-get. (#7086)
705 * plugin: `jekyll-info` (#7091)
706 * plugin: jekyll-xml-source (#7114)
707 * plugin: jekyll-firstimage filter (#7127)
708 * plugin: CAT (#7011)
709 * Resources: Statictastic (#7593)
710 * Resources: Bonsai Search (#7543)
711 * Resources: Formspark (#7601)
712 * Resources: Jekpack(#7598)
713 * Resources: formX (#7536)
714 * Resources: 99inbound&#39;s Jekyll post (#7348)
715 * Resources: CloudSh (#7497)
716 * Community: DEV Community&#39;s Jekyll tag (#7139)
717 * Showcase: developer.spotify.com (#7217)
718 * Showcase: Isomer (#7300)
719 * Add version number for group_by_exp doc (#6956)
720 * Updated nginx configuration for custom-404-page documentation (#6994)
721 * Clarify definition of &#39;draft&#39; (#7037)
722 * _drafts need to be contained within the custom collection directory (#6985)
723 * Updated to supported version (#7031)
724 * Add Hints for some Improved Travis Config in Doc (#7049)
725 * Update travis-ci.md to point out &#34;this is an example Gemfile&#34; (#7089)
726 * Instructions to view theme’s files under Linux (#7095)
727 * Use a real theme in the example (#7125)
728 * Update docs about post creation (#7138)
729 * Initialize upgrading doc for v4.0 (#7140)
730 * Add version badge for date filters with ordinal (#7162)
731 * Corrected sample usage of postfiles (#7181)
732 * Resolve &#34;Unable to locate package ruby2.4&#34; error (#7196)
733 * Correct stylesheet url in tutorial step 7 (#7210)
734 * Removes quotes from markdown for assets (#7223)
735 * Clarified front matter requirement (#7234)
736 * Explicit location of where to create blog.html (#7241)
737 * Reference the build command options that allows multiple config files (#7266)
738 * Add more issue template(s) and pull request template (#7269)
739 * Suggest sites use OpenSSL instead of GnuTLS for their site&#39;s CI (#7010)
740 * Fix broken Contributors link in README.markdown (#7200)
741 * Add title tag to item in RSS template (#7282)
742 * Add link tag to item in RSS template (#7291)
743 * Remove redundant instruction comment (#7342)
744 * Textile is only supported through a converter plugin (#7003)
745 * Add recursive navigation tutorial (#7720)
746 * Remove installation instructions with Homebrew (#7381)
747 * Fix dead link and misleading prose (#7383)
748 * Fix content management section (#7385)
749 * Apply ruby official guide documents (#7393)
750 * Fix group_by_exp filter example (#7394)
751 * Remove alt attribute from a tags (#7407)
752 * Fix BASH code-block in ubuntu.md (#7420)
753 * zlib is missing (#7428)
754 * Fixed unnecessary articles and pronouns (#7466)
755 * Store SSL key and cert in site source (#7473)
756 * Fix typo in tutorial for converting existing site (#7524)
757 * Check if var exists before include tag (#7530)
758 * Clarify docs on collections regarding the need for front matter (#7538)
759 * Fix incorrect Windows path in themes.md (#7525)
760 * Addresses bundle not found. (#7351)
761 * Update the contribution docs for draft pull requests (#7619)
762 * Data file section adds TSV (#7640)
763 * Indicate where the _sass folder is by default (#7644)
764 * Docs: add version tags to new placeholders (#5981) for permalinks (#7647)
765 * Solve &#34;GitHub Page build failure&#34; in 10-deployment.md (#7648)
766 * fix link to Site Source config (#7708)
767 * Introduce frontmatter in step 2 (#7704)
768 * Add @ashmaroli to Core Team listing (#7398)
769 * Link to Tidelift in site&#39;s footer (#7377)
770 * Link to OpenCollective backing (#7378
771 * Link to sponsor listing in README (#7405)
772 * Adjust team page listings (#7395)
773 * Updates to CODE OF CONDUCT (v1.4.0) (#7105)
774 * More inclusive writing (#7283)
775 * Update Ruby version used in Travis-CI example (#7783)
776 * Documentation for binary operators in where_exp (#7786)
777 * Adding SmartForms as Forms service (#7794)
778
779 ### Site Enhancements
780
781 * Better Performance (#7388)
782 * Add some minor improvements to image loading in Showcase page (#7214)
783 * Simplify assigning classname to docs&#39; aside-links (#7609)
784 * Simplify couple of includes in the docs site (#7607)
785 * Avoid generating empty classnames (#7610)
786 * Minimize rendering count (#7343)
787
788 ### Release
789
790 * Jekyll v4.0 release (#7782)
791 * Release post for v4.0.0 beta1 (#7716)
792 * Release post for v4.0.0.pre.alpha1 (#7574)
793 * Release post for v3.8.0 (#6849)
794 * Release post for v3.6.3, v3.7.4 and v3.8.4 (#7259)
795 * Post: v4.0 development (#6934)
12796
13797 ## 3.8.6 / 2019-07-02
14798
36820
37821 ### Bug Fixes
38822
39 * security: fix `include` bypass of `EntryFilter#filter` symlink check (#7228)
823 * 3.8.x: security: fix `include` bypass of `EntryFilter#filter` symlink check (#7228)
40824
41825 ## 3.8.3 / 2018-06-05
42826
81865 * Minimize array allocations in the `where` filter (#6860)
82866 * Bump JRuby (#6878)
83867 * Assert existence of &lt;collection&gt;.files (#6907)
84 * Bump Rubocop to 0.54.x (#6915)
868 * Bump RuboCop to 0.54.x (#6915)
85869 * Regenerate unconditionally unless its an incremental build (#6917)
86870 * Centralize require statements (#6910)
87 * Bump to Rubocop 0.55 (#6929)
871 * Bump to RuboCop 0.55 (#6929)
88872 * Refactor private method `HighlightBlock#parse_options` (#6822)
89873
90874 ### Minor Enhancements
133917 * doc: add liquid tag plugin jekyll-onebox for html previews (#6898)
134918 * Add `jekyll-w2m` to plugins (#6855)
135919 * Fix tutorials navigation HTML (#6919)
136 * add Arch Linux instalation troubleshoot (#6782)
920 * add Arch Linux installation troubleshoot (#6782)
137921 * Docs: Install Jekyll on macOS (#6881)
138922 * Fix CodeClimate badges [ci skip] (#6930)
139923 * Update index.md (#6933)
154938 * `include_relative` tag should find related documents in collections gathered within custom `collections_dir` (#6818)
155939 * Handle liquid tags in excerpts robustly (#6891)
156940 * Allow front matter defaults to be applied properly to documents gathered under custom `collections_dir` (#6885)
941
942 ## 3.7.4 / 2018-09-07
943
944 ### Bug Fixes
945
946 * Security: fix `include` bypass of EntryFilter#filter symlink check (#7224)
157947
158948 ## 3.7.3 / 2018-02-25
159949
2831073 * Fix list appearance by adding missing `ol` tag (#6421)
2841074 * Explain how to override output collection index page (#6424)
2851075 * Added github-cards to the list of plugins (#6425)
286 * CoC violation correspondants (#6429)
1076 * CoC violation correspondents (#6429)
2871077 * Add a note about Liquid and syntax highlighting (#6466)
2881078 * Remove `sudo` from macOS troubleshooting instructions (#6486)
2891079 * Add a note on `:jekyll_plugins` group in the docs (#6488)
3211111 * Improve docs styling for code to be run in shell (#6641)
3221112 * Fix permalink icon markup in news-item layout (#6639)
3231113
1114 ## 3.6.3 / 2018-09-18
1115
1116 ### Bug Fixes
1117
1118 * 3.6.x: security: fix `include` bypass of `EntryFilter#filter` symlink check (#7229)
1119
3241120 ## 3.6.2 / 2017-10-21
3251121
3261122 ### Development Fixes
3931189 * add SUPPORT file for GitHub (#6324)
3941190 * Rename CODE_OF_CONDUCT to show in banner (#6325)
3951191 * Docs : illustrate page.id for a collection&#39;s document (#6329)
396 * Docs: post&#39;s date can be overriden in YAML front matter (#6334)
1192 * Docs: post&#39;s date can be overridden in front matter (#6334)
3971193 * Docs: `site.url` behavior on development and production environments (#6270)
3981194 * Fix typo in site.url section of variables.md :-[ (#6337)
3991195 * Docs: updates (#6343)
4411237
4421238 ### Bug Fixes
4431239
444 * Backward compatiblize URLFilters module (#6163)
1240 * Backward compatibilize URLFilters module (#6163)
4451241 * Static files contain front matter default keys when `to_liquid`'d (#6162)
4461242 * Always normalize the result of the `relative_url` filter (#6185)
4471243
9111707
9121708 ### Minor Enhancements
9131709
914 * Stop testing with Ruby 2.0.x, which is EOL'd. (#4381)
1710 * Stop testing with Ruby 2.0.x EOL (#4381)
9151711 * Allow collections to have documents that have no file extension (#4545)
9161712 * Add size property to `group_by` result (#4557)
9171713 * Site Template: Removed unnecessary nesting from `_base.scss` (#4637)
9371733 * Add 'jekyll new-theme' command to help users get up and running creating a theme (#4848)
9381734 * `markdownify` and `smartify` should convert input to string before conversion (#4958)
9391735 * Run `Site#generate` for 'jekyll doctor' to catch plugin issues (#5005)
940 * Add `normalize_whitepace` filter (#4917)
1736 * Add `normalize_whitespace` filter (#4917)
9411737 * Move bin/jekyll to exe/jekyll to prevent collision with binstubs (#5014)
9421738 * Cleaning up site template & theme updates. (#4922)
9431739 * Add fetch method to Drops (#5056)
9861782 * Fix state leakage in Kramdown test (#4618)
9871783 * Unify method for copying special files from repo to site (#4601)
9881784 * Refresh the contributing file (#4596)
989 * change smartify doc from copy/paste of mardownify doc (#4653)
1785 * change smartify doc from copy/paste of markdownify doc (#4653)
9901786 * Update Rake & disable warnings when running tests (#4720)
9911787 * Fix many warnings (#4537)
9921788 * Don't blindly assume the last system when determining "open" cmd (#4717)
10591855 * Fix typo on Chocolatey name in Windows documentation (#4686)
10601856 * Use the correct URL, Fixes #4698 (#4699)
10611857 * Add jekyll-paspagon plugin (#4700)
1062 * Bold-italicize note in assets documentation about needing yaml front matter (#4706)
1858 * Bold-italicize note in assets documentation about needing front matter (#4706)
10631859 * Highlight the `script/` calls in the Contributing documentation (#4712)
10641860 * Add Hawkins to the list of third-party plugins (#4755)
10651861 * Fix a typo in pagination doc (#4763)
10841880 * Corrected pagination docs for hidden: true feature (#4903)
10851881 * Remove a Broken Link for Refheap Plugin (#4971)
10861882 * Instructions on how to install github-gem on Windows (#4975)
1087 * Minor tweak to fix missing apostrophne (#4962)
1883 * Minor tweak to fix missing apostrophe (#4962)
10881884 * Instructions on how to install github-gem on Windows (v2) (#4977)
10891885 * Fix inaccurate HTTP response header field name (#4976)
10901886 * Add post about GSoC project (#4980)
10921888 * Update normalize.css to v4.0.0. (#4989)
10931889 * Add jekyll-tags-list-plugin to list of third-party plugins (#5000)
10941890 * Windows docs: Command needs to be called from blog path (#5006)
1095 * Update text to be consitent with example (#5010)
1891 * Update text to be consistent with example (#5010)
10961892 * Update template links to point to core Liquid site (#5012)
10971893 * Add generator-jekyllized to third-party plugins (#5027)
1098 * Add Jekyll Art Hallery generator plugin to list of third-party plugins (#5043)
1894 * Add Jekyll Art Gallery generator plugin to list of third-party plugins (#5043)
10991895 * Add Formingo to the list of Jekyll form SaaS (#5054)
11001896 * Highlight help nav item when navigated to. (#5058)
11011897 * Update normalize.css to v4.2.0. (#5096)
12272023 * Drop: fix hash setter precedence (#4312)
12282024 * utils: `has_yaml_header?` should accept files with extraneous spaces (#4290)
12292025 * Escape html from site.title and page.title in site template (#4307)
1230 * Allow custom file extensions if defined in `permalink` YAML front matter (#4314)
2026 * Allow custom file extensions if defined in `permalink` front matter (#4314)
12312027 * Fix deep_merge_hashes! handling of drops and hashes (#4359)
12322028 * Page should respect output extension of its permalink (#4373)
12332029 * Disable auto-regeneration when running server detached (#4376)
12472043 * Reorganize and cleanup the Gemfile, shorten required depends. (#4318)
12482044 * Remove script/rebund. (#4341)
12492045 * Implement codeclimate platform (#4340)
1250 * Remove ObectSpace dumping and start using inherited, it's faster. (#4342)
2046 * Remove ObjectSpace dumping and start using inherited, it's faster. (#4342)
12512047 * Add script/travis so all people can play with Travis-CI images. (#4338)
1252 * Move Cucumber to using RSpec-Expections and furthering JRuby support. (#4343)
2048 * Move Cucumber to using RSpec-Expectations and furthering JRuby support. (#4343)
12532049 * Rearrange Cucumber and add some flair. (#4347)
12542050 * Remove old FIXME (#4349)
12552051 * Clean up the Gemfile (and keep all the necessary dependencies) (#4350)
14302226 * Internal: trigger hooks by owner symbol (#3871)
14312227 * Update MIME types from mime-db (#3933)
14322228 * Add header to site template `_config.yml` for clarity & direction (#3997)
1433 * Site template: add timezone offset to post date frontmatter (#4001)
2229 * Site template: add timezone offset to post date front matter (#4001)
14342230 * Make a constant for the regex to find hidden files (#4032)
14352231 * Site template: refactor github & twitter icons into includes (#4049)
14362232 * Site template: add background to Kramdown Rouge-ified backtick code blocks (#4053)
14472243 * Fix nav items alignment when on multiple rows (#3264)
14482244 * Highlight: Only Strip Newlines/Carriage Returns, not Spaces (#3278)
14492245 * Find variables in front matter defaults by searching with relative file path. (#2774)
1450 * Allow variables (e.g `:categories`) in YAML front matter permalinks (#3320)
2246 * Allow variables (e.g `:categories`) in front matter permalinks (#3320)
14512247 * Handle nil URL placeholders in permalinks (#3325)
14522248 * Template: Fix nav items alignment when in "burger" mode (#3329)
14532249 * Template: Remove `!important` from nav SCSS introduced in #3329 (#3375)
14642260 * Add WOFF2 font MIME type to Jekyll server MIME types (#3647)
14652261 * Be smarter about extracting the extname in `StaticFile` (#3632)
14662262 * Process metadata for all dependencies (#3608)
1467 * Show error message if the YAML front matter on a page/post is invalid. (#3643)
2263 * Show error message if the front matter on a page/post is invalid. (#3643)
14682264 * Upgrade redcarpet to 3.2 (Security fix: OSVDB-120415) (#3652)
14692265 * Create #mock_expects that goes directly to RSpec Mocks. (#3658)
14702266 * Open `.jekyll-metadata` in binary mode to read binary Marshal data (#3713)
15422338 * Add a Resources link to tutorial on building dynamic navbars (#3185)
15432339 * Semantic structure improvements to the post and page layouts (#3251)
15442340 * Add new AsciiDoc plugin to list of third-party plugins. (#3277)
1545 * Specify that all transformable collection documents must contain YAML front matter (#3271)
2341 * Specify that all transformable collection documents must contain front matter (#3271)
15462342 * Assorted accessibility fixes (#3256)
15472343 * Update configuration docs to mention `keep_files` for `destination` (#3288, #3296)
15482344 * Break when we successfully generate nav link to save CPU cycles. (#3291)
15702366 * Add a link on all the docs pages to "Improve this page". (#3510)
15712367 * Add jekyll-auto-image generator to the list of third-party plugins (#3489)
15722368 * Replace link to the proposed `picture` element spec (#3530)
1573 * Add frontmatter date formatting information (#3469)
2369 * Add front matter date formatting information (#3469)
15742370 * Improve consistency and clarity of plugins options note (#3546)
15752371 * Add permalink warning to pagination docs (#3551)
15762372 * Fix grammar in Collections docs API stability warning (#3560)
15822378 * Define the `install` step in the CI example `.travis.yml` (#3622)
15832379 * Expand collections documentation. (#3638)
15842380 * Add the "warning" note label to excluding `vendor` in the CI docs page (#3623)
1585 * Upgrade pieces of the Ugrading guide for Jekyll 3 (#3607)
2381 * Upgrade pieces of the Upgrading guide for Jekyll 3 (#3607)
15862382 * Showing how to access specific data items (#3468)
15872383 * Clarify pagination works from within HTML files (#3467)
15882384 * Add note to `excerpt_separator` documentation that it can be set globally (#3667)
20792875 * Clean up the `<head>` in the site template (#2186)
20802876 * Permit YAML blocks to end with three dots to better conform with the YAML spec (#2110)
20812877 * Use `File.exist?` instead of deprecated `File.exists?` (#2214)
2082 * Require newline after start of YAML Front Matter header (#2211)
2878 * Require newline after start of front matter header (#2211)
20832879 * Add the ability for pages to be marked as `published: false` (#1492)
20842880 * Add `Jekyll::LiquidExtensions` with `.lookup_variable` method for easy looking up of variable values in a Liquid context. (#2253)
20852881 * Remove literal lang name from class (#2292)
25773373 * Add ReadInXMinutes plugin to the plugin list (#1222)
25783374 * Remove plugins from the plugin list that have equivalents in Jekyll proper (#1223)
25793375 * Add jekyll-assets to the plugin list (#1225)
2580 * Add jekyll-pandoc-mulitple-formats to the plugin list (#1229)
3376 * Add jekyll-pandoc-multiple-formats to the plugin list (#1229)
25813377 * Remove dead link to "Using Git to maintain your blog" (#1227)
25823378 * Tidy up the third-party plugins listing (#1228)
25833379 * Update contributor information (#1192)
27303526 * Adds excerpt attribute to posts which contains first paragraph of content (#837)
27313527 * Accept custom configuration file via CLI (#863)
27323528 * Load in GitHub Pages MIME Types on `jekyll serve` (#847, #871)
2733 * Improve debugability of error message for a malformed highlight tag (#785)
3529 * Improve debuggability of error message for a malformed highlight tag (#785)
27343530 * Allow symlinked files in unsafe mode (#824)
27353531 * Add 'gist' Liquid tag to core (#822, #861)
27363532 * New format of Jekyll output (#795)
27573553 * Bullet-proof `limit_posts` option (#1004)
27583554 * Read in YAML as UTF-8 to accept non-ASCII chars (#836)
27593555 * Fix the CLI option `--plugins` to actually accept dirs and files (#993)
2760 * Allow 'excerpt' in YAML front matter to override the extracted excerpt (#946)
3556 * Allow 'excerpt' in front matter to override the extracted excerpt (#946)
27613557 * Fix cascade problem with site.baseurl, site.port and site.host. (#935)
27623558 * Filter out directories with valid post names (#875)
27633559 * Fix symlinked static files not being correctly built in unsafe mode (#909)
27693565 * Patch for multibyte URI problem with `jekyll serve` (#723)
27703566 * Order plugin execution by priority (#864)
27713567 * Fixed Page#dir and Page#url for edge cases (#536)
2772 * Fix broken `post_url` with posts with a time in their YAML front matter (#831)
3568 * Fix broken `post_url` with posts with a time in their front matter (#831)
27733569 * Look for plugins under the source directory (#654)
27743570 * Tumblr Migrator: finds `_posts` dir correctly, fixes truncation of long post names (#775)
27753571 * Force Categories to be Strings (#767)
29603756
29613757 * Bug Fixes
29623758 * Require redcloth >= 4.2.1 in tests (#92)
2963 * Don't break on triple dashes in yaml front matter (#93)
3759 * Don't break on triple dashes in front matter (#93)
29643760
29653761 ### Minor Enhancements
29663762
29923788 * Added --paginate option to the executable along with a paginator object for the payload (@calavera)
29933789 * Upgraded RedCloth to 4.2.1, which makes `<notextile>` tags work once again.
29943790 * Configuration options set in config.yml are now available through the site payload (@vilcans)
2995 * Posts can now have an empty YAML front matter or none at all (@ bahuvrihi)
3791 * Posts can now have an empty front matter or none at all (@ bahuvrihi)
29963792 * Bug Fixes
29973793 * Fixing Ruby 1.9 issue that requires `#to_s` on the err object (@Chrononaut)
29983794 * Fixes for pagination and ordering posts on the same day (@ujh)
30003796 * Index.html file should always have index.html permalink (@eugenebolshakov)
30013797 * Added trailing slash to pretty permalink style so Apache is happy (@eugenebolshakov)
30023798 * Bad markdown processor in config fails sooner and with better message (@ gcnovus)
3003 * Allow CRLFs in yaml front matter (@juretta)
3799 * Allow CRLFs in front matter (@juretta)
30043800 * Added Date#xmlschema for Ruby versions < 1.9
30053801
30063802 ## 0.5.1 / 2009-05-06
30793875 * Added post categories based on directories containing `_posts` (@mreid)
30803876 * Added post topics based on directories underneath `_posts`
30813877 * Added new date filter that shows the full month name (@mreid)
3082 * Merge Post's YAML front matter into its to_liquid payload (@remi)
3878 * Merge Post's front matter into its to_liquid payload (@remi)
30833879 * Restrict includes to regular files underneath `_includes`
30843880 * Bug Fixes
30853881 * Change YAML delimiter matcher so as to not chew up 2nd level markdown headers (@mreid)
00 The MIT License (MIT)
11
2 Copyright (c) 2008-2018 Tom Preston-Werner and Jekyll contributors
2 Copyright (c) 2008-present Tom Preston-Werner and Jekyll contributors
33
44 Permission is hereby granted, free of charge, to any person obtaining a copy
55 of this software and associated documentation files (the "Software"), to deal
00 # [Jekyll](https://jekyllrb.com/)
11
22 [![Gem Version](https://img.shields.io/gem/v/jekyll.svg)][ruby-gems]
3 [![Linux Build Status](https://img.shields.io/travis/jekyll/jekyll/master.svg?label=Linux%20build)][travis]
3 [![Linux Build Status](https://github.com/jekyll/jekyll/workflows/Continuous%20Integration/badge.svg)][ci-workflow]
44 [![Windows Build status](https://img.shields.io/appveyor/ci/jekyll/jekyll/master.svg?label=Windows%20build)][appveyor]
5 [![Maintainability](https://api.codeclimate.com/v1/badges/8ba0cb5b17bb9848e128/maintainability)](codeclimate)
6 [![Test Coverage](https://api.codeclimate.com/v1/badges/8ba0cb5b17bb9848e128/test_coverage)](coverage)
7 [![Dependency Status](https://img.shields.io/gemnasium/jekyll/jekyll.svg)][gemnasium]
8 [![Security](https://hakiri.io/github/jekyll/jekyll/master.svg)][hakiri]
5 [![Backers on Open Collective](https://opencollective.com/jekyll/backers/badge.svg)](#backers)
6 [![Sponsors on Open Collective](https://opencollective.com/jekyll/sponsors/badge.svg)](#sponsors)
97
108 [ruby-gems]: https://rubygems.org/gems/jekyll
11 [gemnasium]: https://gemnasium.com/jekyll/jekyll
12 [codeclimate]: https://codeclimate.com/github/jekyll/jekyll
13 [coverage]: https://codeclimate.com/github/jekyll/jekyll/coverage
14 [hakiri]: https://hakiri.io/github/jekyll/jekyll/master
15 [travis]: https://travis-ci.org/jekyll/jekyll
9 [ci-workflow]: https://github.com/jekyll/jekyll/actions?query=workflow%3A%22Continuous+Integration%22+branch%3Amaster
1610 [appveyor]: https://ci.appveyor.com/project/jekyll/jekyll/branch/master
1711
1812 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.
2115
2216 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.
2317
24 See: https://jekyllrb.com/philosophy
25
26 ## Having trouble?
27
28 See: https://jekyllrb.com/docs/troubleshooting/
18 See: [https://jekyllrb.com/philosophy](https://jekyllrb.com/philosophy)
2919
3020 ## Getting Started
3121
3222 * [Install](https://jekyllrb.com/docs/installation/) the gem
3323 * Read up about its [Usage](https://jekyllrb.com/docs/usage/) and [Configuration](https://jekyllrb.com/docs/configuration/)
34 * Take a gander at some existing [Sites](https://wiki.github.com/jekyll/jekyll/sites)
24 * Take a gander at some existing [Sites](https://github.com/jekyll/jekyll/wiki/sites)
3525 * [Fork](https://github.com/jekyll/jekyll/fork) and [Contribute](https://jekyllrb.com/docs/contributing/) your own modifications
36 * Have questions? Check out our official forum community [Jekyll Talk](https://talk.jekyllrb.com/) or [`#jekyll` on irc.freenode.net](https://botbot.me/freenode/jekyll/)
26 * Have questions? Check out our official forum community [Jekyll Talk](https://talk.jekyllrb.com/) and [`#jekyll` Channel on Libera IRC](https://libera.chat)
27
28 ## Diving In
29
30 * [Migrate](https://import.jekyllrb.com/docs/home/) from your previous system
31 * Learn how [Front Matter](https://jekyllrb.com/docs/front-matter/) works
32 * Put information on your site with [Variables](https://jekyllrb.com/docs/variables/)
33 * Customize the [Permalinks](https://jekyllrb.com/docs/permalinks/) your posts are generated with
34 * Use the built-in [Liquid Extensions](https://jekyllrb.com/docs/templates/) to make your life easier
35 * Use custom [Plugins](https://jekyllrb.com/docs/plugins/) to generate content specific to your site
36 * Watch [video tutorials from Giraffe Academy](https://jekyllrb.com/tutorials/video-walkthroughs/)
37
38 ## Need help?
39
40 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.
3741
3842 ## Code of Conduct
3943
4044 In order to have a more open and welcoming community, Jekyll adheres to a
41 [code of conduct](CODE_OF_CONDUCT.markdown) adapted from the Ruby on Rails code of
45 [code of conduct](https://jekyllrb.com/docs/conduct/) adapted from the Ruby on Rails code of
4246 conduct.
4347
4448 Please adhere to this code of conduct in any interactions you have in the
4549 Jekyll community. It is strictly enforced on all official Jekyll
4650 repositories, websites, and resources. If you encounter someone violating
47 these terms, please let one of our core team members [Olivia](mailto:olivia@jekyllrb.com?subject=Jekyll%20CoC%20Violation), [Pat](mailto:pat@jekyllrb.com?subject=Jekyll%20CoC%20Violation), [Matt](mailto:matt@jekyllrb.com?subject=Jekyll%20CoC%20Violation) or [Parker](mailto:parker@jekyllrb.com?subject=Jekyll%20CoC%20Violation) know and we will address it as soon as possible.
51 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.
4852
49 ## Diving In
53 ## Credits
5054
51 * [Migrate](http://import.jekyllrb.com/docs/home/) from your previous system
52 * Learn how the [YAML Front Matter](https://jekyllrb.com/docs/frontmatter/) works
53 * Put information on your site with [Variables](https://jekyllrb.com/docs/variables/)
54 * Customize the [Permalinks](https://jekyllrb.com/docs/permalinks/) your posts are generated with
55 * Use the built-in [Liquid Extensions](https://jekyllrb.com/docs/templates/) to make your life easier
56 * Use custom [Plugins](https://jekyllrb.com/docs/plugins/) to generate content specific to your site
55 ### Sponsors
56
57 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)
58 [![Jekyll Sponsor 0](https://opencollective.com/jekyll/sponsor/0/avatar.svg)](https://opencollective.com/jekyll/sponsor/0/website)
59 [![Jekyll Sponsor 1](https://opencollective.com/jekyll/sponsor/1/avatar.svg)](https://opencollective.com/jekyll/sponsor/1/website)
60 [![Jekyll Sponsor 2](https://opencollective.com/jekyll/sponsor/2/avatar.svg)](https://opencollective.com/jekyll/sponsor/2/website)
61 [![Jekyll Sponsor 3](https://opencollective.com/jekyll/sponsor/3/avatar.svg)](https://opencollective.com/jekyll/sponsor/3/website)
62 [![Jekyll Sponsor 4](https://opencollective.com/jekyll/sponsor/4/avatar.svg)](https://opencollective.com/jekyll/sponsor/4/website)
63 [![Jekyll Sponsor 5](https://opencollective.com/jekyll/sponsor/5/avatar.svg)](https://opencollective.com/jekyll/sponsor/5/website)
64 [![Jekyll Sponsor 6](https://opencollective.com/jekyll/sponsor/6/avatar.svg)](https://opencollective.com/jekyll/sponsor/6/website)
65 [![Jekyll Sponsor 7](https://opencollective.com/jekyll/sponsor/7/avatar.svg)](https://opencollective.com/jekyll/sponsor/7/website)
66 [![Jekyll Sponsor 8](https://opencollective.com/jekyll/sponsor/8/avatar.svg)](https://opencollective.com/jekyll/sponsor/8/website)
67 [![Jekyll Sponsor 9](https://opencollective.com/jekyll/sponsor/9/avatar.svg)](https://opencollective.com/jekyll/sponsor/9/website)
68
69 ### Contributors
70
71 This project exists thanks to all the people who contribute.
72 [![Jekyll Contributors](https://opencollective.com/jekyll/contributors.svg?width=890&button=false)](../../graphs/contributors)
73
74 ### Backers
75
76 Thank you to all our backers! 🙏 [Become a backer](https://opencollective.com/jekyll#backer)
77
78 [![Jekyll Backers](https://opencollective.com/jekyll/backers.svg?width=890)](https://opencollective.com/jekyll#backers)
5779
5880 ## License
5981
158158
159159 desc "Open an irb session preloaded with this library"
160160 task :console do
161 sh "irb -rubygems -r ./lib/#{name}.rb"
161 sh "irb -r ./lib/#{name}.rb"
162162 end
00 version: "{build}"
11
2 clone_depth: 10
2 clone_depth: 5
33
44 branches:
55 only:
99
1010 build: off
1111
12 install:
13 - SET PATH=C:\Ruby%RUBY_FOLDER_VER%-x64\bin;%PATH%
14 - bundle install --retry 5 --jobs=%NUMBER_OF_PROCESSORS% --clean --path vendor\bundle
15
1612 environment:
1713 BUNDLE_WITHOUT: "benchmark:development"
1814 matrix:
1915 - RUBY_FOLDER_VER: "26"
16 TZINFO_VERSION: "~> 1.2"
2017 TEST_SUITE: "test"
21 - RUBY_FOLDER_VER: "25"
22 TEST_SUITE: "test"
23 - RUBY_FOLDER_VER: "24"
24 TEST_SUITE: "test"
25 - RUBY_FOLDER_VER: "23"
18 - RUBY_FOLDER_VER: "26"
19 TZINFO_VERSION: "~> 2.0"
2620 TEST_SUITE: "test"
2721 - RUBY_FOLDER_VER: "26"
2822 TEST_SUITE: "default-site"
2923 - RUBY_FOLDER_VER: "26"
3024 TEST_SUITE: "profile-docs"
3125 - RUBY_FOLDER_VER: "26"
26 TEST_SUITE: "memprof"
27 - RUBY_FOLDER_VER: "26"
28 TZINFO_VERSION: "~> 1.2"
3229 TEST_SUITE: "cucumber"
30 - RUBY_FOLDER_VER: "26"
31 TZINFO_VERSION: "~> 2.0"
32 TEST_SUITE: "cucumber"
33
34 install:
35 - SET PATH=C:\Ruby%RUBY_FOLDER_VER%-x64\bin;%PATH%
36 - bundle install --retry 5 --jobs=%NUMBER_OF_PROCESSORS% --clean --path vendor\bundle
3337
3438 test_script:
3539 - ruby --version
3842 - bash ./script/cibuild
3943
4044 cache:
41 # If one of the files after the right arrow changes, cache will be skipped
45 # If one of the files after the right arrow changes, cache will be invalidated
4246 - 'vendor\bundle -> appveyor.yml,Gemfile,jekyll.gemspec'
0 #!/usr/bin/env ruby
1 # frozen_string_literal: true
2
3 require 'benchmark/ips'
4 require_relative '../lib/jekyll'
5
6 puts ''
7 print 'Setting up... '
8
9 SITE = Jekyll::Site.new(
10 Jekyll.configuration({
11 "source" => File.expand_path("../docs", __dir__),
12 "destination" => File.expand_path("../docs/_site", __dir__),
13 "disable_disk_cache" => true,
14 "quiet" => true,
15 })
16 )
17
18 TEMPLATE_1 = Liquid::Template.parse(<<~HTML)
19 {%- assign doc = site.documents | where: 'url', '/docs/assets/' | first -%}
20 {{- doc.title -}}
21 HTML
22
23 TEMPLATE_2 = Liquid::Template.parse(<<~HTML)
24 {%- assign doc = site.documents | find: 'url', '/docs/assets/' -%}
25 {{- doc.title -}}
26 HTML
27
28 [:reset, :read, :generate].each { |phase| SITE.send(phase) }
29
30 puts 'done.'
31 puts 'Testing... '
32 puts " #{'where + first'.cyan} results in #{TEMPLATE_1.render(SITE.site_payload).inspect.green}"
33 puts " #{'find'.cyan} results in #{TEMPLATE_2.render(SITE.site_payload).inspect.green}"
34
35 if TEMPLATE_1.render(SITE.site_payload) == TEMPLATE_2.render(SITE.site_payload)
36 puts 'Success! Proceeding to run benchmarks.'.green
37 puts ''
38 else
39 puts 'Something went wrong. Aborting.'.magenta
40 puts ''
41 return
42 end
43
44 Benchmark.ips do |x|
45 x.report('where + first') { TEMPLATE_1.render(SITE.site_payload) }
46 x.report('find') { TEMPLATE_2.render(SITE.site_payload) }
47 x.compare!
48 end
0 #!/usr/bin/env ruby
1
2 require_relative '../lib/jekyll'
3 require 'benchmark/ips'
4
5 date = "2014-08-02 14:43:06 PDT".freeze
6 time = Time.parse(date)
7
8 Benchmark.ips do |x|
9 x.report('Time.parse') do
10 Time.parse(date)
11 end
12
13 x.report('localtime') do
14 Time.parse(date).localtime
15 end
16
17 x.report('localtime parsed') do
18 time.localtime
19 end
20
21 x.report('Utils.parse_date') do
22 Jekyll::Utils.parse_date(date)
23 end
24 end
0 #!/usr/bin/env ruby
1 # frozen_string_literal: true
2
3 # For pull request: https://github.com/jekyll/jekyll/pull/8192
4
5 require 'benchmark/ips'
6 require 'bundler/setup'
7 require 'memory_profiler'
8 require 'jekyll'
9
10 CONTEXT = {"bar"=>"The quick brown fox"}
11 MARKUP_1 = %Q(foo=bar lorem="ipsum \\"dolor\\"" alpha='beta \\'gamma\\'').freeze
12 MARKUP_2 = %Q(foo=bar lorem="ipsum 'dolor'" alpha='beta "gamma"').freeze
13
14 #
15
16 def old_parse_params(markup)
17 params = {}
18
19 while (match = Jekyll::Tags::IncludeTag::VALID_SYNTAX.match(markup))
20 markup = markup[match.end(0)..-1]
21
22 value = if match[2]
23 match[2].gsub('\\"', '"')
24 elsif match[3]
25 match[3].gsub("\\'", "'")
26 elsif match[4]
27 CONTEXT[match[4]]
28 end
29
30 params[match[1]] = value
31 end
32 params
33 end
34
35 def new_parse_params(markup)
36 params = {}
37 markup.scan(Jekyll::Tags::IncludeTag::VALID_SYNTAX) do |key, d_quoted, s_quoted, variable|
38 value = if d_quoted
39 d_quoted.include?('\\"') ? d_quoted.gsub('\\"', '"') : d_quoted
40 elsif s_quoted
41 s_quoted.include?("\\'") ? s_quoted.gsub("\\'", "'") : s_quoted
42 elsif variable
43 CONTEXT[variable]
44 end
45
46 params[key] = value
47 end
48 params
49 end
50
51 #
52
53 def report(label, markup, color)
54 prof_report = MemoryProfiler.report { yield }
55
56 allocated_memory = prof_report.scale_bytes(prof_report.total_allocated_memsize)
57 allocated_objects = prof_report.total_allocated
58 retained_memory = prof_report.scale_bytes(prof_report.total_retained_memsize)
59 retained_objects = prof_report.total_retained
60
61 puts <<~MSG.send(color)
62 #{(label + " ").ljust(49, "-")}
63
64 MARKUP: #{markup}
65 RESULT: #{yield}
66
67 Total allocated: #{allocated_memory} (#{allocated_objects} objects)
68 Total retained: #{retained_memory} (#{retained_objects} objects)
69 MSG
70 end
71
72 report('old w/ escaping', MARKUP_1, :magenta) { old_parse_params(MARKUP_1) }
73 report('new w/ escaping', MARKUP_1, :cyan) { new_parse_params(MARKUP_1) }
74
75 report('old no escaping', MARKUP_2, :green) { old_parse_params(MARKUP_2) }
76 report('new no escaping', MARKUP_2, :yellow) { new_parse_params(MARKUP_2) }
77
78 #
79
80 Benchmark.ips do |x|
81 x.report("old + esc".magenta) { old_parse_params(MARKUP_1) }
82 x.report("new + esc".cyan) { new_parse_params(MARKUP_1) }
83 x.compare!
84 end
85
86 Benchmark.ips do |x|
87 x.report("old - esc".green) { old_parse_params(MARKUP_2) }
88 x.report("new - esc".yellow) { new_parse_params(MARKUP_2) }
89 x.compare!
90 end
0 # frozen_string_literal: true
1
2 require 'benchmark/ips'
3 require 'jekyll'
4
5 class FooPage
6 def initialize(dir:, name:)
7 @dir = dir
8 @name = name
9 end
10
11 def slow_path
12 File.join(*[@dir, @name].map(&:to_s).reject(&:empty?)).sub(%r!\A/!, "")
13 end
14
15 def fast_path
16 Jekyll::PathManager.join(@dir, @name).sub(%r!\A/!, "")
17 end
18 end
19
20 nil_page = FooPage.new(:dir => nil, :name => nil)
21 empty_page = FooPage.new(:dir => "", :name => "")
22 root_page = FooPage.new(:dir => "", :name => "ipsum.md")
23 nested_page = FooPage.new(:dir => "lorem", :name => "ipsum.md")
24 slashed_page = FooPage.new(:dir => "/lorem/", :name => "/ipsum.md")
25
26 if nil_page.slow_path == nil_page.fast_path
27 Benchmark.ips do |x|
28 x.report('nil_page slow') { nil_page.slow_path }
29 x.report('nil_page fast') { nil_page.fast_path }
30 x.compare!
31 end
32 end
33
34 if empty_page.slow_path == empty_page.fast_path
35 Benchmark.ips do |x|
36 x.report('empty_page slow') { empty_page.slow_path }
37 x.report('empty_page fast') { empty_page.fast_path }
38 x.compare!
39 end
40 end
41
42 if root_page.slow_path == root_page.fast_path
43 Benchmark.ips do |x|
44 x.report('root_page slow') { root_page.slow_path }
45 x.report('root_page fast') { root_page.fast_path }
46 x.compare!
47 end
48 end
49
50 if nested_page.slow_path == nested_page.fast_path
51 Benchmark.ips do |x|
52 x.report('nested_page slow') { nested_page.slow_path }
53 x.report('nested_page fast') { nested_page.fast_path }
54 x.compare!
55 end
56 end
57
58 if slashed_page.slow_path == slashed_page.fast_path
59 Benchmark.ips do |x|
60 x.report('slashed_page slow') { slashed_page.slow_path }
61 x.report('slashed_page fast') { slashed_page.fast_path }
62 x.compare!
63 end
64 end
8989 Correctness.new(site_docs, "redirect_from".freeze).assert!
9090 Correctness.new(site_docs, "title".freeze).assert!
9191
92 # First, test with a property only a handful of documents have.
93 Benchmark.ips do |x|
94 x.config(time: 10, warmup: 5)
95 x.report('sort_by_property_directly with sparse property') do
96 sort_by_property_directly(site_docs, "redirect_from".freeze)
92 def property(property, meta_key)
93 Benchmark.ips do |x|
94 x.config(time: 10, warmup: 5)
95 x.report("sort_by_property_directly with #{property} property") do
96 sort_by_property_directly(site_docs, meta_key)
97 end
98 x.report("schwartzian_transform with #{property} property") do
99 schwartzian_transform(site_docs, meta_key)
100 end
101 x.compare!
97102 end
98 x.report('schwartzian_transform with sparse property') do
99 schwartzian_transform(site_docs, "redirect_from".freeze)
100 end
101 x.compare!
102103 end
103104
105 # First, test with a property only a handful of documents have.
106 test_property('sparse', 'redirect_from')
107
104108 # Next, test with a property they all have.
105 Benchmark.ips do |x|
106 x.config(time: 10, warmup: 5)
107 x.report('sort_by_property_directly with non-sparse property') do
108 sort_by_property_directly(site_docs, "title".freeze)
109 end
110 x.report('schwartzian_transform with non-sparse property') do
111 schwartzian_transform(site_docs, "title".freeze)
112 end
113 x.compare!
114 end
109 test_property('non-sparse', 'title')
0 #!/usr/bin/env ruby
1 # frozen_string_literal: true
2
3 require "forwardable"
4 require "colorator"
5 require "liquid"
6 require "benchmark/ips"
7 require "memory_profiler"
8
9 # Set up (memory) profiler
10
11 class Profiler
12 def self.run
13 yield new(ARGV[0] || 10_000)
14 end
15
16 def initialize(count)
17 @count = count.to_i
18 end
19
20 def report(label, color, &block)
21 prof_report = MemoryProfiler.report { @count.to_i.times(&block) }
22
23 allocated_memory = prof_report.scale_bytes(prof_report.total_allocated_memsize)
24 allocated_objects = prof_report.total_allocated
25 retained_memory = prof_report.scale_bytes(prof_report.total_retained_memsize)
26 retained_objects = prof_report.total_retained
27
28 puts <<~MSG.send(color)
29 With #{label} calls
30
31 Total allocated: #{allocated_memory} (#{allocated_objects} objects)
32 Total retained: #{retained_memory} (#{retained_objects} objects)
33 MSG
34 end
35 end
36
37 # Set up stage
38
39 class Drop < Liquid::Drop
40 def initialize(obj)
41 @obj = obj
42 end
43 end
44
45 class ForwardDrop < Drop
46 extend Forwardable
47 def_delegators :@obj, :name
48 end
49
50 class StaticDrop < Drop
51 def name
52 @obj.name
53 end
54 end
55
56 class Document
57 def name
58 "lipsum"
59 end
60 end
61
62 # Set up actors
63
64 document = Document.new
65 alpha = ForwardDrop.new(document)
66 beta = StaticDrop.new(document)
67 count = ARGV[0] || 10_000
68
69 # Run profilers
70 puts "\nMemory profiles for #{count} calls to invoke drop key:"
71 Profiler.run do |x|
72 x.report("forwarded", :cyan) { alpha["name"] }
73 x.report("static", :green) { beta["name"] }
74 end
75
76 # Benchmark
77 puts "\nBenchmarking the two scenarios..."
78 Benchmark.ips do |x|
79 x.report("forwarded".cyan) { alpha["name"] }
80 x.report("static".green) { beta["name"] }
81 x.compare!
82 end
00 _site/
1 .idea/
12 *.swp
23 pkg/
34 test/
4 .idea/
+0
-37
docs/404.html less more
0 ---
1 layout: error
2 permalink: /404.html
3 sitemap: false
4 ---
5
6 <section class="intro">
7 <div class="grid">
8 <div class="unit whole align-center">
9 <p class="first">Huh. It seems that page is<br/>Hyde-ing...</p>
10 </div>
11 </div>
12 </section>
13
14 <section class="error">
15 <div class="grid">
16 <div class="unit whole align-center">
17 <p>The resource you requested was not found. Here are some links to help you find your way:</p>
18 <nav class="main-nav">
19 <ul>
20 <li>
21 <a href="/">Home</a>
22 </li>
23 <li>
24 <a href="/docs/home/">Documentation</a>
25 </li>
26 <li>
27 <a href="/news/">News</a>
28 </li>
29 <li>
30 <a href="/help/">Help</a>
31 </li>
32 </ul>
33 </nav>
34 </div>
35 </div>
36 </section>
00 ---
1 version: 3.9.0
1 version: 4.3.1
22 name: Jekyll • Simple, blog-aware, static sites
33 description: Transform your plain text into static websites and blogs
44 url: https://jekyllrb.com
99 logo: "/img/logo-2x.png"
1010 google_analytics_id: UA-50755011-1
1111 google_site_verification: onQcXpAvtHBrUI5LlroHNE_FP0b2qvFyPq7VZw36iEY
12 cloudinary_url: https://res.cloudinary.com/jekyll/image/upload/f_auto,q_auto,w_404
1213 collections:
1314 docs:
1415 permalink: "/:collection/:path/"
1718 permalink: "/news/:year/:month/:day/:title/"
1819 output: true
1920 tutorials:
21 permalink: "/:collection/:path/"
2022 output: true
2123 defaults:
2224 - scope:
2931 type: posts
3032 values:
3133 layout: news_item
32 image: "/img/twitter-card.png"
34 - scope:
35 path: _tutorials
36 type: tutorials
37 values:
38 layout: tutorials
39 - scope:
40 path: ''
41 values:
42 image: "/img/jekyll-og.png"
43 future: true
3344 plugins:
3445 - jekyll-avatar
3546 - jekyll-feed
3849 - jekyll-seo-tag
3950 - jekyll-sitemap
4051 - jemoji
52 feed:
53 categories:
54 - release
55 kramdown:
56 syntax_highlighter_opts:
57 default_lang: plaintext
4158 sass:
4259 style: compressed
60 strict_front_matter: true
4361 exclude:
44 - ".gitignore"
45 - CNAME
4662 - icomoon-selection.json
4763 - readme.md
0 - name: Regeneration
1 description: Enable auto-regeneration of the site when files are modified.
2 flag: "-w, --[no-]watch"
3
4
5 - name: Configuration
6 description: >-
7 Specify config files instead of using <code>_config.yml</code> automatically.
8 Settings in later files override settings in earlier files.
9 flag: "--config FILE1[,FILE2,...]"
10
11
12 - name: Plugins
13 description: >-
14 Specify plugin directories instead of using <code>_plugins/</code> automatically.
15 option: "plugins_dir: [ DIR1,... ]"
16 flag: "-p, --plugins DIR1[,DIR2,...]"
17
18
19 - name: Layouts
20 description: >-
21 Specify layout directory instead of using <code>_layouts/</code> automatically.
22 option: "layouts_dir: DIR"
23 flag: --layouts DIR
24
25
26 - name: Drafts
27 description: Process and render draft posts.
28 option: "show_drafts: BOOL"
29 flag: -D, --drafts
30
31
32 - name: Environment
33 description: Use a specific environment value in the build.
34 flag: JEKYLL_ENV=production
35
36
37 - name: Future
38 description: Publish posts or collection documents with a future date.
39 option: "future: BOOL"
40 flag: --future
41
42
43 - name: Unpublished
44 description: Render posts that were marked as unpublished.
45 option: "unpublished: BOOL"
46 flag: --unpublished
47
48
49 - name: LSI
50 description: >-
51 Produce an index for related posts. Requires the
52 <a href="https://jekyll.github.io/classifier-reborn/">classifier-reborn</a> plugin.
53 option: "lsi: BOOL"
54 flag: --lsi
55
56
57 - name: Limit posts
58 description: Limit the number of posts to parse and publish.
59 option: "limit_posts: NUM"
60 flag: --limit_posts NUM
61
62
63 - name: Force polling
64 description: Force watch to use polling.
65 option: "force_polling: BOOL"
66 flag: --force_polling
67
68
69 - name: Verbose output
70 description: Print verbose output.
71 option: "verbose: BOOL"
72 flag: -V, --verbose
73
74
75 - name: Silence output
76 description: Silence the normal output from Jekyll during a build.
77 option: "quiet: BOOL"
78 flag: -q, --quiet
79
80
81 - name: Log level
82 description: Specify a log level among debug, info, warn, or error.
83 flag: JEKYLL_LOG_LEVEL=info
84
85
86 - name: Incremental build
87 description: >-
88 Enable the experimental
89 <a href="/docs/configuration/incremental-regeneration/">incremental
90 build</a> feature. Incremental build only re-builds posts and pages that
91 have changed, resulting in significant performance improvements for large
92 sites, but may also break site generation in certain cases.
93 option: "incremental: BOOL"
94 flag: -I, --incremental
95
96
97 - name: Disable bundle require
98 description: Disables the need to require gems in `:jekyll_plugins` Gemfile
99 flag: JEKYLL_NO_BUNDLER_REQUIRE=true
100
101
102 - name: Liquid profiler
103 description: Generate a Liquid rendering profile to help you identify performance bottlenecks.
104 option: "profile: BOOL"
105 flag: --profile
106
107
108 - name: Strict front matter
109 description: Cause a build to fail if there is a YAML syntax error in a page's front matter.
110 option: "strict_front_matter: BOOL"
111 flag: --strict_front_matter
112
113
114 - name: Base URL
115 description: Serve the website from the given base URL.
116 option: "baseurl: URL"
117 flag: -b, --baseurl URL
118
119
120 - name: Trace
121 description: Show the full backtrace when an error occurs.
122 flag: -t, --trace
0 - name: Site source
1 description: Change the directory where Jekyll will read files
2 option: "source: DIR"
3 flag: -s, --source DIR
4
5
6 - name: Site destination
7 description: Change the directory where Jekyll will write files
8 option: "destination: DIR"
9 flag: -d, --destination DIR
10
11
12 - name: Safe
13 description: >-
14 Disable <a href="/docs/plugins/">non-whitelisted plugins</a>, caching to disk, and ignore symbolic links.
15 option: "safe: BOOL"
16 flag: --safe
17
18
19 - name: Disable disk cache
20 version-badge: 4.1.0
21 description: >-
22 Disable caching of content to disk in order to skip creating a <code>.jekyll-cache</code> or similar directory at
23 the source to avoid interference with virtual environments and third-party directory watchers. Caching to disk is
24 always disabled in <code>safe</code> mode.
25 option: "disable_disk_cache: BOOL"
26 flag: --disable-disk-cache
27
28
29 - name: Ignore theme configuration
30 version-badge: 4.1.0
31 description: >-
32 Jekyll 4.0 started allowing themes to bundle a <code>_config.yml</code> to simplify theme-onboarding for new users.
33 In the unfortunate situation that importing a bundled theme configuration messes up the merged site-configuration,
34 the user can configure Jekyll to not import the theme-config entirely.
35 option: "ignore_theme_config: BOOL"
36
37
38 - name: Exclude
39 description: >-
40 Exclude directories and/or files from the conversion. These exclusions are relative to the site's source directory
41 and cannot be outside the source directory.
42 option: "exclude: [DIR, FILE, ...]"
43
44
45 - name: Include
46 description: >-
47 Force inclusion of directories and/or files in the conversion. <code>.htaccess</code> is a good example since
48 dotfiles are excluded by default.
49 option: "include: [DIR, FILE, ...]"
50
51
52 - name: Keep files
53 description: >-
54 When clobbering the site destination, keep the selected files. Useful for files that are not generated by jekyll;
55 e.g. files or assets that are generated by your build tool. The paths are relative to the <code>destination</code>.
56 option: "keep_files: [DIR, FILE, ...]"
57
58
59 - name: Time zone
60 description: >-
61 Set the time zone for site generation. This sets the <code>TZ</code> environment variable, which Ruby uses to handle
62 time and date creation and manipulation. Any entry from the
63 <a href="https://en.wikipedia.org/wiki/Tz_database">IANA Time Zone Database</a>
64 is valid, e.g. <code>America/New_York</code>. A list of all available values can be found
65 <a href="https://en.wikipedia.org/wiki/List_of_tz_database_time_zones"> here</a>.
66 When serving on a local machine, the default time zone is set by your operating system. But when served on a remote
67 host/server, the default time zone depends on the server's setting or location.
68 option: "timezone: TIMEZONE"
69
70
71 - name: Encoding
72 description: >-
73 Set the encoding of files by name (only available for Ruby 1.9 or later). The default value is <code>utf-8</code>
74 starting in 2.0.0, and <code>nil</code> before 2.0.0, which will yield the Ruby default of <code>ASCII-8BIT</code>.
75 Available encodings can be shown by the command <code>ruby -e 'puts Encoding::list.join("\n")'</code>.
76 option: "encoding: ENCODING"
0 - name: Local server port
1 description: Listen on the given port. The default is `4000`.
2 option: "port: PORT"
3 flag: "-P, --port PORT"
4
5
6 - name: Local server hostname
7 description: Listen at the given hostname. The default is `localhost`.
8 option: "host: HOSTNAME"
9 flag: "-H, --host HOSTNAME"
10
11
12 - name: Live reload
13 description: Reload a page automatically on the browser when its content is edited.
14 option: "livereload: BOOL"
15 flag: "-l, --livereload"
16
17
18 - name: Live reload ignore
19 description: File glob patterns for LiveReload to ignore.
20 option: "livereload_ignore: [ GLOB1,... ]"
21 flag: "--livereload-ignore GLOB1[,GLOB2,...]"
22
23
24 - name: Live reload min/max delay
25 description: Minimum/Maximum delay before automatically reloading page.
26 options:
27 - "livereload_min_delay: SECONDS"
28 - "livereload_max_delay: SECONDS"
29 flags:
30 - "--livereload-min-delay SECONDS"
31 - "--livereload-max-delay SECONDS"
32
33
34 - name: Live reload port
35 description: Port for LiveReload to listen on.
36 flag: "--livereload-port PORT"
37
38
39 - name: Open URL
40 description: Open the site's URL in the browser.
41 option: "open_url: BOOL"
42 flag: "-o, --open-url"
43
44
45 - name: Detach
46 description: Detach the server from the terminal.
47 option: "detach: BOOL"
48 flag: "-B, --detach"
49
50
51 - name: Skips the initial site build
52 description: Skips the initial site build which occurs before the server is started.
53 option: "skip_initial_build: BOOL"
54 flag: "--skip-initial-build"
55
56
57 - name: Show directory listing
58 description: Show a directory listing instead of loading your index file.
59 option: "show_dir_listing: BOOL"
60 flag: "--show-dir-listing"
61
62
63 - name: X.509 (SSL) private key
64 description: "SSL Private Key, stored or symlinked in the site source."
65 flag: "--ssl-key"
66
67
68 - name: X.509 (SSL) certificate
69 description: "SSL Public certificate, stored or symlinked in the site source."
70 flag: "--ssl-cert"
+0
-53
docs/_data/docs.yml less more
0 - title: Getting Started
1 docs:
2 - home
3 - quickstart
4 - installation
5 - windows
6 - usage
7 - structure
8 - configuration
9
10 - title: Your Content
11 docs:
12 - frontmatter
13 - posts
14 - drafts
15 - pages
16 - static-files
17 - variables
18 - collections
19 - datafiles
20 - assets
21 - migrations
22
23 - title: Customization
24 docs:
25 - templates
26 - includes
27 - permalinks
28 - pagination
29 - plugins
30 - themes
31 - extras
32
33 - title: Deployment
34 docs:
35 - github-pages
36 - deployment-methods
37 - continuous-integration
38
39 - title: Miscellaneous
40 docs:
41 - troubleshooting
42 - sites
43 - resources
44 - upgrading/0-to-2
45 - upgrading/2-to-3
46
47 - title: Meta
48 docs:
49 - contributing
50 - maintaining
51 - conduct
52 - history
0 - title: Getting Started
1 docs:
2 - link: /docs/
3 - link: /docs/installation/
4 - link: /docs/ruby-101/
5 - link: /docs/community/
6 - link: /docs/step-by-step/01-setup/
7 - title: Build
8 docs:
9 - link: /docs/usage/
10 - link: /docs/configuration/
11 - link: /docs/rendering-process/
12 - title: Content
13 docs:
14 - link: /docs/pages/
15 - link: /docs/posts/
16 - link: /docs/front-matter/
17 - link: /docs/collections/
18 - link: /docs/datafiles/
19 - link: /docs/assets/
20 - link: /docs/static-files/
21 - title: Site Structure
22 docs:
23 - link: /docs/structure/
24 - link: /docs/liquid/
25 - link: /docs/variables/
26 - link: /docs/includes/
27 - link: /docs/layouts/
28 - link: /docs/permalinks/
29 - link: /docs/themes/
30 - link: /docs/pagination/
31 - title: Guides
32 docs:
33 - link: /docs/plugins/
34 - link: /docs/migrations/
35 - link: /docs/upgrading/
36 - link: /docs/deployment/
0 #
1 # ---------------------------------------------------------------------------------------
2 # List of Liquid Filters provided by Jekyll Core that will be utilized for their
3 # documentation.
4 #
5 # To document a new filter, create a new "list-item" below with the following keys:
6 # name: : [REQUIRED] A string label that identifies the filter
7 # description: : [REQUIRED] A short description of what to expect from the filter
8 # version_badge: : [OPTIONAL] Jekyll version that introduced the filter
9 # examples: : [REQUIRED] A 'nested list' comprised of inputs and outputs
10 # input: : [REQUIRED] The filter syntax and usage
11 # output: : [OPTIONAL] The output from the filter
12 #
13 # Tip: Use YAML Block notations to "fold" a long string, or to "break" a long string
14 # to the following line. Block notations can also be used to avoid having to use
15 # backslashes to escape quotes.
16 # ---------------------------------------------------------------------------------------
17 #
18 - name: Relative URL
19 description: >-
20 Prepend <code>baseurl</code> config value to the input to convert a URL path into a relative URL.
21 This is recommended for a site that is hosted on a subpath of a domain.
22 examples:
23 - input: '{{ "/assets/style.css" | relative_url }}'
24 output: '/my-baseurl/assets/style.css'
25
26 #
27
28 - name: Absolute URL
29 description: >-
30 Prepend <code>url</code> and <code>baseurl</code> values to the input to convert a URL path to an absolute URL.
31 examples:
32 - input: '{{ "/assets/style.css" | absolute_url }}'
33 output: 'http://example.com/my-baseurl/assets/style.css'
34
35 #
36
37 - name: Date to XML Schema
38 description: Convert a Date into XML Schema (ISO 8601) format.
39 examples:
40 - input: '{{ site.time | date_to_xmlschema }}'
41 output: '2008-11-07T13:07:54-08:00'
42
43 #
44
45 - name: Date to RFC-822 Format
46 description: Convert a Date into the RFC-822 format used for RSS feeds.
47 examples:
48 - input: '{{ site.time | date_to_rfc822 }}'
49 output: 'Mon, 07 Nov 2008 13:07:54 -0800'
50
51 #
52
53 - name: Date to String
54 description: Convert a date to short format.
55 examples:
56 - input: '{{ site.time | date_to_string }}'
57 output: '07 Nov 2008'
58
59 #
60
61 - name: Date to String in ordinal US style
62 description: 'Format a date to ordinal, US, short format.'
63 version_badge: 3.8.0
64 examples:
65 - input: '{{ site.time | date_to_string: "ordinal", "US" }}'
66 output: 'Nov 7th, 2008'
67
68 #
69
70 - name: Date to Long String
71 description: Format a date to long format.
72 examples:
73 - input: '{{ site.time | date_to_long_string }}'
74 output: '07 November 2008'
75
76 #
77
78 - name: Date to Long String in ordinal UK style
79 description: 'Format a date to ordinal, UK, long format.'
80 version_badge: 3.8.0
81 examples:
82 - input: '{{ site.time | date_to_long_string: "ordinal" }}'
83 output: '7th November 2008'
84
85 #
86
87 - name: Where
88 description: Select all the objects in an array where the key has the given value.
89 examples:
90 - input: '{{ site.members | where:"graduation_year","2014" }}'
91 output:
92
93 #
94
95 - name: Where Expression
96 description: Select all the objects in an array where the expression is true.
97 version_badge: 3.2.0
98 examples:
99 - input: |-
100 {{ site.members | where_exp:"item",
101 "item.graduation_year == 2014" }}
102 output:
103 - input: |-
104 {{ site.members | where_exp:"item",
105 "item.graduation_year < 2014" }}
106 output:
107 - input: |-
108 {{ site.members | where_exp:"item",
109 "item.projects contains 'foo'" }}
110 output:
111
112 #
113
114 - name: Find
115 description: >-
116 Return <strong>the first object</strong> in an array for which the queried
117 attribute has the given value or return <code>nil</code> if no item in
118 the array satisfies the given criteria.
119 version_badge: 4.1.0
120 examples:
121 - input: '{{ site.members | find: "graduation_year", "2014" }}'
122 output:
123
124 #
125
126 - name: Find Expression
127 description: >-
128 Return <strong>the first object</strong> in an array for which the given
129 expression evaluates to true or return <code>nil</code> if no item in
130 the array satisfies the evaluated expression.
131 version_badge: 4.1.0
132 examples:
133 - input: |-
134 {{ site.members | find_exp:"item",
135 "item.graduation_year == 2014" }}
136 output:
137 - input: |-
138 {{ site.members | find_exp:"item",
139 "item.graduation_year < 2014" }}
140 output:
141 - input: |-
142 {{ site.members | find_exp:"item",
143 "item.projects contains 'foo'" }}
144 output:
145
146 #
147
148 - name: Group By
149 description: Group an array's items by a given property.
150 examples:
151 - input: '{{ site.members | group_by:"graduation_year" }}'
152 output: |-
153 [{"name"=>"2013", "items"=>[...]},
154 {"name"=>"2014", "items"=>[...]}]
155
156 #
157
158 - name: Group By Expression
159 description: Group an array's items using a Liquid expression.
160 version_badge: 3.4.0
161 examples:
162 - input: |-
163 {{ site.members | group_by_exp: "item",
164 "item.graduation_year | truncate: 3, ''" }}
165 output: |-
166 [{"name"=>"201", "items"=>[...]},
167 {"name"=>"200", "items"=>[...]}]
168
169 #
170
171 - name: XML Escape
172 description: Escape some text for use in XML.
173 examples:
174 - input: '{{ page.content | xml_escape }}'
175 output:
176
177 #
178
179 - name: CGI Escape
180 description: >-
181 CGI escape a string for use in a URL. Replaces any special characters
182 with appropriate <code>%XX</code> replacements. CGI escape normally
183 replaces a space with a plus <code>+</code> sign.
184 examples:
185 - input: '{{ "foo, bar; baz?" | cgi_escape }}'
186 output: 'foo%2C+bar%3B+baz%3F'
187
188 #
189
190 - name: URI Escape
191 description: >-
192 Percent encodes any special characters in a URI.
193 URI escape normally replaces a space with <code>%20</code>.
194 <a href="https://en.wikipedia.org/wiki/Percent-encoding#Types_of_URI_characters">Reserved characters</a>
195 will not be escaped.
196 examples:
197 - input: '{{ "http://foo.com/?q=foo, \bar?" | uri_escape }}'
198 output: 'http://foo.com/?q=foo,%20%5Cbar?'
199
200 #
201
202 - name: Number of Words
203 description: >-
204 Count the number of words in some text.<br/>
205 From <span class="version-badge">v4.1.0</span>, this filter takes an optional
206 argument to control the handling of Chinese-Japanese-Korean (CJK) characters
207 in the <code>input</code> string.<br/>
208 Passing <code>'cjk'</code> as the argument will count every CJK character
209 detected as one word irrespective of being separated by whitespace.<br/>
210 Passing <code>'auto'</code> (auto-detect) works similar to <code>'cjk'</code>
211 but is more performant if the filter is used on a variable string that may
212 or may not contain CJK chars.
213 examples:
214 - input: '{{ "Hello world!" | number_of_words }}'
215 output: 2
216 - input: '{{ "你好hello世界world" | number_of_words }}'
217 output: 1
218 - input: '{{ "你好hello世界world" | number_of_words: "cjk" }}'
219 output: 6
220 - input: '{{ "你好hello世界world" | number_of_words: "auto" }}'
221 output: 6
222
223 #
224
225 - name: Array to Sentence
226 description: >-
227 Convert an array into a sentence. Useful for listing tags.
228 Optional argument for connector.
229 examples:
230 - input: '{{ page.tags | array_to_sentence_string }}'
231 output: 'foo, bar, and baz'
232 - input: '{{ page.tags | array_to_sentence_string: "or" }}'
233 output: 'foo, bar, or baz'
234
235 #
236
237 - name: Markdownify
238 description: Convert a Markdown-formatted string into HTML.
239 examples:
240 - input: '{{ page.excerpt | markdownify }}'
241 output:
242
243 #
244
245 - name: Smartify
246 description: 'Convert "quotes" into &ldquo;smart quotes.&rdquo;'
247 examples:
248 - input: '{{ page.title | smartify }}'
249 output:
250
251 #
252
253 - name: Converting Sass/SCSS
254 description: Convert a Sass- or SCSS-formatted string into CSS.
255 examples:
256 - input: '{{ some_sass | sassify }}'
257 output:
258 - input: '{{ some_scss | scssify }}'
259 output:
260
261 #
262
263 - name: Slugify
264 description: Convert a string into a lowercase URL "slug". See below for options.
265 examples:
266 - input: '{{ "The _config.yml file" | slugify }}'
267 output: 'the-config-yml-file'
268 - input: '{{ "The _config.yml file" | slugify: "pretty" }}'
269 output: 'the-_config.yml-file'
270 - input: '{{ "The _cönfig.yml file" | slugify: "ascii" }}'
271 output: 'the-c-nfig-yml-file'
272 - input: '{{ "The cönfig.yml file" | slugify: "latin" }}'
273 output: 'the-config-yml-file'
274
275 #
276
277 - name: Data To JSON
278 description: Convert Hash or Array to JSON.
279 examples:
280 - input: '{{ site.data.projects | jsonify }}'
281 output:
282
283 #
284
285 - name: Normalize Whitespace
286 description: Replace any occurrence of whitespace with a single space.
287 examples:
288 - input: '{{ "a \n b" | normalize_whitespace }}'
289 output:
290
291 #
292
293 - name: Sort
294 description: >-
295 Sort an array. Optional arguments for hashes
296 1.&nbsp;property name
297 2.&nbsp;nils order (<em>first</em> or <em>last</em>).
298 examples:
299 - input: '{{ page.tags | sort }}'
300 output:
301 - input: '{{ site.posts | sort: "author" }}'
302 output:
303 - input: '{{ site.pages | sort: "title", "last" }}'
304 output:
305
306 #
307
308 - name: Sample
309 description: 'Pick a random value from an array. Optionally, pick multiple values.'
310 examples:
311 - input: '{{ site.pages | sample }}'
312 output:
313 - input: '{{ site.pages | sample: 2 }}'
314 output:
315
316 #
317
318 - name: To Integer
319 description: Convert a string or boolean to integer.
320 examples:
321 - input: '{{ some_var | to_integer }}'
322 output:
323
324 #
325
326 - name: Array Filters
327 description: >-
328 Push, pop, shift, and unshift elements from an Array.
329 These are <strong>NON-DESTRUCTIVE</strong>, i.e. they do not mutate the array,
330 but rather make a copy and mutate that.
331 examples:
332 - input: '{{ page.tags | push: "Spokane" }}'
333 output: '["Seattle", "Tacoma", "Spokane"]'
334 - input: '{{ page.tags | pop }}'
335 output: '["Seattle"]'
336 - input: '{{ page.tags | shift }}'
337 output: '["Tacoma"]'
338 - input: '{{ page.tags | unshift: "Olympia" }}'
339 output: '["Olympia", "Seattle", "Tacoma"]'
340
341 #
342
343 - name: Inspect
344 description: Convert an object into its String representation for debugging.
345 examples:
346 - input: '{{ some_var | inspect }}'
347 output:
0 # Variables provided by Jekyll core
1 #
2 # name: : name of the variable
3 # description: : content returned by the variable
4
5 global:
6 - name: site
7 description: >-
8 Site wide information + configuration settings from <code>_config.yml</code>.
9 See below for details.
10 - name: page
11 description: >-
12 Page specific information + the <a href="/docs/front-matter/">front matter</a>.
13 Custom variables set via the front matter will be available here. See below for details.
14 - name: layout
15 description: >-
16 Layout specific information + the <a href="/docs/front-matter/">front matter</a>.
17 Custom variables set via front matter in layouts will be available here.
18 - name: theme
19 description: >-
20 Theme-gem specific information as defined in the theme's gemspec. Useful for rendering
21 information in the theme demo's "About" page, for example. See below for details.
22 - name: content
23 description: >-
24 In layout files, the rendered content of the Post or Page being wrapped.
25 Not defined in Post or Page files.
26 - name: paginator
27 description: >-
28 When the <code>paginate</code> configuration option is set, this variable becomes available
29 for use. See <a href="../pagination/">Pagination</a> for details.
30
31 site:
32 - name: site.time
33 description: >-
34 The current time (when you run the <code>jekyll</code> command).
35 - name: site.pages
36 description: >-
37 A list of all Pages.
38 - name: site.posts
39 description: >-
40 A reverse chronological list of all Posts.
41 - name: site.related_posts
42 description: >-
43 If the page being processed is a Post, this contains a list of up to ten related Posts.
44 By default, these are the ten most recent posts. For high quality but slow to compute
45 results, run the <code>jekyll</code> command with the <code>--lsi</code>
46 (<a href="https://en.wikipedia.org/wiki/Latent_semantic_analysis#Latent_semantic_indexing">latent semantic indexing</a>)
47 option. Also note GitHub Pages does not support the
48 <code>lsi</code> option when generating sites.
49 - name: site.static_files
50 description: >-
51 A list of all <a href="/docs/static-files/">static files</a> (i.e.
52 files not processed by Jekyll's converters or the Liquid renderer).
53 Each file has five properties: <code>path</code>, <code>modified_time</code>,
54 <code>name</code>, <code>basename</code> and <code>extname</code>.
55 - name: site.html_pages
56 description: >-
57 A subset of <code>site.pages</code> listing those which end in <code>.html</code>.
58 - name: site.html_files
59 description: >-
60 A subset of <code>site.static_files</code> listing those which end in <code>.html</code>.
61 - name: site.collections
62 description: >-
63 A list of all the collections (including posts).
64 - name: site.data
65 description: >-
66 A list containing the data loaded from the YAML files located in the <code>_data</code>
67 directory.
68 - name: site.documents
69 description: >-
70 A list of all the documents in every collection.
71 - name: site.categories.CATEGORY
72 description: >-
73 The list of all Posts in category <code>CATEGORY</code>.
74 - name: site.tags.TAG
75 description: >-
76 The list of all Posts with tag <code>TAG</code>.
77 - name: site.url
78 description: >-
79 Contains the url of your site as it is configured in the <code>_config.yml</code>.
80 For example, if you have <code>url: http://mysite.com</code> in your configuration file,
81 then it will be accessible in Liquid as <code>site.url</code>. For the development
82 environment there is <a href="/news/2016/10/06/jekyll-3-3-is-here/#3-siteurl-is-set-by-the-development-server">an
83 exception</a>, if you are running <code>jekyll serve</code> in a development environment
84 <code>site.url</code> will be set to the value of <code>host</code>, <code>port</code>,
85 and SSL-related options. This defaults to <code>url: http://localhost:4000</code>.
86 - name: "site.[CONFIGURATION_DATA]"
87 description: >-
88 All the variables set via the command line and your <code>_config.yml</code> are available
89 through the <code>site</code> variable. For example, if you have <code>foo: bar</code> in
90 your configuration file, then it will be accessible in Liquid as <code>site.foo</code>.
91 Jekyll does not parse changes to <code>_config.yml</code> in
92 <code>watch</code> mode, you must restart Jekyll to see changes to variables.
93
94 page:
95 - name: page.content
96 description: >-
97 The content of the Page, rendered or un-rendered depending upon
98 what Liquid is being processed and what <code>page</code> is.
99 - name: page.title
100 description: >-
101 The title of the Page.
102 - name: page.excerpt
103 description: >-
104 The un-rendered excerpt of a document.
105 - name: page.url
106 description: >-
107 The URL of the Post without the domain, but with a leading slash, e.g.
108 <code>/2008/12/14/my-post.html</code>
109 - name: page.date
110 description: >-
111 The Date assigned to the Post. This can be overridden in a Post’s front matter by specifying
112 a new date/time in the format <code>YYYY-MM-DD HH:MM:SS</code> (assuming UTC), or
113 <code>YYYY-MM-DD HH:MM:SS +/-TTTT</code> (to specify a time zone using an offset from UTC.
114 e.g. <code>2008-12-14 10:30:00 +0900</code>).
115 - name: page.id
116 description: >-
117 An identifier unique to a document in a Collection or a Post (useful in RSS feeds). e.g.
118 <code>/2008/12/14/my-post</code><code>/my-collection/my-document</code>
119 - name: page.categories
120 description: >-
121 The list of categories to which this post belongs. Categories are derived from the directory
122 structure above the <code>_posts</code> directory. For example, a post at
123 <code>/work/code/_posts/2008-12-24-closures.md</code> would have this field set to
124 <code>['work', 'code']</code>. These can also be specified in the
125 <a href="/docs/front-matter/">front matter</a>.
126 - name: page.collection
127 description: >-
128 The label of the collection to which this document belongs. e.g. <code>posts</code> for a post, or
129 <code>puppies</code> for a document at path <code>_puppies/rover.md</code>. If not part of a
130 collection, an empty string is returned.
131 - name: page.tags
132 description: >-
133 The list of tags to which this post belongs. These can be specified in the
134 <a href="/docs/front-matter/">front matter</a>.
135 - name: page.dir
136 description: >-
137 The path between the source directory and the file of the post or page, e.g.
138 <code>/pages/</code>.
139 This can be overridden by <code>permalink</code> in the <a href="/docs/front-matter/">front matter</a>.
140 - name: page.name
141 description: >-
142 The filename of the post or page, e.g. <code>about.md</code>
143 - name: page.path
144 description: >-
145 The path to the raw post or page. Example usage: Linking back to the page or post’s source
146 on GitHub. This can be overridden in the <a href="/docs/front-matter/">front matter</a>.
147 - name: page.next
148 description: >-
149 The next post relative to the position of the current post in <code>site.posts</code>.
150 Returns <code>nil</code> for the last entry.
151 - name: page.previous
152 description: >-
153 The previous post relative to the position of the current post in <code>site.posts</code>.
154 Returns <code>nil</code> for the first entry.
155
156 theme:
157 - name: theme.root
158 description: Absolute path to the theme-gem.
159 - name: theme.authors
160 description: Comma separated string composed of the authors of the theme-gem.
161 - name: theme.description
162 description: Description or summary of the theme-gem as specified in the theme gemspec.
163 - name: theme.version
164 description: The version string of current theme.
165 - name: theme.dependencies
166 description: List of runtime dependencies of the theme.
167 - name: theme.metadata
168 description: A mapping of key-value pairs as defined in the theme gemspec.
169
170 paginator:
171 - name: paginator.page
172 description: The number of the current page
173 - name: paginator.per_page
174 description: Number of posts per page
175 - name: paginator.posts
176 description: Posts available for the current page
177 - name: paginator.total_posts
178 description: Total number of posts
179 - name: paginator.total_pages
180 description: Total number of pages
181 - name: paginator.previous_page
182 description: >-
183 The number of the previous page, or <code>nil</code> if no previous page exists
184 - name: paginator.previous_page_path
185 description: >-
186 The path to the previous page, or <code>nil</code> if no previous page exists
187 - name: paginator.next_page
188 description: >-
189 The number of the next page, or <code>nil</code> if no subsequent page exists
190 - name: paginator.next_page_path
191 description: >-
192 The path to the next page, or <code>nil</code> if no subsequent page exists
5252 year: 2016
5353
5454 - speaker: Amy Johnston
55 twitter_handle: amybeukenex
55 twitter_handle: AmyJohnstonXL
5656 youtube_id: HR12JiUI2Zc
5757 topic: Jekyll for Technical Documentation
5858 year: 2016
8282 year: 2016
8383
8484 - speaker: Julio Faerman
85 twitter_handle: jmfaerman
85 twitter_handle: juliodevrel
8686 youtube_id: SOMonG8Iqak
8787 topic: Jekyll on AWS
8888 year: 2016
130130 year: 2016
131131
132132 - speaker: Nils Borchers
133 twitter_handle: nilsborchers
133 twitter_handle: nilsbo
134134 youtube_id: DtNMjuv6Rbo
135135 topic: Building a living brand guide with Jekyll and Hologram
136136 year: 2016
164164 youtube_id: nq1AUB72GCQ
165165 topic: Overcoming challenges in using Jekyll for documentation projects
166166 year: 2016
167
168 - speaker: Pieter Roozen
169 twitter_handle: Pieter_Roozen
170 youtube_id: moQP0SqEPsw
171 topic: Jekyll As An API Endpoint
172 year: 2019
173
174 - speaker: Chen Hui Jing
175 twitter_handle: hj_chen
176 youtube_id: CERXESTZ5w4
177 topic: Why I love Jekyll Data Files
178 year: 2019
179
180 - speaker: Chris Ferdinandi
181 twitter_handle: ChrisFerdinandi
182 youtube_id: vR1aI_kQ4-A
183 topic: The Lean Web
184 year: 2019
185
186 - speaker: Catherine Roebuck
187 twitter_handle:
188 youtube_id: zTAP1m1BaDM
189 topic: Jekyll For City Government
190 year: 2019
191
192 - speaker: Joost van der Schee
193 twitter_handle: jhvanderschee
194 youtube_id: ztJJ1GSlYgI
195 topic: "Jekyll Codex - Jekyll for front-end developers"
196 year: 2019
197
198 - speaker: Matthew Loberg
199 twitter_handle: mloberg
200 youtube_id: 6eiAjAtSGqw
201 topic: Leverage AWS S3 And CloudFront To Deploy Blazing Fast Jekyll Sites
202 year: 2019
203
204 - speaker: George Phillips
205 twitter_handle: gphillips_nz
206 youtube_id: nEvdOwFJBVc
207 topic: Structuring Jekyll Sites For Enterprise Design Systems
208 year: 2019
0 - title: Home
1 link: /
2 show_on_mobile: true
3 - title: Docs
4 link: /docs/
5 show_on_mobile: true
6 - title: Resources
7 link: /resources/
8 show_on_mobile: true
9 - title: Showcase
10 link: /showcase/
11 show_on_mobile: false
12 - title: News
13 link: /news/
14 show_on_mobile: true
0 min_version: 2.5.0
1 current_version: 3.1.2
2 current_version_output: ruby 3.1.2p20 (2022-04-12 revision 4491bb740a)
0 - name: Tom Preston Werner Blog
1 url: https://tom.preston-werner.com/
2 image: tom-preston-werner.png
3 categories:
4 - personal
5 - blog
6
7 # - name: White House Social and Behavioral Sciences Team
8 # url: https://sbst.gov/
9 # image: sbst.png
10 # categories:
11 # - government
12
13 - name: SiteLeaf
14 url: https://siteleaf.com
15 image: siteleaf.png
16 categories:
17 - software
18 - marketing-site
19
20 - name: CloudCannon
21 url: https://cloudcannon.com/
22 image: cloudcannon.png
23 categories:
24 - software
25 - marketing-site
26
27 - name: Vesterheim Norwegian-American Museum
28 url: https://vesterheim.org/
29 image: vesterheim.png
30 categories:
31 - marketing-site
32
33 - name: KOTN
34 url: https://kotn.com/
35 image: kotn.png
36 categories:
37 - marketing-site
38
39 - name: MvvmCross
40 url: https://www.mvvmcross.com/
41 image: mvvm.png
42 categories:
43 - software
44 - marketing-site
45
46 - name: Vidgrid
47 url: https://www.vidgrid.com/
48 image: vidgrid.png
49 categories:
50 - software
51 - marketing-site
52
53 - name: Bitcoin
54 url: https://bitcoin.org/en/
55 image: bitcoin.png
56 categories:
57 - software
58 - marketing-site
59
60 - name: Mapwize
61 url: https://www.mapwize.io/
62 image: mapwize.png
63 categories:
64 - software
65 - marketing-site
66
67 - name: Auth0 Blog
68 url: https://auth0.com/blog/
69 image: auth0-blog.png
70 categories:
71 - software
72 - blog
73
74 - name: Freedom of Information Act
75 url: https://www.foia.gov/
76 image: foia-gov.png
77 categories:
78 - government
79
80 - name: "Art & About Sydney"
81 url: https://www.artandabout.com.au/
82 image: art-sydney.png
83 categories:
84 - government
85
86 - name: Passbolt Help
87 url: https://help.passbolt.com/
88 image: passbolt-help.png
89 categories:
90 - knowledgebase
91
92 - name: We are COLLINS
93 url: https://www.wearecollins.com/
94 image: collins.png
95 categories:
96 - agency
97
98 - name: Lightburn
99 url: https://lightburn.co/
100 image: lightburn.png
101 categories:
102 - agency
103
104 - name: italia.it
105 url: https://developers.italia.it/
106 image: italia-it.png
107 categories:
108 - community
109
110 - name: Sydney New Years Eve
111 url: https://www.sydneynewyearseve.com/
112 image: nsw.png
113 categories:
114 - government
115
116 - name: Login.gov
117 url: https://login.gov/
118 image: login-gov.png
119 categories:
120 - government
121
122 - name: plainlanguage.gov
123 url: https://plainlanguage.gov/
124 image: plainlanguage-gov.png
125 categories:
126 - government
127
128 - name: U.S. Web Design Standards
129 url: https://standards.usa.gov/
130 image: uswds.png
131 categories:
132 - government
133
134 - name: Grantmaker Search
135 url: https://www.grantmakers.io/
136 image: grantmakers.png
137 categories:
138 - marketing-site
139
140 - name: Rehan Butt
141 url: https://rehanbutt.com/
142 image: rehn.png
143 categories:
144 - personal
145 - portfolio
146
147 - name: The Markdown Guide
148 url: https://www.markdownguide.org/
149 image: markdown-guide.png
150 categories:
151 - knowledgebase
152
153 - name: Probot
154 url: https://probot.github.io/
155 image: probot.png
156 categories:
157 - documentation
158
159 - name: Matt Grey
160 url: https://himatt.com/
161 image: matt-grey.png
162 categories:
163 - personal
164 - portfolio
165
166 - name: Lattice
167 url: https://latticehq.com/
168 image: lattice.png
169 categories:
170 - software
171 - marketing-site
172
173 - name: MailTape
174 url: https://www.mailta.pe/
175 image: mailtape.png
176 categories:
177 - other
178
179 - name: Digital Democracy
180 url: https://www.digital-democracy.org/
181 image: digital-democracy.png
182 categories:
183 - other
184
185 - name: HTML Reference
186 url: https://htmlreference.io/
187 image: htmlreference.png
188 categories:
189 - documentation
190
191 - name: CSS Reference
192 url: https://cssreference.io/
193 image: cssreference.png
194 categories:
195 - documentation
196
197 - name: Chain
198 url: https://chain.com/
199 image: chain.png
200 categories:
201 - marketing-site
202
203 - name: IBM MobileFirst Foundation
204 url: https://mobilefirstplatform.ibmcloud.com/
205 image: ibm-mobile-foundation.png
206 categories:
207 - documentation
208
209 - name: "18F"
210 url: https://18f.gsa.gov/
211 image: 18f.png
212 categories:
213 - agency
214 - government
215
216 - name: Development Seed
217 url: https://developmentseed.org/
218 image: development-seed.png
219 categories:
220 - agency
221
222 - name: Isomer - Singapore Government Static Websites
223 url: https://isomer.gov.sg/
224 image: isomer.png
225 categories:
226 - government
227
228 - name: French Government Digital Services
229 url: https://beta.gouv.fr/
230 image: beta-gouv-fr.png
231 categories:
232 - government
233
234 - name: Paris Call for Trust and Security in Cyberspace
235 url: https://pariscall.international/
236 image: appel-de-paris.png
237 categories:
238 - government
239
240 - name: GitHub On Demand Training
241 url: https://services.github.com/on-demand/
242 image: github-learning-lab.png
243 categories:
244 - software
245 - knowledgebase
246
247 - name: TwitchCon
248 url: https://www.twitchcon.com/
249 image: twitchcon.png
250 categories:
251 - marketing-site
252 - conference
253
254 - name: UN World Statistics
255 url: https://worldstatisticsday.org
256 image: world-statistics-day.png
257 categories:
258 - government
259
260 - name: Netflix Devices
261 url: https://devices.netflix.com/en/
262 image: netflix.png
263 categories:
264 - marketing-site
265
266 - name: Twitch Developer Documentation
267 url: https://dev.twitch.tv/
268 image: twitch-developers.png
269 categories:
270 - marketing-site
271 - documentation
272
273 - name: Yeoman
274 url: https://yeoman.io/
275 image: yeoman.png
276 categories:
277 - open-source
278 - marketing-site
279
280 - name: Release Management Blog
281 url: https://release.mozilla.org/
282 image: mozilla-release-blog.png
283 categories:
284 - software
285 - blog
286
287 - name: frame.ai
288 url: https://frame.ai/
289 image: frame-ai.png
290 categories:
291 - software
292 - marketing-site
293
294 - name: Spotify for Developers
295 url: https://developer.spotify.com
296 image: spotify-developers.png
297 categories:
298 - marketing-site
299 - documentation
300 - software
301
302 - name: Sketch
303 url: https://sketch.com/
304 image: sketch.png
305 categories:
306 - software
307 - marketing-site
308
309 - name: Ruby on Rails
310 url: https://rubyonrails.org/
311 image: ruby-on-rails.png
312 categories:
313 - marketing-site
314 - documentation
66 - custom-404-page
77 - convert-site-to-jekyll
88 - using-jekyll-with-bundler
9 - csv-to-table
910
1011 #- title: Another section
1112 # tutorials:
22 permalink: /docs/assets/
33 ---
44
5 Jekyll provides built-in support for Sass and can work with CoffeeScript via
6 a Ruby gem. In order to use them, you must first create a file with the
7 proper extension name (one of `.sass`, `.scss`, or `.coffee`) and ***start the
8 file with two lines of triple dashes***, like this:
5 Jekyll provides built-in support for [Sass](https://sass-lang.com/)
6 and can work with [CoffeeScript](https://coffeescript.org/) via a Ruby gem.
7 In order to use them, you must first create a file with the proper extension
8 name (one of `.sass`, `.scss`, or `.coffee`) and
9 ***start the file with two lines of triple dashes***, like this:
910
1011 ```sass
1112 ---
2627 <h5>Jekyll processes all Liquid filters and tags in asset files</h5>
2728 <p>If you are using <a href="https://mustache.github.io">Mustache</a>
2829 or another JavaScript templating language that conflicts with
29 the <a href="/docs/templates/">Liquid template syntax</a>, you
30 the <a href="{{ '/docs/templates/' | relative_url }}">Liquid template syntax</a>, you
3031 will need to place <code>{&#37; raw &#37;}</code> and
3132 <code>{&#37; endraw &#37;}</code> tags around your code.</p>
3233 </div>
4142 a look at [this example site using Sass support in Jekyll][example-sass].
4243
4344 If you are using Sass `@import` statements, you'll need to ensure that your
44 `sass_dir` is set to the base directory that contains your Sass files. You
45 can do that thusly:
45 `sass_dir` is set to the base directory that contains your Sass files:
4646
4747 ```yaml
4848 sass:
6060
6161 Note that the <code>sass_dir</code> becomes the load path for Sass imports,
6262 nothing more. This means that Jekyll does not know about these files
63 directly, so any files here should not contain the YAML Front Matter as
64 described above nor will they be transformed as described above. This
63 directly. Any files here should not contain the empty front matter as
64 described above. If they do, they'll not be transformed as described above. This
6565 folder should only contain imports.
6666
6767 </p>
7878 These are passed to Sass, so any output style options Sass supports are valid
7979 here, too.
8080
81 For more information on Sass configuration options, see the [Sass configuration]({{ '/docs/configuration/sass/' | relative_url }}) docs.
8182
8283 ## Coffeescript
8384
8889
8990 ```yaml
9091 plugins:
91 - jekyll-coffeescript
92 - jekyll-coffeescript
9293 ```
00 ---
11 title: Code of Conduct
22 permalink: "/docs/code_of_conduct/"
3 note: This file is autogenerated. Edit /CODE_OF_CONDUCT.markdown instead.
3 note: This file is autogenerated. Edit /.github/CODE_OF_CONDUCT.markdown instead.
44 redirect_from: "/conduct/index.html"
55 editable: false
66 ---
77
8 As contributors and maintainers of this project, and in the interest of
9 fostering an open and welcoming community, we pledge to respect all people who
10 contribute through reporting issues, posting feature requests, updating
11 documentation, submitting pull requests or patches, and other activities.
8 ## Our Pledge
129
13 We are committed to making participation in this project a harassment-free
14 experience for everyone, regardless of level of experience, gender, gender
15 identity and expression, sexual orientation, disability, personal appearance,
16 body size, race, ethnicity, age, religion, or nationality.
10 In the interest of fostering an open and welcoming environment, we as
11 contributors and maintainers pledge to making participation in our project and
12 our community a harassment-free experience for everyone, regardless of age, body
13 size, disability, ethnicity, sex characteristics, gender identity and expression,
14 level of experience, education, socio-economic status, nationality, personal
15 appearance, race, religion, or sexual identity and orientation.
16
17 ## Our Standards
18
19 Examples of behavior that contributes to creating a positive environment
20 include:
21
22 * Using welcoming and inclusive language
23 * Being respectful of differing viewpoints and experiences
24 * Gracefully accepting constructive criticism
25 * Focusing on what is best for the community
26 * Showing empathy towards other community members
1727
1828 Examples of unacceptable behavior by participants include:
1929
20 * The use of sexualized language or imagery
21 * Personal attacks
22 * Trolling or insulting/derogatory comments
30 * The use of sexualized language or imagery and unwelcome sexual attention or
31 advances
32 * Trolling, insulting/derogatory comments, and personal or political attacks
2333 * Public or private harassment
24 * Publishing other's private information, such as physical or electronic
25 addresses, without explicit permission
26 * Other unethical or unprofessional conduct
34 * Publishing others' private information, such as a physical or electronic
35 address, without explicit permission
36 * Other conduct which could reasonably be considered inappropriate in a
37 professional setting
38
39 ## Our Responsibilities
40
41 Project maintainers are responsible for clarifying the standards of acceptable
42 behavior and are expected to take appropriate and fair corrective action in
43 response to any instances of unacceptable behavior.
2744
2845 Project maintainers have the right and responsibility to remove, edit, or
2946 reject comments, commits, code, wiki edits, issues, and other contributions
3148 permanently any contributor for other behaviors that they deem inappropriate,
3249 threatening, offensive, or harmful.
3350
34 By adopting this Code of Conduct, project maintainers commit themselves to
35 fairly and consistently applying these principles to every aspect of managing
36 this project. Project maintainers who do not follow or enforce the Code of
37 Conduct may be permanently removed from the project team.
51 ## Scope
3852
3953 This Code of Conduct applies both within project spaces and in public spaces
40 when an individual is representing the project or its community.
54 when an individual is representing the project or its community. Examples of
55 representing a project or community include using an official project e-mail
56 address, posting via an official social media account, or acting as an appointed
57 representative at an online or offline event. Representation of a project may be
58 further defined and clarified by project maintainers.
59
60 ## Enforcement
4161
4262 Instances of abusive, harassing, or otherwise unacceptable behavior may be
43 reported by opening an issue or contacting a project maintainer. All complaints
44 will be reviewed and investigated and will result in a response that is deemed
45 necessary and appropriate to the circumstances. Maintainers are obligated to
46 maintain confidentiality with regard to the reporter of an incident.
63 reported by contacting the project team at [olivia@jekyllrb.com](mailto:olivia@jekyllrb.com). All
64 complaints will be reviewed and investigated and will result in a response that
65 is deemed necessary and appropriate to the circumstances. The project team is
66 obligated to maintain confidentiality with regard to the reporter of an incident.
67 Further details of specific enforcement policies may be posted separately.
4768
69 Project maintainers who do not follow or enforce the Code of Conduct in good
70 faith may face temporary or permanent repercussions as determined by other
71 members of the project's leadership.
4872
49 This Code of Conduct is adapted from the [Contributor Covenant][homepage],
50 version 1.3.0, available at
51 [http://contributor-covenant.org/version/1/3/0/][version]
73 ## Attribution
5274
53 [homepage]: http://contributor-covenant.org
54 [version]: http://contributor-covenant.org/version/1/3/0/
75 This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
76 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)
77
78 [homepage]: https://www.contributor-covenant.org
79
80 For answers to common questions about this code of conduct, see
81 [https://www.contributor-covenant.org/faq](https://www.contributor-covenant.org/faq)
22 permalink: /docs/collections/
33 ---
44
5 Not everything is a post or a page. Maybe you want to document the various
6 methods in your open source project, members of a team, or talks at a
7 conference. Collections allow you to define a new type of document that behave
8 like Pages or Posts do normally, but also have their own unique properties and
9 namespace.
10
11 ## Using Collections
12
13 To start using collections, follow these 3 steps:
14
15 * [Step 1: Tell Jekyll to read in your collection](#step1)
16 * [Step 2: Add your content](#step2)
17 * [Step 3: Optionally render your collection's documents into independent files](#step3)
18
19 ### Step 1: Tell Jekyll to read in your collection {#step1}
20
21 Add the following to your site's `_config.yml` file, replacing `my_collection`
22 with the name of your collection:
23
24 ```yaml
25 collections:
26 - my_collection
27 ```
28
29 You can optionally specify metadata for your collection in the configuration:
30
31 ```yaml
32 collections:
33 my_collection:
34 foo: bar
35 ```
36
37 Default attributes can also be set for a collection:
38
39 ```yaml
40 defaults:
41 - scope:
42 path: ""
43 type: my_collection
44 values:
45 layout: page
46 ```
5 Collections are a great way to group related content like members of a team or
6 talks at a conference.
7
8 ## Setup
9
10 To use a Collection you first need to define it in your `_config.yml`. For
11 example here's a collection of staff members:
12
13 ```yaml
14 collections:
15 - staff_members
16 ```
17
18 In this case `collections` is defined as a sequence (i.e., array) with no additional metadata defined for each collection.
19 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:
20
21 ```yaml
22 collections:
23 staff_members:
24 people: true
25 ```
26
27 {: .note .info}
28 When defining a collection as a sequence, its pages will not be rendered by
29 default. To enable this, <code>output: true</code> must be specified on the
30 collection, which requires defining the collection as a mapping. For more
31 information, see the section <a href="#output">Output</a>.
4732
4833 <div class="note">
4934 <h5>Gather your collections {%- include docs_version_badge.html version="3.7.0" -%}</h5>
5540 </div>
5641
5742 <div class="note warning">
58 <h5>Be sure to move posts into custom collections directory</h5>
59
60 <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>_posts</code> directory to <code>my_collections/_posts</code>. Note that, the name of your collections directory cannot start with an underscore (`_`).</p>
61 </div>
62
63 ### Step 2: Add your content {#step2}
64
65 Create a corresponding folder (e.g. `<source>/_my_collection`) and add
66 documents. YAML front matter is processed if the front matter exists, and everything
67 after the front matter is pushed into the document's `content` attribute. If no YAML front
68 matter is provided, Jekyll will not generate the file in your collection.
43 <h5>Be sure to move drafts and posts into custom collections directory</h5>
44
45 <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>
46 </div>
47
48 ## Add content
49
50 Create a corresponding folder (e.g. `<source>/_staff_members`) and add
51 documents. Front matter is processed if the front matter exists, and everything
52 after the front matter is pushed into the document's `content` attribute. If no front
53 matter is provided, Jekyll will consider it to be a [static file]({{ '/docs/static-files/' | relative_url }})
54 and the contents will not undergo further processing. If front matter is provided,
55 Jekyll will process the file contents into the expected output.
56
57 Regardless of whether front matter exists or not, Jekyll will write to the destination
58 directory (e.g. `_site`) only if `output: true` has been set in the collection's
59 metadata.
60
61 For example here's how you would add a staff member to the collection set above.
62 The filename is `./_staff_members/jane.md` with the following content:
63
64 ```markdown
65 ---
66 name: Jane Doe
67 position: Developer
68 ---
69 Jane has worked on Jekyll for the past *five years*.
70 ```
71
72 <em>
73 Do note that in spite of being considered as a collection internally, the above
74 doesn't apply to [posts](/docs/posts/). Posts with a valid filename format will be
75 marked for processing even if they do not contain front matter.
76 </em>
6977
7078 <div class="note info">
7179 <h5>Be sure to name your directories correctly</h5>
7583 </p>
7684 </div>
7785
78 ### Step 3: Optionally render your collection's documents into independent files {#step3}
79
80 If you'd like Jekyll to create a public-facing, rendered version of each
81 document in your collection, set the `output` key to `true` in your collection
82 metadata in your `_config.yml`:
83
84 ```yaml
85 collections:
86 my_collection:
87 output: true
88 ```
89
90 This will produce a file for each document in the collection.
91 For example, if you have `_my_collection/some_subdir/some_doc.md`,
92 it will be rendered using Liquid and the Markdown converter of your
93 choice and written out to `<dest>/my_collection/some_subdir/some_doc.html`.
94
95 If you wish a specific page to be shown when accessing `/my_collection/`,
96 simply add `permalink: /my_collection/index.html` to a page.
97 To list items from the collection, on that page or any other, you can use:
86 ## Output
87
88 Now you can iterate over `site.staff_members` on a page and output the content
89 for each staff member. Similar to posts, the body of the document is accessed
90 using the `content` variable:
9891
9992 {% raw %}
10093 ```liquid
101 {% for item in site.my_collection %}
102 <h2>{{ item.title }}</h2>
103 <p>{{ item.description }}</p>
104 <p><a href="{{ item.url }}">{{ item.title }}</a></p>
94 {% for staff_member in site.staff_members %}
95 <h2>{{ staff_member.name }} - {{ staff_member.position }}</h2>
96 <p>{{ staff_member.content | markdownify }}</p>
10597 {% endfor %}
10698 ```
10799 {% endraw %}
108100
109 <div class="note info">
110 <h5>Don't forget to add YAML for processing</h5>
111 <p>
112 Files in collections that do not have front matter are treated as
113 <a href="/docs/static-files">static files</a> and simply copied to their
114 output location without processing.
115 </p>
116 </div>
117
118 ## Configuring permalinks for collections {#permalinks}
119
120 If you wish to specify a custom pattern for the URLs where your Collection pages
121 will reside, you may do so with the [`permalink` property](../permalinks/):
122
123 ```yaml
124 collections:
125 my_collection:
101 If you'd like Jekyll to create a rendered page for each document in your
102 collection, you can set the `output` key to `true` in your collection
103 metadata in `_config.yml`:
104
105 ```yaml
106 collections:
107 staff_members:
126108 output: true
127 permalink: /:collection/:name
128 ```
129
130 ### Examples
131
132 For a collection with the following source file structure,
133
134 ```
135 _my_collection/
136 └── some_subdir
137 └── some_doc.md
138 ```
139
140 each of the following `permalink` configurations will produce the document structure shown below it.
141
142 * **Default**
143 Same as `permalink: /:collection/:path`.
144
145 ```
146 _site/
147 ├── my_collection
148 │   └── some_subdir
149 │   └── some_doc.html
150 ...
151 ```
152 * `permalink: pretty`
153 Same as `permalink: /:collection/:path/`.
154
155 ```
156 _site/
157 ├── my_collection
158 │   └── some_subdir
159 │   └── some_doc
160 │   └── index.html
161 ...
162 ```
163 * `permalink: /doc/:path`
164
165 ```
166 _site/
167 ├── doc
168 │   └── some_subdir
169 │   └── some_doc.html
170 ...
171 ```
172 * `permalink: /doc/:name`
173
174 ```
175 _site/
176 ├── doc
177 │   └── some_doc.html
178 ...
179 ```
180 * `permalink: /:name`
181
182 ```
183 _site/
184 ├── some_doc.html
185 ...
186 ```
187
188 ### Template Variables
189
190 <div class="mobile-side-scroller">
191 <table>
192 <thead>
193 <tr>
194 <th>Variable</th>
195 <th>Description</th>
196 </tr>
197 </thead>
198 <tbody>
199 <tr>
200 <td>
201 <p><code>:collection</code></p>
202 </td>
203 <td>
204 <p>Label of the containing collection.</p>
205 </td>
206 </tr>
207 <tr>
208 <td>
209 <p><code>:path</code></p>
210 </td>
211 <td>
212 <p>Path to the document relative to the collection's directory.</p>
213 </td>
214 </tr>
215 <tr>
216 <td>
217 <p><code>:name</code></p>
218 </td>
219 <td>
220 <p>The document's base filename, with every sequence of spaces
221 and non-alphanumeric characters replaced by a hyphen.</p>
222 </td>
223 </tr>
224 <tr>
225 <td>
226 <p><code>:title</code></p>
227 </td>
228 <td>
229 <p>
230 The <code>:title</code> template variable will take the
231 <code>slug</code> <a href="/docs/frontmatter/">front matter</a>
232 variable value if any is present in the document; if none is
233 defined then <code>:title</code> will be equivalent to
234 <code>:name</code>, aka the slug generated from the filename.
235 </p>
236 </td>
237 </tr>
238 <tr>
239 <td>
240 <p><code>:output_ext</code></p>
241 </td>
242 <td>
243 <p>Extension of the output file. (Included by default and usually unnecessary.)</p>
244 </td>
245 </tr>
246 </tbody>
247 </table>
248 </div>
109 ```
110
111 You can link to the generated page using the `url` attribute:
112
113 {% raw %}
114 ```liquid
115 {% for staff_member in site.staff_members %}
116 <h2>
117 <a href="{{ staff_member.url }}">
118 {{ staff_member.name }} - {{ staff_member.position }}
119 </a>
120 </h2>
121 <p>{{ staff_member.content | markdownify }}</p>
122 {% endfor %}
123 ```
124 {% endraw %}
125
126 ## Permalinks
127
128 There are special [permalink variables for collections]({{ '/docs/permalinks/#collections' | relative_url }}) to
129 help you control the output url for the entire collection.
130
131 ## Custom Sorting of Documents {%- include docs_version_badge.html version="4.0" -%}
132 {: #custom-sorting-of-documents}
133
134 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.
135
136 You can control this sorting via the collection's metadata.
137
138 ### Sort By Front Matter Key
139
140 Documents can be sorted based on a front matter key by setting a `sort_by` metadata to the front matter key string. For example,
141 to sort a collection of tutorials based on key `lesson`, the configuration would be:
142
143 ```yaml
144 collections:
145 tutorials:
146 sort_by: lesson
147 ```
148
149 The documents are arranged in the increasing order of the key's value. If a document does not have the front matter key defined
150 then that document is placed immediately after sorted documents. When multiple documents do not have the front matter key defined,
151 those documents are sorted by their dates or paths and then placed immediately after the sorted documents.
152
153 ### Manually Ordering Documents
154
155 You can also manually order the documents by setting an `order` metadata with **the filenames listed** in the desired order.
156 For example, a collection of tutorials would be configured as:
157
158 ```yaml
159 collections:
160 tutorials:
161 order:
162 - hello-world.md
163 - introduction.md
164 - basic-concepts.md
165 - advanced-concepts.md
166 ```
167
168 Any documents with filenames that do not match the list entry simply gets placed after the rearranged documents. If a document is
169 nested under subdirectories, include them in entries as well:
170
171 ```yaml
172 collections:
173 tutorials:
174 order:
175 - hello-world.md
176 - introduction.md
177 - concepts/basics.md
178 - concepts/advanced.md
179 ```
180
181 If both metadata keys have been defined properly, `order` list takes precedence.
249182
250183 ## Liquid Attributes
251184
252185 ### Collections
253186
254 Each collection is accessible as a field on the `site` variable. For example, if
255 you want to access the `albums` collection found in `_albums`, you'd use
256 `site.albums`.
257
258 Each collection is itself an array of documents (e.g., `site.albums` is an array of documents, much like `site.pages` and
259 `site.posts`). See the table below for how to access attributes of those documents.
260
261 The collections are also available under `site.collections`, with the metadata
187 Collections are also available under `site.collections`, with the metadata
262188 you specified in your `_config.yml` (if present) and the following information:
263189
264190 <div class="mobile-side-scroller">
352278 <p>Except for documents in hard-coded default collection <code>posts</code>, all documents in collections
353279 you create, are accessible via Liquid irrespective of their assigned date, if any, and therefore renderable.
354280 </p>
355 <p>However documents are attempted to be written to disk only if the concerned collection
281 <p>Documents are attempted to be written to disk only if the concerned collection
356282 metadata has <code>output: true</code>. Additionally, future-dated documents are only written if
357283 <code>site.future</code> <em>is also true</em>.
358284 </p>
361287 </p>
362288 </div>
363289
364
365290 ### Documents
366291
367 In addition to any YAML Front Matter provided in the document's corresponding
292 In addition to any front matter provided in the document's corresponding
368293 file, each document has the following attributes:
369294
370295 <div class="mobile-side-scroller">
382307 </td>
383308 <td>
384309 <p>
385 The (unrendered) content of the document. If no YAML Front Matter is
310 The (unrendered) content of the document. If no front matter is
386311 provided, Jekyll will not generate the file in your collection. If
387 YAML Front Matter is used, then this is all the contents of the file
312 front matter is used, then this is all the contents of the file
388313 after the terminating
389314 `---` of the front matter.
390315 </p>
454379 </tbody>
455380 </table>
456381 </div>
457
458 ## Accessing Collection Attributes
459
460 Attributes from the YAML front matter can be accessed as data anywhere in the
461 site. Using the above example for configuring a collection as `site.albums`,
462 you might have front matter in an individual file structured as follows (which
463 must use a supported markup format, and cannot be saved with a `.yaml`
464 extension):
465
466 ```yaml
467 title: "Josquin: Missa De beata virgine and Missa Ave maris stella"
468 artist: "The Tallis Scholars"
469 director: "Peter Phillips"
470 works:
471 - title: "Missa De beata virgine"
472 composer: "Josquin des Prez"
473 tracks:
474 - title: "Kyrie"
475 duration: "4:25"
476 - title: "Gloria"
477 duration: "9:53"
478 - title: "Credo"
479 duration: "9:09"
480 - title: "Sanctus & Benedictus"
481 duration: "7:47"
482 - title: "Agnus Dei I, II & III"
483 duration: "6:49"
484 ```
485
486 Every album in the collection could be listed on a single page with a template:
487
488 ```liquid
489 {% raw %}
490 {% for album in site.albums %}
491 <h2>{{ album.title }}</h2>
492 <p>Performed by {{ album.artist }}{% if album.director %}, directed by {{ album.director }}{% endif %}</p>
493 {% for work in album.works %}
494 <h3>{{ work.title }}</h3>
495 <p>Composed by {{ work.composer }}</p>
496 <ul>
497 {% for track in work.tracks %}
498 <li>{{ track.title }} ({{ track.duration }})</li>
499 {% endfor %}
500 </ul>
501 {% endfor %}
502 {% endfor %}
503 {% endraw %}
504 ```
0 ---
1 title: Report a bug
2 permalink: "/docs/community/bug/"
3 ---
4
5 If you think you've found a bug within a Jekyll plugin, open an issue in that plugin's repository &mdash; First [look for the plugin on rubygems](https://rubygems.org/) then click on the `Homepage` link to access the plugin repository.
6
7 If you think you've found a bug within Jekyll itself, [open an issue](https://github.com/jekyll/jekyll/issues/new).
0 ---
1 title: Community
2 permalink: /docs/community/
3 redirect_from: "/help/index.html"
4 ---
5
6 ## Jekyll Contributor Code of Conduct
7
8 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.
9
10 Read the full [code of conduct]({{ '/docs/conduct/' | relative_url }})
11
12 ## Reporting Security Vulnerabilities
13
14 Find something in our codebase that could be exploited by malicious elements?
15
16 Consult our [Security Policy]({{ '/docs/security/' | relative_url }}) to see if a product version is considered *outdated* and how to report
17 the situation responsibly.
18
19 ## Where to get support
20
21 If you're looking for support for Jekyll, there are a lot of options:
22
23 * Read the [Jekyll Documentation]({{ '/docs/' | relative_url }})
24 * 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)
25 * Chat with Jekyllers &mdash; Join our [Gitter channel](https://gitter.im/jekyll/jekyll) or our IRC channel #jekyll on [Libera](irc://irc.libera.chat/#jekyll).
26
27 There are a bunch of helpful community members on these services who are willing to point you in the right direction.
28
29 **Reminder: Jekyll's issue tracker is not a support forum.**
30
31 ## Ways to contribute
32
33 * [How to Contribute]({{ '/docs/contributing/' | relative_url }})
34 * [How to file a bug]({{ '/docs/community/bug/' | relative_url }})
35 * [Guide for maintaining Jekyll]({{ '/docs/maintaining/' | relative_url }})
36
37 ## Jekyllconf
38
39 [Watch videos]({{ '/jekyllconf/' | relative_url }}) from members of the Jekyll community speak about interesting use cases, tricks they’ve learned or meta Jekyll topics.
40
41 ## Jekyll on Twitter
42
43 The [official Jekyll Twitter account](https://twitter.com/jekyllrb).
55 editable: false
66 ---
77
8 As contributors and maintainers of this project, and in the interest of
9 fostering an open and welcoming community, we pledge to respect all people who
10 contribute through reporting issues, posting feature requests, updating
11 documentation, submitting pull requests or patches, and other activities.
8 ## Our Pledge
129
13 We are committed to making participation in this project a harassment-free
14 experience for everyone, regardless of level of experience, gender, gender
15 identity and expression, sexual orientation, disability, personal appearance,
16 body size, race, ethnicity, age, religion, or nationality.
10 In the interest of fostering an open and welcoming environment, we as
11 contributors and maintainers pledge to making participation in our project and
12 our community a harassment-free experience for everyone, regardless of age, body
13 size, disability, ethnicity, sex characteristics, gender identity and expression,
14 level of experience, education, socio-economic status, nationality, personal
15 appearance, race, religion, or sexual identity and orientation.
16
17 ## Our Standards
18
19 Examples of behavior that contributes to creating a positive environment
20 include:
21
22 * Using welcoming and inclusive language
23 * Being respectful of differing viewpoints and experiences
24 * Gracefully accepting constructive criticism
25 * Focusing on what is best for the community
26 * Showing empathy towards other community members
1727
1828 Examples of unacceptable behavior by participants include:
1929
20 * The use of sexualized language or imagery
21 * Personal attacks
22 * Trolling or insulting/derogatory comments
30 * The use of sexualized language or imagery and unwelcome sexual attention or
31 advances
32 * Trolling, insulting/derogatory comments, and personal or political attacks
2333 * Public or private harassment
24 * Publishing other's private information, such as physical or electronic
25 addresses, without explicit permission
26 * Other unethical or unprofessional conduct
34 * Publishing others' private information, such as a physical or electronic
35 address, without explicit permission
36 * Other conduct which could reasonably be considered inappropriate in a
37 professional setting
38
39 ## Our Responsibilities
40
41 Project maintainers are responsible for clarifying the standards of acceptable
42 behavior and are expected to take appropriate and fair corrective action in
43 response to any instances of unacceptable behavior.
2744
2845 Project maintainers have the right and responsibility to remove, edit, or
2946 reject comments, commits, code, wiki edits, issues, and other contributions
3148 permanently any contributor for other behaviors that they deem inappropriate,
3249 threatening, offensive, or harmful.
3350
34 By adopting this Code of Conduct, project maintainers commit themselves to
35 fairly and consistently applying these principles to every aspect of managing
36 this project. Project maintainers who do not follow or enforce the Code of
37 Conduct may be permanently removed from the project team.
51 ## Scope
3852
3953 This Code of Conduct applies both within project spaces and in public spaces
40 when an individual is representing the project or its community.
54 when an individual is representing the project or its community. Examples of
55 representing a project or community include using an official project e-mail
56 address, posting via an official social media account, or acting as an appointed
57 representative at an online or offline event. Representation of a project may be
58 further defined and clarified by project maintainers.
59
60 ## Enforcement
4161
4262 Instances of abusive, harassing, or otherwise unacceptable behavior may be
43 reported by opening an issue or contacting a project maintainer. All complaints
44 will be reviewed and investigated and will result in a response that is deemed
45 necessary and appropriate to the circumstances. Maintainers are obligated to
46 maintain confidentiality with regard to the reporter of an incident.
63 reported by contacting the project team at [olivia@jekyllrb.com](mailto:olivia@jekyllrb.com). All
64 complaints will be reviewed and investigated and will result in a response that
65 is deemed necessary and appropriate to the circumstances. The project team is
66 obligated to maintain confidentiality with regard to the reporter of an incident.
67 Further details of specific enforcement policies may be posted separately.
4768
69 Project maintainers who do not follow or enforce the Code of Conduct in good
70 faith may face temporary or permanent repercussions as determined by other
71 members of the project's leadership.
4872
49 This Code of Conduct is adapted from the [Contributor Covenant][homepage],
50 version 1.3.0, available at
51 [http://contributor-covenant.org/version/1/3/0/][version]
73 ## Attribution
5274
53 [homepage]: http://contributor-covenant.org
54 [version]: http://contributor-covenant.org/version/1/3/0/
75 This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
76 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)
77
78 [homepage]: https://www.contributor-covenant.org
79
80 For answers to common questions about this code of conduct, see
81 [https://www.contributor-covenant.org/faq](https://www.contributor-covenant.org/faq)
0 ---
1 title: Default Configuration
2 permalink: "/docs/configuration/default/"
3 ---
4
5 Jekyll runs with the following configuration options by default. Alternative
6 settings for these options can be explicitly specified in the configuration
7 file or on the command-line.
8
9 <div class="note info">
10 <h5>Be aware of directory paths</h5>
11 <p>
12 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.
13 </p>
14 </div>
15
16 ```yaml
17 # Where things are
18 source : .
19 destination : ./_site
20 collections_dir : .
21 plugins_dir : _plugins # takes an array of strings and loads plugins in that order
22 layouts_dir : _layouts
23 data_dir : _data
24 includes_dir : _includes
25 sass:
26 sass_dir: _sass
27 collections:
28 posts:
29 output : true
30
31 # Handling Reading
32 safe : false
33 include : [".htaccess"]
34 exclude : ["Gemfile", "Gemfile.lock", "node_modules", "vendor/bundle/", "vendor/cache/", "vendor/gems/", "vendor/ruby/"]
35 keep_files : [".git", ".svn"]
36 encoding : "utf-8"
37 markdown_ext : "markdown,mkdown,mkdn,mkd,md"
38 strict_front_matter : false
39
40 # Filtering Content
41 show_drafts : null
42 limit_posts : 0
43 future : false
44 unpublished : false
45
46 # Plugins
47 whitelist : []
48 plugins : []
49
50 # Conversion
51 markdown : kramdown
52 highlighter : rouge
53 lsi : false
54 excerpt_separator : "\n\n"
55 incremental : false
56
57 # Serving
58 detach : false
59 port : 4000
60 host : 127.0.0.1
61 baseurl : "" # does not include hostname
62 show_dir_listing : false
63
64 # Outputting
65 permalink : date
66 paginate_path : /page:num
67 timezone : null
68
69 quiet : false
70 verbose : false
71 defaults : []
72
73 liquid:
74 error_mode : warn
75 strict_filters : false
76 strict_variables : false
77
78 # Markdown Processors
79 kramdown:
80 auto_ids : true
81 entity_output : as_char
82 toc_levels : [1, 2, 3, 4, 5, 6]
83 smart_quotes : lsquo,rsquo,ldquo,rdquo
84 input : GFM
85 hard_wrap : false
86 footnote_nr : 1
87 show_warnings : false
88 ```
0 ---
1 title: Environments
2 permalink: "/docs/configuration/environments/"
3 ---
4 In the `build` (or `serve`) arguments, you can specify a Jekyll environment
5 and value. The build will then apply this value in any conditional statements
6 in your content.
7
8 For example, suppose you set this conditional statement in your code:
9
10 {% raw %}
11 ```liquid
12 {% if jekyll.environment == "production" %}
13 {% include disqus.html %}
14 {% endif %}
15 ```
16 {% endraw %}
17
18 When you build your Jekyll site, the content inside the `if` statement won't be
19 run unless you also specify a `production` environment in the build command,
20 like this:
21
22 ```sh
23 JEKYLL_ENV=production jekyll build
24 ```
25
26 Specifying an environment value allows you to make certain content available
27 only within specific environments.
28
29 The default value for `JEKYLL_ENV` is `development`. Therefore if you omit
30 `JEKYLL_ENV` from the build arguments, the default value will be
31 `JEKYLL_ENV=development`. Any content inside
32 {% raw %}`{% if jekyll.environment == "development" %}`{% endraw %} tags will
33 automatically appear in the build.
34
35 Your environment values can be anything you want (not just `development` or
36 `production`). Some elements you might want to hide in development
37 environments include Disqus comment forms or Google Analytics. Conversely,
38 you might want to expose an "Edit me in GitHub" button in a development
39 environment but not include it in production environments.
40
41 By specifying the option in the build command, you avoid having to change
42 values in your configuration files when moving from one environment to another.
43
44 {: .note}
45 To switch part of your config settings depending on the environment, use the
46 <a href="{{ '/docs/configuration/options/#build-command-options' | relative_url }}">build command option</a>,
47 for example <code>--config _config.yml,_config_development.yml</code>. Settings
48 in later files override settings in earlier files.
0 ---
1 title: Front Matter Defaults
2 permalink: "/docs/configuration/front-matter-defaults/"
3 ---
4
5 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.
6
7 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.
8
9 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.
10
11 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.
12
13 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:
14
15 ```yaml
16 defaults:
17 -
18 scope:
19 path: "" # an empty string here means all files in the project
20 values:
21 layout: "default"
22 ```
23
24 <div class="note info">
25 <h5>Stop and rerun `jekyll serve` command.</h5>
26 <p>
27 The <code>_config.yml</code> master configuration file contains global configurations
28 and variable definitions that are read once at execution time. Changes made to <code>_config.yml</code>
29 during automatic regeneration are not loaded until the next execution.
30 </p>
31 <p>
32 Note <a href="{{ '/docs/datafiles/' | relative_url }}">Data Files</a> are included and reloaded during automatic regeneration.
33 </p>
34 </div>
35
36 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.
37
38 ```yaml
39 defaults:
40 -
41 scope:
42 path: "" # an empty string here means all files in the project
43 type: "posts" # previously `post` in Jekyll 2.2.
44 values:
45 layout: "default"
46 ```
47
48 Now, this will only set the layout for files where the type is `posts`.
49 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.
50
51 As mentioned earlier, you can set multiple scope/values pairs for `defaults`.
52
53 ```yaml
54 defaults:
55 -
56 scope:
57 path: ""
58 type: "pages"
59 values:
60 layout: "my-site"
61 -
62 scope:
63 path: "projects"
64 type: "pages" # previously `page` in Jekyll 2.2.
65 values:
66 layout: "project" # overrides previous default layout
67 author: "Mr. Hyde"
68 ```
69
70 With these defaults, all pages would use the `my-site` layout. Any html files that exist in the `projects/`
71 folder will use the `project` layout, if it exists. Those files will also have the `page.author`
72 [liquid variable]({{ '/docs/variables/' | relative_url }}) set to `Mr. Hyde`.
73
74 ```yaml
75 collections:
76 my_collection:
77 output: true
78
79 defaults:
80 -
81 scope:
82 path: ""
83 type: "my_collection" # a collection in your site, in plural form
84 values:
85 layout: "default"
86 ```
87
88 In this example, the `layout` is set to `default` inside the
89 [collection]({{ '/docs/collections/' | relative_url }}) with the name `my_collection`.
90
91 ### Glob patterns in Front Matter defaults
92
93 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" -%}
94
95 ```yaml
96 collections:
97 my_collection:
98 output: true
99
100 defaults:
101 -
102 scope:
103 path: "section/*/special-page.html"
104 values:
105 layout: "specific-layout"
106 ```
107
108 <div class="note warning">
109 <h5>Globbing and Performance</h5>
110 <p>
111 Please note that globbing a path is known to have a negative effect on
112 performance and is currently not optimized, especially on Windows.
113 Globbing a path will increase your build times in proportion to the size
114 of the associated collection directory.
115 </p>
116 </div>
117
118 ### Precedence
119
120 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.
121
122 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.
123
124 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:
125
126 ```yaml
127 # In _config.yml
128 ...
129 defaults:
130 -
131 scope:
132 path: "projects"
133 type: "pages"
134 values:
135 layout: "project"
136 author: "Mr. Hyde"
137 category: "project"
138 ...
139 ```
140
141 ```yaml
142 # In projects/foo_project.md
143 ---
144 author: "John Smith"
145 layout: "foobar"
146 ---
147 The post text goes here...
148 ```
149
150 The `projects/foo_project.md` would have the `layout` set to `foobar` instead
151 of `project` and the `author` set to `John Smith` instead of `Mr. Hyde` when
152 the site is built.
0 ---
1 title: Default Configuration
2 permalink: "/docs/configuration/incremental-regeneration/"
3 ---
4
5 ## Incremental Regeneration
6 <div class="note warning">
7 <h5>Incremental regeneration is still an experimental feature</h5>
8 <p>
9 While incremental regeneration will work for the most common cases, it will
10 not work correctly in every scenario. Please be extremely cautious when
11 using the feature, and report any problems not listed below by
12 <a href="https://github.com/jekyll/jekyll/issues/new">opening an issue on GitHub</a>.
13 </p>
14 </div>
15
16 Incremental regeneration helps shorten build times by only generating documents
17 and pages that were updated since the previous build. It does this by keeping
18 track of both file modification times and inter-document dependencies in the
19 `.jekyll-metadata` file.
20
21 Under the current implementation, incremental regeneration will only generate a
22 document or page if either it, or one of its dependencies, is modified. Currently,
23 the only types of dependencies tracked are includes (using the
24 {% raw %}`{% include %}`{% endraw %} tag) and layouts. This means that plain
25 references to other documents (for example, the common case of iterating over
26 `site.posts` in a post listings page) will not be detected as a dependency.
27
28 To remedy some of these shortfalls, putting `regenerate: true` in the front-matter
29 of a document will force Jekyll to regenerate it regardless of whether it has been
30 modified. Note that this will generate the specified document only; references
31 to other documents' contents will not work since they won't be re-rendered.
32
33 Incremental regeneration can be enabled via the `--incremental` flag (`-I` for
34 short) from the command-line or by setting `incremental: true` in your
35 configuration file.
0 ---
1 title: Liquid Options
2 permalink: "/docs/configuration/liquid/"
3 ---
4 Liquid's response to errors can be configured by setting `error_mode`. The
5 options are
6
7 - `lax` --- Ignore all errors.
8 - `warn` --- Output a warning on the console for each error. (default)
9 - `strict` --- Output an error message and stop the build.
10
11 Within _config.yml, the default configuration is as follows:
12
13 ```yaml
14 liquid:
15 error_mode: warn
16 ```
17
18 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.
19
20 You can also configure Liquid's renderer to catch non-assigned variables and
21 non-existing filters by setting `strict_variables` and / or `strict_filters`
22 to `true` respectively. {% include docs_version_badge.html version="3.8.0" %}
23
24 Do note that while `error_mode` configures Liquid's parser, the `strict_variables`
25 and `strict_filters` options configure Liquid's renderer and are consequently
26 orthogonal.
27
28 An example of setting these variables within _config.yml is as follows:
29
30 ```yaml
31 liquid:
32 error_mode: strict
33 strict_variables: true
34 strict_filters: true
35 ```
36
37 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.
0 ---
1 title: Markdown Options
2 permalink: "/docs/configuration/markdown/"
3 ---
4 The various Markdown renderers supported by Jekyll sometimes have extra options
5 available.
6
7 ## Kramdown
8
9 Kramdown is the default Markdown renderer for Jekyll, and often works well with no additional configuration. However, it does support many configuration options.
10
11 ### Kramdown Processor
12
13 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:
14
15 ```yaml
16 kramdown:
17 gfm_quirks: [paragraph_end]
18 ```
19
20 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.
21
22 ```yaml
23 kramdown:
24 input: Kramdown
25 ```
26
27 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.
28
29 ### Syntax Highlighting (CodeRay)
30
31 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:
32
33 ```yaml
34 kramdown:
35 syntax_highlighter: coderay
36 ```
37
38 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:
39
40 ```yaml
41 kramdown:
42 syntax_highlighter: coderay
43 syntax_highlighter_opts:
44 line_numbers: table
45 bold_every: 5
46 ```
47
48 ### Advanced Kramdown Options
49
50 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:
51
52 ```yaml
53 kramdown:
54 header_offset: 2
55 ```
56
57 <div class="note warning">
58 <h5>There are several unsupported kramdown options</h5>
59 <p>
60 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.
61 </p>
62 </div>
63
64 ## CommonMark
65
66 [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).
67
68 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).
69
70 ### Custom Markdown Processors
71
72 If you're interested in creating a custom markdown processor, you're in luck! Create a new class in the `Jekyll::Converters::Markdown` namespace:
73
74 ```ruby
75 class Jekyll::Converters::Markdown::MyCustomProcessor
76 def initialize(config)
77 require 'funky_markdown'
78 @config = config
79 rescue LoadError
80 STDERR.puts 'You are missing a library required for Markdown. Please run:'
81 STDERR.puts ' $ [sudo] gem install funky_markdown'
82 raise FatalException.new("Missing dependency: funky_markdown")
83 end
84
85 def convert(content)
86 ::FunkyMarkdown.new(content).convert
87 end
88 end
89 ```
90
91 Once you've created your class and have it properly set up either as a plugin
92 in the `_plugins` folder or as a gem, specify it in your `_config.yml`:
93
94 ```yaml
95 markdown: MyCustomProcessor
96 ```
0 ---
1 title: Configuration Options
2 permalink: "/docs/configuration/options/"
3 ---
4
5 The tables below list the available settings for Jekyll, and the various <code
6 class="option">options</code> (specified in the configuration file) and <code
7 class="flag">flags</code> (specified on the command-line) that control them.
8
9 ### Global Configuration
10
11 <div class="mobile-side-scroller">
12 <table>
13 <thead>
14 <tr>
15 <th>Setting</th>
16 <th>
17 <span class="option">Options</span> and <span class="flag">Flags</span>
18 </th>
19 </tr>
20 </thead>
21 <tbody>
22 {% for setting in site.data.config_options.global %}
23 <tr class="setting">
24 <td>
25 <p class="name">
26 <strong>{{ setting.name }}</strong>
27 {% if setting.version-badge %}
28 <span class="version-badge" title="Introduced in v{{ setting.version-badge }}">{{ setting.version-badge }}</span>
29 {% endif %}
30 </p>
31 <p class="description">{{ setting.description }}</p>
32 </td>
33 <td class="align-center">
34 <p><code class="option">{{ setting.option }}</code></p>
35 {% if setting.flag %}
36 <p><code class="flag">{{ setting.flag }}</code></p>
37 {% endif %}
38 </td>
39 </tr>
40 {% endfor %}
41 <tr>
42 <td>
43 <p class='name'><strong>Defaults</strong></p>
44 <p class='description'>
45 Set defaults for <a href="{{ '/docs/front-matter/' | relative_url }}" title="front matter">front matter</a>
46 variables.
47 </p>
48 </td>
49 <td class='align-center'>
50 <p>see <a href="{{ '/docs/configuration/front-matter-defaults/' | relative_url }}" title="details">below</a></p>
51 </td>
52 </tr>
53 </tbody>
54 </table>
55 </div>
56
57 <div class="note warning">
58 <h5>Destination folders are cleaned on site builds</h5>
59 <p>
60 The contents of <code>&lt;destination&gt;</code> are automatically
61 cleaned, by default, when the site is built. Files or folders that are not
62 created by your site will be removed. Some files could be retained
63 by specifying them within the <code>&lt;keep_files&gt;</code> configuration directive.
64 </p>
65 <p>
66 Do not use an important location for <code>&lt;destination&gt;</code>; instead, use it as
67 a staging area and copy files from there to your web server.
68 </p>
69 </div>
70
71 ### Build Command Options
72
73 <div class="mobile-side-scroller">
74 <table>
75 <thead>
76 <tr>
77 <th>Setting</th>
78 <th><span class="option">Options</span> and <span class="flag">Flags</span></th>
79 </tr>
80 </thead>
81 <tbody>
82 {% for setting in site.data.config_options.build %}
83 <tr class="setting">
84 <td>
85 <p class="name">
86 <strong>{{ setting.name }}</strong>
87 {% if setting.version-badge %}
88 <span class="version-badge" title="Introduced in v{{ setting.version-badge }}">{{ setting.version-badge }}</span>
89 {% endif %}
90 </p>
91 <p class="description">{{ setting.description }}</p>
92 </td>
93 <td class="align-center">
94 {% if setting.option %}<p><code class="option">{{ setting.option }}</code></p>{% endif %}
95 {% if setting.flag %}<p><code class="flag">{{ setting.flag }}</code></p>{% endif %}
96 </td>
97 </tr>
98 {% endfor %}
99 </tbody>
100 </table>
101 </div>
102
103 ### Serve Command Options
104
105 In addition to the options below, the `serve` sub-command can accept any of the options
106 for the `build` sub-command, which are then applied to the site build which occurs right
107 before your site is served.
108
109 <div class="mobile-side-scroller">
110 <table>
111 <thead>
112 <tr>
113 <th>Setting</th>
114 <th><span class="option">Options</span> and <span class="flag">Flags</span></th>
115 </tr>
116 </thead>
117 <tbody>
118 {% for setting in site.data.config_options.serve %}
119 <tr class="setting">
120 <td>
121 <p class="name">
122 <strong>{{ setting.name }}</strong>
123 {% if setting.version-badge %}
124 <span class="version-badge" title="Introduced in v{{ setting.version-badge }}">{{ setting.version-badge }}</span>
125 {% endif %}
126 </p>
127 <p class="description">{{ setting.description }}</p>
128 </td>
129 <td class="align-center">
130 {% if setting.option %}
131 <p><code class="option">{{ setting.option }}</code></p>
132 {% elsif setting.options %}
133 <p>
134 {% for option in setting.options %}
135 <code class="option">{{ option }}</code><br>
136 {% endfor %}
137 </p>
138 {% endif %}
139 {% if setting.flag %}
140 <p><code class="flag">{{ setting.flag }}</code></p>
141 {% elsif setting.flags %}
142 <p>
143 {% for flag in setting.flags %}
144 <code class="flag">{{ flag }}</code><br>
145 {% endfor %}
146 </p>
147 {% endif %}
148 </td>
149 </tr>
150 {% endfor %}
151 </tbody>
152 </table>
153 </div>
154
155 <div class="note warning">
156 <h5>Do not use tabs in configuration files</h5>
157 <p>
158 This will either lead to parsing errors, or Jekyll will revert to the
159 default settings. Use spaces instead.
160 </p>
161 </div>
0 ---
1 title: Sass/SCSS Options
2 permalink: "/docs/configuration/sass/"
3 ---
4
5 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.
6
7 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.
8
9 <div class="note info">
10 <p>
11 Note that directory paths specified in the <code>sass</code> configuration
12 are resolved relative to your site's <code>source</code>, not relative to the location of the <code>_config.yml</code> file.
13 </p>
14 </div>
0 ---
1 title: WEBrick Options
2 permalink: "/docs/configuration/webrick/"
3 ---
4 You can provide custom headers for your site by adding them to `_config.yml`
5
6 ```yaml
7 # File: _config.yml
8 webrick:
9 headers:
10 My-Header: My-Value
11 My-Other-Header: My-Other-Value
12 ```
13
14 ### Defaults
15
16 Jekyll provides by default `Content-Type` and `Cache-Control` response
17 headers: one dynamic in order to specify the nature of the data being served,
18 the other static in order to disable caching so that you don't have to fight
19 with Chrome's aggressive caching when you are in development mode.
22 permalink: /docs/configuration/
33 ---
44
5 Jekyll allows you to concoct your sites in any way you can dream up, and it’s
6 thanks to the powerful and flexible configuration options that this is possible.
7 These options can either be specified in a `_config.yml` file placed in your
8 site’s root directory, or can be specified as flags for the `jekyll` executable
9 in the terminal.
5 Jekyll gives you a lot of flexibility to customize how it builds your site. These
6 options can either be specified in a `_config.yml` or `_config.toml` file placed
7 in your site’s root directory, or can be specified as flags for the `jekyll`
8 executable in the terminal.
109
11 ## Configuration Settings
12
13 ### Global Configuration
14
15 The table below lists the available settings for Jekyll, and the various <code
16 class="option">options</code> (specified in the configuration file) and <code
17 class="flag">flags</code> (specified on the command-line) that control them.
18
19 <div class="mobile-side-scroller">
20 <table>
21 <thead>
22 <tr>
23 <th>Setting</th>
24 <th>
25 <span class="option">Options</span> and <span class="flag">Flags</span>
26 </th>
27 </tr>
28 </thead>
29 <tbody>
30 <tr class="setting">
31 <td>
32 <p class="name"><strong>Site Source</strong></p>
33 <p class="description">Change the directory where Jekyll will read files</p>
34 </td>
35 <td class="align-center">
36 <p><code class="option">source: DIR</code></p>
37 <p><code class="flag">-s, --source DIR</code></p>
38 </td>
39 </tr>
40 <tr class="setting">
41 <td>
42 <p class="name"><strong>Site Destination</strong></p>
43 <p class="description">Change the directory where Jekyll will write files</p>
44 </td>
45 <td class="align-center">
46 <p><code class="option">destination: DIR</code></p>
47 <p><code class="flag">-d, --destination DIR</code></p>
48 </td>
49 </tr>
50 <tr class="setting">
51 <td>
52 <p class="name"><strong>Safe</strong></p>
53 <p class="description">Disable <a href="../plugins/">custom plugins, and ignore symbolic links</a>.</p>
54 </td>
55 <td class="align-center">
56 <p><code class="option">safe: BOOL</code></p>
57 <p><code class="flag">--safe</code></p>
58 </td>
59 </tr>
60 <tr class="setting">
61 <td>
62 <p class="name"><strong>Exclude</strong></p>
63 <p class="description">
64 Exclude directories and/or files from the
65 conversion. These exclusions are relative to the site's
66 source directory and cannot be outside the source directory.
67 </p>
68 </td>
69 <td class="align-center">
70 <p><code class="option">exclude: [DIR, FILE, ...]</code></p>
71 </td>
72 </tr>
73 <tr class="setting">
74 <td>
75 <p class="name"><strong>Include</strong></p>
76 <p class="description">
77 Force inclusion of directories and/or files in the conversion.
78 <code>.htaccess</code> is a good example since dotfiles are excluded
79 by default.
80 </p>
81 </td>
82 <td class="align-center">
83 <p><code class="option">include: [DIR, FILE, ...]</code></p>
84 </td>
85 </tr>
86 <tr class="setting">
87 <td>
88 <p class="name"><strong>Keep files</strong></p>
89 <p class="description">
90 When clobbering the site destination, keep the selected files.
91 Useful for files that are not generated by jekyll; e.g. files or
92 assets that are generated by your build tool.
93 The paths are relative to the <code>destination</code>.
94 </p>
95 </td>
96 <td class="align-center">
97 <p><code class="option">keep_files: [DIR, FILE, ...]</code></p>
98 </td>
99 </tr>
100 <tr class="setting">
101 <td>
102 <p class="name"><strong>Time Zone</strong></p>
103 <p class="description">
104 Set the time zone for site generation. This sets the <code>TZ</code>
105 environment variable, which Ruby uses to handle time and date
106 creation and manipulation. Any entry from the
107 <a href="https://en.wikipedia.org/wiki/Tz_database">IANA Time Zone
108 Database</a> is valid, e.g. <code>America/New_York</code>. A list of all
109 available values can be found <a href="https://en.wikipedia.org/wiki/List_of_tz_database_time_zones">
110 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.
111 </p>
112 </td>
113 <td class="align-center">
114 <p><code class="option">timezone: TIMEZONE</code></p>
115 </td>
116 </tr>
117 <tr class="setting">
118 <td>
119 <p class="name"><strong>Encoding</strong></p>
120 <p class="description">
121 Set the encoding of files by name (only available for Ruby
122 1.9 or later).
123 The default value is <code>utf-8</code> starting in 2.0.0,
124 and <code>nil</code> before 2.0.0, which will yield the Ruby
125 default of <code>ASCII-8BIT</code>.
126 Available encodings can be shown by the
127 command <code>ruby -e 'puts Encoding::list.join("\n")'</code>.
128 </p>
129 </td>
130 <td class="align-center">
131 <p><code class="option">encoding: ENCODING</code></p>
132 </td>
133 </tr>
134 <tr>
135 <td>
136 <p class='name'><strong>Defaults</strong></p>
137 <p class='description'>
138 Set defaults for <a href="../frontmatter/" title="YAML Front Matter">YAML Front Matter</a>
139 variables.
140 </p>
141 </td>
142 <td class='align-center'>
143 <p>see <a href="#front-matter-defaults" title="details">below</a></p>
144 </td>
145 </tr>
146 </tbody>
147 </table>
148 </div>
149
150 <div class="note warning">
151 <h5>Destination folders are cleaned on site builds</h5>
152 <p>
153 The contents of <code>&lt;destination&gt;</code> are automatically
154 cleaned, by default, when the site is built. Files or folders that are not
155 created by your site will be removed. Some files could be retained
156 by specifying them within the <code>&lt;keep_files&gt;</code> configuration directive.
157 </p>
158 <p>
159 Do not use an important location for <code>&lt;destination&gt;</code>; instead, use it as
160 a staging area and copy files from there to your web server.
161 </p>
162 </div>
163
164 ### Build Command Options
165
166 <div class="mobile-side-scroller">
167 <table>
168 <thead>
169 <tr>
170 <th>Setting</th>
171 <th><span class="option">Options</span> and <span class="flag">Flags</span></th>
172 </tr>
173 </thead>
174 <tbody>
175 <tr class="setting">
176 <td>
177 <p class="name"><strong>Regeneration</strong></p>
178 <p class="description">Enable auto-regeneration of the site when files are modified.</p>
179 </td>
180 <td class="align-center">
181 <p><code class="flag">-w, --[no-]watch</code></p>
182 </td>
183 </tr>
184 <tr class="setting">
185 <td>
186 <p class="name"><strong>Configuration</strong></p>
187 <p class="description">Specify config files instead of using <code>_config.yml</code> automatically. Settings in later files override settings in earlier files.</p>
188 </td>
189 <td class="align-center">
190 <p><code class="flag">--config FILE1[,FILE2,...]</code></p>
191 </td>
192 </tr>
193 <tr class="setting">
194 <td>
195 <p class="name"><strong>Drafts</strong></p>
196 <p class="description">Process and render draft posts.</p>
197 </td>
198 <td class="align-center">
199 <p><code class="option">show_drafts: BOOL</code></p>
200 <p><code class="flag">--drafts</code></p>
201 </td>
202 </tr>
203 <tr class="setting">
204 <td>
205 <p class="name"><strong>Environment</strong></p>
206 <p class="description">Use a specific environment value in the build.</p>
207 </td>
208 <td class="align-center">
209 <p><code class="flag">JEKYLL_ENV=production</code></p>
210 </td>
211 </tr>
212 <tr class="setting">
213 <td>
214 <p class="name"><strong>Future</strong></p>
215 <p class="description">Publish posts or collection documents with a future date.</p>
216 </td>
217 <td class="align-center">
218 <p><code class="option">future: BOOL</code></p>
219 <p><code class="flag">--future</code></p>
220 </td>
221 </tr>
222 <tr class="setting">
223 <td>
224 <p class="name"><strong>Unpublished</strong></p>
225 <p class="description">Render posts that were marked as unpublished.</p>
226 </td>
227 <td class="align-center">
228 <p><code class="option">unpublished: BOOL</code></p>
229 <p><code class="flag">--unpublished</code></p>
230 </td>
231 </tr>
232 <tr class="setting">
233 <td>
234 <p class="name"><strong>LSI</strong></p>
235 <p class="description">Produce an index for related posts. Requires the <a href="http://www.classifier-reborn.com/">classifier-reborn</a> plugin.</p>
236 </td>
237 <td class="align-center">
238 <p><code class="option">lsi: BOOL</code></p>
239 <p><code class="flag">--lsi</code></p>
240 </td>
241 </tr>
242 <tr class="setting">
243 <td>
244 <p class="name"><strong>Limit Posts</strong></p>
245 <p class="description">Limit the number of posts to parse and publish.</p>
246 </td>
247 <td class="align-center">
248 <p><code class="option">limit_posts: NUM</code></p>
249 <p><code class="flag">--limit_posts NUM</code></p>
250 </td>
251 </tr>
252 <tr class="setting">
253 <td>
254 <p class="name"><strong>Force polling</strong></p>
255 <p class="description">Force watch to use polling.</p>
256 </td>
257 <td class="align-center">
258 <p><code class="flag">--force_polling</code></p>
259 </td>
260 </tr>
261 <tr class="setting">
262 <td>
263 <p class="name"><strong>Verbose output</strong></p>
264 <p class="description">Print verbose output.</p>
265 </td>
266 <td class="align-center">
267 <p><code class="flag">-V, --verbose</code></p>
268 </td>
269 </tr>
270 <tr class="setting">
271 <td>
272 <p class="name"><strong>Silence Output</strong></p>
273 <p class="description">Silence the normal output from Jekyll
274 during a build</p>
275 </td>
276 <td class="align-center">
277 <p><code class="flag">-q, --quiet</code></p>
278 </td>
279 </tr>
280 <tr class="setting">
281 <td>
282 <p class="name"><strong>Incremental build</strong></p>
283 <p class="description">
284 Enable the experimental incremental build feature. Incremental build only
285 re-builds posts and pages that have changed, resulting in significant performance
286 improvements for large sites, but may also break site generation in certain
287 cases.
288 </p>
289 </td>
290 <td class="align-center">
291 <p><code class="option">incremental: BOOL</code></p>
292 <p><code class="flag">-I, --incremental</code></p>
293 </td>
294 </tr>
295 <tr class="setting">
296 <td>
297 <p class="name"><strong>Liquid profiler</strong></p>
298 <p class="description">
299 Generate a Liquid rendering profile to help you identify performance bottlenecks.
300 </p>
301 </td>
302 <td class="align-center">
303 <p><code class="option">profile: BOOL</code></p>
304 <p><code class="flag">--profile</code></p>
305 </td>
306 </tr>
307 <tr class="setting">
308 <td>
309 <p class="name"><strong>Strict Front Matter</strong></p>
310 <p class="description">
311 Cause a build to fail if there is a YAML syntax error in a page's front matter.
312 </p>
313 </td>
314 <td class="align-center">
315 <p><code class="option">strict_front_matter: BOOL</code></p>
316 <p><code class="flag">--strict_front_matter</code></p>
317 </td>
318 </tr>
319 </tbody>
320 </table>
321 </div>
322
323
324 ### Serve Command Options
325
326 In addition to the options below, the `serve` sub-command can accept any of the options
327 for the `build` sub-command, which are then applied to the site build which occurs right
328 before your site is served.
329
330 <div class="mobile-side-scroller">
331 <table>
332 <thead>
333 <tr>
334 <th>Setting</th>
335 <th><span class="option">Options</span> and <span class="flag">Flags</span></th>
336 </tr>
337 </thead>
338 <tbody>
339 <tr class="setting">
340 <td>
341 <p class="name"><strong>Local Server Port</strong></p>
342 <p class="description">Listen on the given port.</p>
343 </td>
344 <td class="align-center">
345 <p><code class="option">port: PORT</code></p>
346 <p><code class="flag">--port PORT</code></p>
347 </td>
348 </tr>
349 <tr class="setting">
350 <td>
351 <p class="name"><strong>Local Server Hostname</strong></p>
352 <p class="description">Listen at the given hostname.</p>
353 </td>
354 <td class="align-center">
355 <p><code class="option">host: HOSTNAME</code></p>
356 <p><code class="flag">--host HOSTNAME</code></p>
357 </td>
358 </tr>
359 <tr class="setting">
360 <td>
361 <p class="name"><strong>Base URL</strong></p>
362 <p class="description">Serve the website from the given base URL</p>
363 </td>
364 <td class="align-center">
365 <p><code class="option">baseurl: URL</code></p>
366 <p><code class="flag">--baseurl URL</code></p>
367 </td>
368 </tr>
369 <tr class="setting">
370 <td>
371 <p class="name"><strong>Detach</strong></p>
372 <p class="description">Detach the server from the terminal</p>
373 </td>
374 <td class="align-center">
375 <p><code class="option">detach: BOOL</code></p>
376 <p><code class="flag">-B, --detach</code></p>
377 </td>
378 </tr>
379 <tr class="setting">
380 <td>
381 <p class="name"><strong>Skips the initial site build.</strong></p>
382 <p class="description">Skips the initial site build which occurs before the server is started.</p>
383 </td>
384 <td class="align-center">
385 <p><code class="flag">--skip-initial-build</code></p>
386 </td>
387 </tr>
388 <tr class="setting">
389 <td>
390 <p class="name"><strong>X.509 (SSL) Private Key</strong></p>
391 <p class="description">SSL Private Key.</p>
392 </td>
393 <td class="align-center">
394 <p><code class="flag">--ssl-key</code></p>
395 </td>
396 </tr>
397 <tr class="setting">
398 <td>
399 <p class="name"><strong>X.509 (SSL) Certificate</strong></p>
400 <p class="description">SSL Public certificate.</p>
401 </td>
402 <td class="align-center">
403 <p><code class="flag">--ssl-cert</code></p>
404 </td>
405 </tr>
406 </tbody>
407 </table>
408 </div>
409
410 <div class="note warning">
411 <h5>Do not use tabs in configuration files</h5>
412 <p>
413 This will either lead to parsing errors, or Jekyll will revert to the
414 default settings. Use spaces instead.
415 </p>
416 </div>
417
418 ## Custom WEBrick Headers
419
420 You can provide custom headers for your site by adding them to `_config.yml`
421
422 ```yaml
423 # File: _config.yml
424 webrick:
425 headers:
426 My-Header: My-Value
427 My-Other-Header: My-Other-Value
428 ```
429
430 ### Defaults
431
432 We provide by default `Content-Type` and `Cache-Control` response headers: one
433 dynamic in order to specify the nature of the data being served, the other
434 static in order to disable caching so that you don't have to fight with Chrome's
435 aggressive caching when you are in development mode.
436
437 ## Specifying a Jekyll environment at build time
438
439 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.
440
441 For example, suppose you set this conditional statement in your code:
442
443 ```liquid
444 {% raw %}
445 {% if jekyll.environment == "production" %}
446 {% include disqus.html %}
447 {% endif %}
448 {% endraw %}
449 ```
450
451 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:
452
453 ```sh
454 JEKYLL_ENV=production jekyll build
455 ```
456
457 Specifying an environment value allows you to make certain content available only within specific environments.
458
459 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.
460
461 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.
462
463 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.
464
465 ## Front Matter defaults
466
467 Using [YAML Front Matter](../frontmatter/) 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.
468
469 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.
470
471 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.
472
473 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.
474
475 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:
476
477 ```yaml
478 defaults:
479 -
480 scope:
481 path: "" # an empty string here means all files in the project
482 values:
483 layout: "default"
484 ```
485
486 <div class="note info">
487 <h5>Please stop and rerun `jekyll serve` command.</h5>
488 <p>
489 The <code>_config.yml</code> master configuration file contains global configurations
490 and variable definitions that are read once at execution time. Changes made to <code>_config.yml</code>
491 during automatic regeneration are not loaded until the next execution.
492 </p>
493 <p>
494 Note <a href="../datafiles">Data Files</a> are included and reloaded during automatic regeneration.
495 </p>
496 </div>
497
498 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.
499
500 ```yaml
501 defaults:
502 -
503 scope:
504 path: "" # an empty string here means all files in the project
505 type: "posts" # previously `post` in Jekyll 2.2.
506 values:
507 layout: "default"
508 ```
509
510 Now, this will only set the layout for files where the type is `posts`.
511 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.
512
513 As mentioned earlier, you can set multiple scope/values pairs for `defaults`.
514
515 ```yaml
516 defaults:
517 -
518 scope:
519 path: ""
520 type: "pages"
521 values:
522 layout: "my-site"
523 -
524 scope:
525 path: "projects"
526 type: "pages" # previously `page` in Jekyll 2.2.
527 values:
528 layout: "project" # overrides previous default layout
529 author: "Mr. Hyde"
530 ```
531
532 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](../variables/) set to `Mr. Hyde`.
533
534 ```yaml
535 collections:
536 my_collection:
537 output: true
538
539 defaults:
540 -
541 scope:
542 path: ""
543 type: "my_collection" # a collection in your site, in plural form
544 values:
545 layout: "default"
546 ```
547
548 In this example, the `layout` is set to `default` inside the
549 [collection](../collections/) with the name `my_collection`.
550
551 ### Glob patterns in Front Matter defaults
552
553 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" -%}
554
555 ```yaml
556 collections:
557 my_collection:
558 output: true
559
560 defaults:
561 -
562 scope:
563 path: "section/*/special-page.html"
564 values:
565 layout: "specific-layout"
566 ```
567
568 <div class="note warning">
569 <h5>Globbing and Performance</h5>
570 <p>
571 Please note that globbing a path is known to have a negative effect on
572 performance and is currently not optimized, especially on Windows.
573 Globbing a path will increase your build times in proportion to the size
574 of the associated collection directory.
575 </p>
576 </div>
577
578
579 ### Precedence
580
581 Jekyll will apply all of the configuration settings you specify in the `defaults` section of your `_config.yml` file. However, you can choose to override settings from other scope/values pair by specifying a more specific path for the scope.
582
583 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.
584
585 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:
586
587 ```yaml
588 # In _config.yml
589 ...
590 defaults:
591 -
592 scope:
593 path: "projects"
594 type: "pages"
595 values:
596 layout: "project"
597 author: "Mr. Hyde"
598 category: "project"
599 ...
600 ```
601
602 ```yaml
603 # In projects/foo_project.md
604 ---
605 author: "John Smith"
606 layout: "foobar"
607 ---
608 The post text goes here...
609 ```
610
611 The `projects/foo_project.md` would have the `layout` set to `foobar` instead
612 of `project` and the `author` set to `John Smith` instead of `Mr. Hyde` when
613 the site is built.
614
615 ## Default Configuration
616
617 Jekyll runs with the following configuration options by default. Alternative
618 settings for these options can be explicitly specified in the configuration
619 file or on the command-line.
620
621 <div class="note warning">
622 <h5>There are two unsupported kramdown options</h5>
623 <p>
624 Please note that both <code>remove_block_html_tags</code> and
625 <code>remove_span_html_tags</code> are currently unsupported in Jekyll due
626 to the fact that they are not included within the kramdown HTML converter.
627 </p>
628 </div>
629
630 ```yaml
631 # Where things are
632 source: .
633 destination: ./_site
634 collections_dir: .
635 plugins_dir: _plugins
636 layouts_dir: _layouts
637 data_dir: _data
638 includes_dir: _includes
639 collections:
640 posts:
641 output: true
642
643 # Handling Reading
644 safe: false
645 include: [".htaccess"]
646 exclude: ["Gemfile", "Gemfile.lock", "node_modules", "vendor/bundle/", "vendor/cache/", "vendor/gems/", "vendor/ruby/"]
647 keep_files: [".git", ".svn"]
648 encoding: "utf-8"
649 markdown_ext: "markdown,mkdown,mkdn,mkd,md"
650 strict_front_matter: false
651
652 # Filtering Content
653 show_drafts: null
654 limit_posts: 0
655 future: false
656 unpublished: false
657
658 # Plugins
659 whitelist: []
660 plugins: []
661
662 # Conversion
663 markdown: kramdown
664 highlighter: rouge
665 lsi: false
666 excerpt_separator: "\n\n"
667 incremental: false
668
669 # Serving
670 detach: false
671 port: 4000
672 host: 127.0.0.1
673 baseurl: "" # does not include hostname
674 show_dir_listing: false
675
676 # Outputting
677 permalink: date
678 paginate_path: /page:num
679 timezone: null
680
681 quiet: false
682 verbose: false
683 defaults: []
684
685 liquid:
686 error_mode: warn
687 strict_filters: false
688 strict_variables: false
689
690 # Markdown Processors
691 rdiscount:
692 extensions: []
693
694 redcarpet:
695 extensions: []
696
697 kramdown:
698 auto_ids: true
699 entity_output: as_char
700 toc_levels: 1..6
701 smart_quotes: lsquo,rsquo,ldquo,rdquo
702 input: GFM
703 hard_wrap: false
704 footnote_nr: 1
705 show_warnings: false
706 ```
707
708 ## Liquid Options
709
710 Liquid's response to errors can be configured by setting `error_mode`. The
711 options are
712
713 - `lax` --- Ignore all errors.
714 - `warn` --- Output a warning on the console for each error.
715 - `strict` --- Output an error message and stop the build.
716
717 You can also configure Liquid's renderer to catch non-assigned variables and
718 non-existing filters by setting `strict_variables` and / or `strict_filters`
719 to `true` respectively. {% include docs_version_badge.html version="3.8.0" %}
720
721 Do note that while `error_mode` configures Liquid's parser, the `strict_variables`
722 and `strict_filters` options configure Liquid's renderer and are consequently,
723 mutually exclusive.
724
725 ## Markdown Options
726
727 The various Markdown renderers supported by Jekyll sometimes have extra options
728 available.
729
730 ### Redcarpet
731
732 Redcarpet can be configured by providing an `extensions` sub-setting, whose
733 value should be an array of strings. Each string should be the name of one of
734 the `Redcarpet::Markdown` class's extensions; if present in the array, it will
735 set the corresponding extension to `true`.
736
737 Jekyll handles two special Redcarpet extensions:
738
739 - `no_fenced_code_blocks` --- By default, Jekyll sets the `fenced_code_blocks`
740 extension (for delimiting code blocks with triple tildes or triple backticks)
741 to `true`, probably because GitHub's eager adoption of them is starting to make
742 them inescapable. Redcarpet's normal `fenced_code_blocks` extension is inert
743 when used with Jekyll; instead, you can use this inverted version of the
744 extension for disabling fenced code.
745
746 Note that you can also specify a language for highlighting after the first
747 delimiter:
748
749 ```ruby
750 # ...ruby code
751 ```
752
753 With both fenced code blocks and highlighter enabled, this will statically
754 highlight the code; without any syntax highlighter, it will add a
755 `class="LANGUAGE"` attribute to the `<code>` element, which can be used as a
756 hint by various JavaScript code highlighting libraries.
757
758 - `smart` --- This pseudo-extension turns on SmartyPants, which converts
759 straight quotes to curly quotes and runs of hyphens to em (`---`) and en (`--`) dashes.
760
761 All other extensions retain their usual names from Redcarpet, and no renderer
762 options aside from `smart` can be specified in Jekyll. [A list of available
763 extensions can be found in the Redcarpet README file.][redcarpet_extensions]
764 Make sure you're looking at the README for the right version of
765 Redcarpet: Jekyll currently uses v3.2.x. The most commonly used
766 extensions are:
767
768 - `tables`
769 - `no_intra_emphasis`
770 - `autolink`
771
772 [redcarpet_extensions]: https://github.com/vmg/redcarpet/blob/v3.2.2/README.markdown#and-its-like-really-simple-to-use
773
774 ### Custom Markdown Processors
775
776 If you're interested in creating a custom markdown processor, you're in luck! Create a new class in the `Jekyll::Converters::Markdown` namespace:
777
778 ```ruby
779 class Jekyll::Converters::Markdown::MyCustomProcessor
780 def initialize(config)
781 require 'funky_markdown'
782 @config = config
783 rescue LoadError
784 STDERR.puts 'You are missing a library required for Markdown. Please run:'
785 STDERR.puts ' $ [sudo] gem install funky_markdown'
786 raise FatalException.new("Missing dependency: funky_markdown")
787 end
788
789 def convert(content)
790 ::FunkyMarkdown.new(content).convert
791 end
792 end
793 ```
794
795 Once you've created your class and have it properly set up either as a plugin
796 in the `_plugins` folder or as a gem, specify it in your `_config.yml`:
797
798 ```yaml
799 markdown: MyCustomProcessor
800 ```
801
802 ## Incremental Regeneration
803 <div class="note warning">
804 <h5>Incremental regeneration is still an experimental feature</h5>
805 <p>
806 While incremental regeneration will work for the most common cases, it will
807 not work correctly in every scenario. Please be extremely cautious when
808 using the feature, and report any problems not listed below by
809 <a href="https://github.com/jekyll/jekyll/issues/new">opening an issue on GitHub</a>.
810 </p>
811 </div>
812
813 Incremental regeneration helps shorten build times by only generating documents
814 and pages that were updated since the previous build. It does this by keeping
815 track of both file modification times and inter-document dependencies in the
816 `.jekyll-metadata` file.
817
818 Under the current implementation, incremental regeneration will only generate a
819 document or page if either it, or one of its dependencies, is modified. Currently,
820 the only types of dependencies tracked are includes (using the
821 {% raw %}`{% include %}`{% endraw %} tag) and layouts. This means that plain
822 references to other documents (for example, the common case of iterating over
823 `site.posts` in a post listings page) will not be detected as a dependency.
824
825 To remedy some of these shortfalls, putting `regenerate: true` in the front-matter
826 of a document will force Jekyll to regenerate it regardless of whether it has been
827 modified. Note that this will generate the specified document only; references
828 to other documents' contents will not work since they won't be re-rendered.
829
830 Incremental regeneration can be enabled via the `--incremental` flag (`-I` for
831 short) from the command-line or by setting `incremental: true` in your
832 configuration file.
10 * [Configuration Options]({{ '/docs/configuration/options/' | relative_url }})
11 * [Default Configuration]({{ '/docs/configuration/default/' | relative_url }})
12 * [Front Matter Defaults]({{ '/docs/configuration/front-matter-defaults/' | relative_url }})
13 * [Environments]({{ '/docs/configuration/environments/' | relative_url }})
14 * [Markdown Options]({{ '/docs/configuration/markdown/' | relative_url }})
15 * [Liquid Options]({{ '/docs/configuration/liquid/' | relative_url }})
16 * [Sass/SCSS Options]({{ '/docs/configuration/sass/' | relative_url }})
17 * [Webrick Options]({{ '/docs/configuration/webrick/' | relative_url }})
18 * [Incremental Regeneration]({{ '/docs/configuration/incremental-regeneration/' | relative_url }})
1919 ## 2. How it works
2020
2121 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.
22
23 ![Jekyll Build](https://buddy.works/data/blog/_images/buddyworks-jekyll-small.png)
2422
2523 [jekyll-docker-image]: https://hub.docker.com/r/jekyll/jekyll/
2624
5856
5957 [jekyll-docs-ci-buddy]: https://github.com/jekyll/jekyll/edit/master/docs/_docs/continuous-integration/buddyworks.md
6058 [jekyll-help]: https://jekyllrb.com/help/
61 [buddy-forum]: http://forum.buddy.works/
59 [buddy-forum]: https://forum.buddy.works/
1111
1212 To start building your project on CircleCI, all you need to do is 'follow' your project from CircleCI's website:
1313
14 1. Visit the 'Add Projects' page: <https://circleci.com/add-projects>
14 1. Visit the 'Add Projects' page
1515 1. From the GitHub or Bitbucket tab on the left, choose a user or organization.
1616 1. Find your project in the list and click 'Build project' on the right.
17 1. The first build will start on its own. You can start telling CircleCI how to build your project by creating a [circle.yml][3] file in the root of your repository.
17 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.
1818
19 [3]: https://circleci.com/docs/configuration/
19 [3]: https://circleci.com/docs/2.0/configuration-reference/
2020
2121 ## 2. Dependencies
2222
2727 ```ruby
2828 source 'https://rubygems.org'
2929
30 ruby '2.4.0'
30 ruby '2.7.4'
3131
3232 gem 'jekyll'
3333 gem 'html-proofer'
3434 ```
3535
36 CircleCI detects when `Gemfile` is present is will automatically run `bundle install` for you in the `dependencies` phase.
36 ```yaml
37 - step:
38 run: bundle install
39 ```
3740
3841 ## 3. Testing
3942
40 The most basic test that can be run is simply 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.
43 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.
4144
4245 ```yaml
43 dependencies:
44 post:
45 - bundle exec jekyll build
46 - step:
47 run: bundle exec jekyll build
4648 ```
4749
4850 ### HTML Proofer
5355 [6]: https://github.com/gjtorikian/html-proofer/blob/master/README.md#configuration
5456
5557 ```yaml
56 test:
57 post:
58 - bundle exec htmlproofer ./_site --check-html --disable-external
58 - step:
59 run: bundle exec htmlproofer ./_site --check-html --disable-external
5960 ```
6061
61 ## Complete Example circle.yml File
62 ## Complete Example .circleci/config.yml File
6263
63 When you put it all together, here's an example of what that `circle.yml` file could look like:
64 The example `.circleci/config.yml` below demonstrates how to
65 deploy your Jekyll project to AWS. In order for this to work you would first have to set the
66 `S3_BUCKET_NAME` [environment variable](https://circleci.com/docs/2.0/env-vars/).
6467
6568 ```yaml
66 machine:
67 environment:
68 NOKOGIRI_USE_SYSTEM_LIBRARIES: true # speeds up installation of html-proofer
69
70 dependencies:
71 post:
72 - bundle exec jekyll build
73
74 test:
75 post:
76 - bundle exec htmlproofer ./_site --allow-hash-href --check-favicon --check-html --disable-external
77
78 deployment:
79 prod:
80 branch: master
81 commands:
82 - rsync -va --delete ./_site username@my-website:/var/html
69 workflows:
70 test-deploy:
71 jobs:
72 - build
73 - deploy:
74 requires:
75 - build
76 filters:
77 branches:
78 only: master
79 version: 2.1
80 jobs:
81 build:
82 docker:
83 - image: cimg/ruby:2.7.4
84 environment:
85 BUNDLE_PATH: ~/repo/vendor/bundle
86 steps:
87 - checkout
88 - restore_cache:
89 keys:
90 - rubygems-v1-{% raw %}{{ checksum "Gemfile.lock" }}{% endraw %}
91 - rubygems-v1-fallback
92 - run:
93 name: Bundle Install
94 command: bundle check || bundle install
95 - save_cache:
96 key: rubygems-v1-{% raw %}{{ checksum "Gemfile.lock" }}{% endraw %}
97 paths:
98 - vendor/bundle
99 - run:
100 name: Jekyll build
101 command: bundle exec jekyll build
102 - run:
103 name: HTMLProofer tests
104 command: |
105 bundle exec htmlproofer ./_site \
106 --allow-hash-href \
107 --check-favicon \
108 --check-html \
109 --disable-external
110 - persist_to_workspace:
111 root: ./
112 paths:
113 - _site
114 deploy:
115 docker:
116 - image: cimg/python:3.9.1
117 environment:
118 S3_BUCKET_NAME: <<YOUR BUCKET NAME HERE>>
119 steps:
120 - attach_workspace:
121 at: ./
122 - run:
123 name: Install AWS CLI
124 command: pip install awscli --upgrade --user
125 - run:
126 name: Upload to s3
127 command: ~/.local/bin/aws s3 sync ./_site s3://$S3_BUCKET_NAME/ --delete --acl public-read
83128 ```
84129
85130 ## Questions?
0 ---
1 title: GitHub Actions
2 ---
3
4 When building a Jekyll site with GitHub Pages, the standard flow is restricted for security reasons
5 and to make it simpler to get a site setup. For more control over the build and still host the site
6 with GitHub Pages you can use GitHub Actions.
7
8 ## Advantages of using Actions
9
10 ### Control over gemset
11
12 - **Jekyll version** --- Instead of using the currently enabled version at `3.9.0`, you can use any
13 version of Jekyll you want. For example `{{site.version}}`, or point directly to the repository.
14 - **Plugins** --- You can use any Jekyll plugins irrespective of them being on the
15 [supported versions][ghp-whitelist] list, even `*.rb` files placed in the `_plugins` directory
16 of your site.
17 - **Themes** --- While using a custom theme is possible without Actions, it is now simpler.
18
19 ### Workflow Management
20
21 - **Customization** --- By creating a workflow file to run Actions, you can specify custom build
22 steps, use environment variables.
23 - **Logging** --- The build log is visible and can be tweaked to be verbose, so it is much easier to
24 debug errors using Actions.
25
26 ## Workspace setup
27
28 The first and foremost requirement is a Jekyll project hosted at GitHub. Choose an existing Jekyll
29 project or follow the [quickstart]({{ '/docs/' | relative_url }}) and push the repository to GitHub
30 if it is not hosted there already.
31
32 We're only going to cover builds from the `main` branch in this page. Therefore, ensure that you
33 are working on the `main` branch. If necessary, you may create it based on your default branch.
34 When the Action builds your site, the contents of the _destination_ directory will be automatically
35 pushed to the `gh-pages` branch with a commit, ready to be used for serving.
36
37 {: .note .warning}
38 The Action we're using here will create (or reset an existing) `gh-pages` branch on every successful
39 deploy.<br/> So, if you have an existing `gh-pages` branch that is used to deploy your production
40 build, ensure to make a backup of the contents into a different branch so that you can rollback
41 easily if necessary.
42
43 The Jekyll site we'll be using for the rest of this page initially consists of just a `_config.yml`,
44 an `index.md` page and a `Gemfile`. The contents are respectively:
45
46 ```yaml
47 # _config.yml
48
49 title: "Jekyll Actions Demo"
50 ```
51
52 {% raw %}
53
54 ```liquid
55 ---
56 ---
57
58 Welcome to My Home Page
59
60 {% assign date = '2020-04-13T10:20:00Z' %}
61
62 - Original date - {{ date }}
63 - With timeago filter - {{ date | timeago }}
64 ```
65
66 {% endraw %}
67
68 ```ruby
69 # Gemfile
70
71 source 'https://rubygems.org'
72
73 gem 'jekyll', '~> 4.2'
74
75 group :jekyll_plugins do
76 gem 'jekyll-timeago', '~> 0.13.1'
77 end
78 ```
79
80 {: .note .info}
81 The demo site uses Jekyll 4 and a [third-party plugin][timeago-plugin], both of which are currently
82 not whitelisted for use on GitHub pages. The plugin will allow us to describe how far back a date
83 was from today. e.g. If we give a date as `2016-03-23T10:20:00Z` and the current date is
84 `2020-04-13T10:20:00Z`, then the output would be `4 years and 3 weeks ago`.
85
86 {: .note .info}
87 The action we're using takes care of installing the Ruby gems and dependencies. While that keeps
88 the setup simple for the user, one may encounter issues if they also check-in `Gemfile.lock` if it
89 was generated with an old version of Bundler.
90
91 ### Setting up the Action
92
93 GitHub Actions are registered for a repository by using a YAML file inside the directory path
94 `.github/workflows` (note the dot at the start). For simplicity, here we use one of the
95 [Jekyll Actions](#external-links) to show you how to use the action.
96
97 Create a **workflow file**, say `github-pages.yml`, using either the GitHub interface or by pushing
98 a YAML file to the workflow directory path manually. The base contents are:
99
100 {% raw %}
101
102 ```yaml
103 name: Build and deploy Jekyll site to GitHub Pages
104
105 on:
106 push:
107 branches:
108 - main # or master before October 2020
109
110 jobs:
111 github-pages:
112 runs-on: ubuntu-latest
113 steps:
114 - uses: actions/checkout@v2
115 - uses: actions/cache@v2
116 with:
117 path: vendor/bundle
118 key: ${{ runner.os }}-gems-${{ hashFiles('**/Gemfile') }}
119 restore-keys: |
120 ${{ runner.os }}-gems-
121 - uses: helaili/jekyll-action@2.0.5 # Choose any one of the Jekyll Actions
122 with: # Some relative inputs of your action
123 token: ${{ secrets.GITHUB_TOKEN }}
124 ```
125
126 {% endraw %}
127
128 The above workflow can be explained as the following:
129
130 - We trigger the build using **on.push** condition for `main` branch only --- this prevents
131 the Action from overwriting the `gh-pages` branch on any feature branch pushes.
132 - The **name** of the job matches our YAML filename: `github-pages`.
133 - The **checkout** action takes care of cloning your repository.
134 - The **cache** action is an optimization to avoid fetching and installing gems on every build.
135 - We specify our selected **action** and **version number** using `helaili/jekyll-action@2.0.5`,
136 this handles the build and deploy. You can choose any one of the Jekyll Actions that matches
137 your project and flavor from [GitHub Marketplace](https://github.com/marketplace?type=actions&query=jekyll+action).
138 - We set a reference to a secret **environment variable** for the action to use. The `GITHUB_TOKEN`
139 is a secret token automatically initialized at the start of every workflow run.
140 More information can be found in [GitHub documentation](https://docs.github.com/en/actions/security-guides/automatic-token-authentication#about-the-github_token-secret).
141
142 Instead of using the **on.push** condition, you could trigger your build on a **schedule** by
143 using the [on.schedule] parameter. For example, here we build daily at midnight by specifying
144 **cron** syntax, which can be tested at the [crontab guru] site.
145
146 ```yaml
147 on:
148 schedule:
149 - cron: "0 0 * * *"
150 ```
151
152 Note that this string must be quoted to prevent the asterisks from being evaluated incorrectly.
153
154 [on.schedule]: https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions#onschedule
155 [crontab guru]: https://crontab.guru/
156
157 ### Providing permissions
158
159 At the start of each workflow run, GitHub automatically creates a unique `GITHUB_TOKEN` secret to use in
160 your workflow. You can use the `GITHUB_TOKEN` to authenticate in a workflow run. You can use the
161 `GITHUB_TOKEN` by using the standard syntax for referencing secrets: `${{ secrets.GITHUB_TOKEN }}`.
162 For more information, please read [GitHub's docs on token authentication][github-token-ref]
163
164 [github-token-ref]: https://docs.github.com/en/actions/security-guides/automatic-token-authentication
165
166 If you need a token that requires permissions that aren't available in the `GITHUB_TOKEN`, you can create
167 a Personal Access Token (PAT), and set it as a secret in your repository for this action to push to the
168 `gh-pages` branch:
169
170 1. On your GitHub profile, under **Developer Settings**, go to the [Personal Access Tokens][tokens]
171 section.
172 2. **Create** a token. Give it a name like "GitHub Actions" and ensure it has permissions to
173 `public_repos` (or the entire `repo` scope for private repository) --- necessary for the action
174 to commit to the `gh-pages` branch.
175 3. **Copy** the token value.
176 4. Go to your repository's **Settings** and then the **Secrets** tab.
177 5. **Create** a token named `YOUR_CUSTOM_TOKEN` (_important_). Give it a value using the value copied
178 above.
179
180 ### Build and deploy
181
182 On pushing any local changes onto `main`, the action will be triggered and the build will
183 **start**.
184
185 To watch the progress and see any build errors, check on the build **status** using one of the
186 following approaches:
187
188 - **View by commit**
189 - Go to the repository level view in GitHub. Under the most recent commit (near the top) you’ll
190 see a **status symbol** next to the commit message as a tick or _X_. Hover over it and click
191 the **details** link.
192 - **Actions tab**
193 - Go to the repository's Actions tab. Click on the `jekyll` workflow tab.
194
195 If all goes well, all steps will be green and the built assets will now exist on the `gh-pages`
196 branch.
197
198 On a successful build, GitHub Pages will **publish** the site stored on the repository `gh-pages`
199 branches. Note that you do not need to setup a `gh-pages` branch or enable GitHub Pages, as the
200 action will take care of this for you.
201 (For private repositories, you'll have to upgrade to a paid plan).
202
203 To see the **live site**:
204
205 1. Go to the **environment** tab on your repository.
206 2. Click **View Deployment** to see the deployed site URL.
207 3. View your site at the **URL**. Make sure the `timeago` filter works as expected.
208 4. Optionally **add** this URL to your repository's main page and to your `README.md`, to make it
209 easy for people to find.
210
211 When you need to make further **changes** to the site, commit to `master` and push. The workflow
212 will build and deploy your site again.
213
214 Be sure **not to edit** the `gh-pages` branch directly, as any changes will be lost on the next
215 successful deploy from the Action.
216
217 ## External links
218
219 - [jekyll-actions] is an action available on the GitHub Marketplace and was used in this guide.
220 - [jekyll-actions-quickstart] is an unofficial repository that includes a live demo of the
221 `jekyll-actions` action. That project can be used as a template for making a new site.
222 - [jekyll-action-ts] is another action to build and publish Jekyll sites on GiHub Pages that includes HTML formatting options with Prettier and caching.
223 - [jekyll-deploy-action] is a GitHub Action to deploy the Jekyll site conveniently for GitHub Pages (An alternative action with better speed and compatibility).
224
225 [ghp-whitelist]: https://pages.github.com/versions/
226 [timeago-plugin]: https://rubygems.org/gems/jekyll-timeago
227 [tokens]: https://github.com/settings/tokens
228 [jekyll-actions]: https://github.com/marketplace/actions/jekyll-actions
229 [jekyll-actions-quickstart]: https://github.com/MichaelCurrin/jekyll-actions-quickstart
230 [jekyll-action-ts]: https://github.com/limjh16/jekyll-action-ts
231 [jekyll-deploy-action]: https://github.com/jeffreytse/jekyll-deploy-action
+0
-10
docs/_docs/continuous-integration/index.md less more
0 ---
1 title: Continuous Integration
2 permalink: /docs/continuous-integration/
3 ---
4
5 Continuous Integration (CI) enables you to publish your Jekyll generated website with confidence by automating the quality assurance and deployment processes. You can quickly get started using CI with one of the providers below:
6
7 * [Travis CI](travis-ci)
8 * [CircleCI](circleci)
9 * [Buddy](buddyworks)
0 ---
1 title: "Razorops"
2 ---
3
4 [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.
5 Razorops has all the capabilities that you would expect from a CI/CD platform such as
6 1. Code compilation/build
7 2. Artifact packaging
8 3. Testing Automation(unit, integration, acceptance etc.)
9 4. Faster builds and shipping to production
10
11 Razorops is a single solution that implements the whole pipeline from start to deployment.
12
13 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.
14
15 [razorops-homepage]: https://razorops.com/
16 [docker-homepage]: https://www.docker.com/
17 [github-homepage]: https://github.com
18 [bitbucket-homepage]: https://bitbucket.org/
19 [gitlab-homepage]: https://gitlab.com
20 [deploy-s3]: https://razorops.com/blog/how-to-deploy-a-static-website-to-aws-s3-with-razorops-ci-cd/
21
22 ## 1. Getting started
23
24 1. Log in at [https://razorops.com/][razorops-homepage] with your GitHub/Bitbucket or Gitlab account
25 2. Create a pipeline, choose your Git provider and select your Jekyll Project
26 3. Add .razorops.yaml file in your root directory of your project
27 4. Add environment var and your deployment is ready
28 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]
29
30 ## 2. How it works
31
32 Whenever you make a push to the selected branch, your steps auto runs as defined in .razorops.yaml file
33
34 ```yaml
35 tasks:
36 build-and-deploy:
37 steps:
38 - checkout
39 # commands to build jekyll website
40 - commands:
41 - bundle install
42 - JEKYLL_ENV=production bundle exec jekyll build
43 # Commands to upload static pages folder to AWS S3 or ftp
44 # Set AWS access key & secrets environment variables under
45 # Razorops dashboard project pipelines
46 - commands:
47 - aws s3 rm s3://$AWS_S3_BUCKET --recursive
48 - aws s3 cp _site s3://$AWS_S3_BUCKET --recursive
49 if: branch == 'main'
50
51 ```
52
53
54
55 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.
56
57 Razorops is FREE for opensource projects, Try it Now
58 [https://razorops.com/][razorops-homepage]
59
60
11 title: "Travis CI"
22 ---
33
4 You can easily test your website build against one or more versions of Ruby.
4 You can test your website build against one or more versions of Ruby.
55 The following guide will show you how to set up a free build environment on
66 [Travis][travis], with [GitHub][github] integration for pull requests.
77
1010
1111 ## 1. Enabling Travis and GitHub
1212
13 Enabling Travis builds for your GitHub repository is pretty simple:
13 To enable Travis builds for your GitHub repository:
1414
1515 1. Go to your profile on travis-ci.org: https://travis-ci.org/profile/username
1616 2. Find the repository for which you're interested in enabling builds.
2020
2121 ## 2. The Test Script
2222
23 The simplest test script simply runs `jekyll build` and ensures that Jekyll
23 The simplest test script runs `jekyll build` and ensures that Jekyll
2424 doesn't fail to build the site. It doesn't check the resulting site, but it
2525 does ensure things are built properly.
2626
3333
3434 ### The HTML Proofer Executable
3535
36 ```sh
36 ```bash
3737 #!/usr/bin/env bash
3838 set -e # halt script on error
3939
7575 environment. Below is a sample `.travis.yml` file, followed by
7676 an explanation of each line.
7777
78 **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:
78 **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":
7979
8080 ```ruby
8181 source "https://rubygems.org"
8989 ```yaml
9090 language: ruby
9191 rvm:
92 - 2.3.3
92 - 2.6.3
9393
9494 before_script:
9595 - chmod +x ./script/cibuild # or do this locally and commit
104104 - gh-pages # test the gh-pages branch
105105 - /pages-(.*)/ # test every branch which starts with "pages-"
106106
107 env:
108 global:
109 - NOKOGIRI_USE_SYSTEM_LIBRARIES=true # speeds up installation of html-proofer
110
111 sudo: false # route your build to the container-based infrastructure for a faster build
107 addons:
108 apt:
109 packages:
110 - libcurl4-openssl-dev
111
112 cache: bundler # caching bundler gem packages will speed up build
113
114 # Optional: disable email notifications about the outcome of your builds
115 notifications:
116 email: false
112117 ```
113118
114119 Ok, now for an explanation of each line:
122127
123128 ```yaml
124129 rvm:
125 - 2.3.3
130 - 2.6.3
126131 ```
127132
128133 RVM is a popular Ruby Version Manager (like rbenv, chruby, etc). This
129134 directive tells Travis the Ruby version to use when running your test
130 script.
135 script. Use a [version which is pre-installed on the Travis build docker][5]
136 image to speed up the build.
131137
132138 ```yaml
133139 before_script:
176182 The `branches` directive is completely optional. Travis will build from every
177183 push to any branch of your repo if leave it out.
178184
179 ```yaml
180 env:
181 global:
182 - NOKOGIRI_USE_SYSTEM_LIBRARIES=true # speeds up installation of html-proofer
183 ```
184
185 Using `html-proofer`? You'll want this environment variable. Nokogiri, used
186 to parse HTML files in your compiled site, comes bundled with libraries
187 which it must compile each time it is installed. Luckily, you can
188 dramatically decrease the install time of Nokogiri by setting the
189 environment variable `NOKOGIRI_USE_SYSTEM_LIBRARIES` to `true`.
190
191185 <div class="note warning">
192186 <h5>Be sure to exclude <code>vendor</code> from your
193187 <code>_config.yml</code></h5>
199193 exclude: [vendor]
200194 ```
201195
202 By default you should supply the `sudo: false` command to Travis. This command
203 explicitly tells Travis to run your build on Travis's [container-based
204 infrastructure](https://docs.travis-ci.com/user/workers/container-based-infrastructure/#Routing-your-build-to-container-based-infrastructure). Running on the container-based infrastructure can often times
205 speed up your build. If you have any trouble with your build, or if your build
206 does need `sudo` access, modify the line to `sudo: required`.
207
208 ```yaml
209 sudo: false
196 To speed up the build, you should cache the gem packages created by `bundler`.
197 Travis has a pre-defined [cache strategy for this tool][6] which should have
198 all the default configs to do exactly that.
199
200 ```yaml
201 cache: bundler
202 ```
203
204 Optionally, if you are not interested in the build email notifications you
205 can disable them with this configuration. Travis supports a wide array of
206 notification services, you may find [another one more useful (e.g. slack)][7].
207
208 ```yaml
209 notifications:
210 email: false
210211 ```
211212
212213 ### Troubleshooting
226227
227228 [3]: https://github.com/jekyll/jekyll/edit/master/docs/_docs/continuous-integration/travis-ci.md
228229 [4]: https://jekyllrb.com/help/
230 [5]: https://docs.travis-ci.com/user/languages/ruby/#Specifying-Ruby-versions-and-implementations
231 [6]: https://docs.travis-ci.com/user/caching/#Caching-directories-(Bundler%2C-dependencies)
232 [7]: https://docs.travis-ci.com/user/notifications/
77
88 ## Where to get help or report a problem
99
10 See [the support guidelines](https://jekyllrb.com/docs/support/)
10 See the [support guidelines](https://jekyllrb.com/docs/support/)
1111
1212 ## Ways to contribute
1313
1414 Whether you're a developer, a designer, or just a Jekyll devotee, there are lots of ways to contribute. Here's a few ideas:
1515
16 * [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.
17 * 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?
18 * 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.
19 * 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.
20 * 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.
21 * Help evaluate [open pull requests](https://github.com/jekyll/jekyll/pulls), by testing the changes locally and reviewing what's proposed.
16 - [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.
17 - 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?
18 - 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.
19 - 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.
20 - 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.
21 - Help evaluate [open pull requests](https://github.com/jekyll/jekyll/pulls), by testing the changes locally and reviewing what's proposed.
2222
2323 ## Submitting a pull request
2424
2525 ### Pull requests generally
2626
27 * The smaller the proposed change, the better. If you'd like to propose two unrelated changes, submit two pull requests.
27 - The smaller the proposed change, the better. If you'd like to propose two unrelated changes, submit two pull requests.
2828
29 * 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.
29 - 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.
3030
31 * Pull requests are easy and fun. If this is your first pull request, it may help to [understand GitHub Flow](https://guides.github.com/introduction/flow/).
31 - If this is your first pull request, it may help to [understand GitHub Flow](https://guides.github.com/introduction/flow/).
3232
33 * If you're submitting a code contribution, be sure to read the [code contributions](#code-contributions) section below.
33 - If you're submitting a code contribution, be sure to read the [code contributions](#code-contributions) section below.
3434
3535 ### Submitting a pull request via github.com
3636
5252 2. Clone the repository locally `git clone https://github.com/<you-username>/jekyll`.
5353 3. Create a new, descriptively named branch to contain your change ( `git checkout -b my-awesome-feature` ).
5454 4. Hack away, add tests. Not necessarily in that order.
55 5. Make sure everything still passes by running `script/cibuild` (see [the tests section](#running-tests-locally) below)
55 5. Make sure everything still passes by running `script/cibuild` (see the [tests section](#running-tests-locally) below)
5656 6. Push the branch up ( `git push origin my-awesome-feature` ).
5757 7. Create a pull request by visiting `https://github.com/<your-username>/jekyll` and following the instructions at the top of the screen.
5858
7979 5. Click `Generate Font` on the bottom-horizontal-bar.
8080 6. Inspect the included icons and proceed by clicking `Download`.
8181 7. Extract the font files and adapt the CSS to the paths we use in Jekyll:
82 - Copy the entire `fonts` directory over and overwrite existing ones at `<jekyll>/docs/`.
83 - Copy the contents of `selection.json` and overwrite existing content inside `<jekyll>/docs/icomoon-selection.json`.
84 - Copy the entire `@font-face {}` declaration and only the **new-icon(s)' css declarations** further below, to update the
82
83 - Copy the entire `fonts` directory over and overwrite existing ones at `<jekyll>/docs/`.
84 - Copy the contents of `selection.json` and overwrite existing content inside `<jekyll>/docs/icomoon-selection.json`.
85 - Copy the entire `@font-face {}` declaration and only the **new-icon(s)' css declarations** further below, to update the
8586 `<jekyll>/docs/_sass/_font-awesome.scss` sass partial.
86 - Fix paths in the `@font-face {}` declaration by adding `../` before `fonts/FontAwesome.*` like so:
87 - Fix paths in the `@font-face {}` declaration by adding `../` before `fonts/FontAwesome.*` like so:
8788 `('../fonts/Fontawesome.woff?9h6hxj')`.
8889
8990 ### Adding plugins
9293
9394 ## Code Contributions
9495
95 Interesting in submitting a pull request? Awesome. Read on. There's a few common gotchas that we'd love to help you avoid.
96 Interested in submitting a pull request? Awesome. Read on. There's a few common gotchas that we'd love to help you avoid.
9697
9798 ### Tests and documentation
9899
104105
105106 #### Tests
106107
107 * 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).
108 - 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).
108109
109 * If it's a brand new feature, create a new [Cucumber](https://github.com/cucumber/cucumber/) feature, reusing existing steps where appropriate.
110 - If it's a brand new feature, create a new [Cucumber](https://github.com/cucumber/cucumber/) feature, reusing existing steps where appropriate.
110111
111112 ### Code contributions generally
112113
113 * 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.
114 - 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.
114115
115 * Don't bump the Gem version in your pull request (if you don't know what that means, you probably didn't).
116 - Don't bump the Gem version in your pull request (if you don't know what that means, you probably didn't).
116117
117 * You can use the command `script/console` to start a REPL to explore the result of
118 Jekyll's methods. It also provides you with helpful methods to quickly create a
119 site or configuration. [Feel free to check it out!](https://github.com/jekyll/jekyll/blob/master/script/console)
118 - You can use the command `script/console` to start a REPL to explore the result of
119 Jekyll's methods. It also provides you with helpful methods to quickly create a
120 site or configuration. [Feel free to check it out!](https://github.com/jekyll/jekyll/blob/master/script/console)
121
122 - 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)
120123
121124 ## Running tests locally
122125
149152 Both `script/test` and `script/cucumber` can be run without arguments to
150153 run its entire respective suite.
151154
155 ## Visual Studio Code Development Container
156
157 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.
158
152159 ## A thank you
153160
154161 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!
22 permalink: /docs/datafiles/
33 ---
44
5 In addition to the [built-in variables](../variables/) available from Jekyll,
5 In addition to the [built-in variables]({{'/docs/variables/' | relative_url }}) available from Jekyll,
66 you can specify your own custom data that can be accessed via the [Liquid
7 templating system](https://wiki.github.com/shopify/liquid/liquid-for-designers).
7 templating system](https://github.com/Shopify/liquid/wiki/Liquid-for-Designers).
88
9 Jekyll supports loading data from [YAML](http://yaml.org/), [JSON](http://www.json.org/),
10 and [CSV](https://en.wikipedia.org/wiki/Comma-separated_values) files located in the `_data` directory.
11 Note that CSV files *must* contain a header row.
9 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.
10 Note that CSV and TSV files *must* contain a header row.
1211
1312 This powerful feature allows you to avoid repetition in your templates and to
1413 set site specific options without changing `_config.yml`.
1716
1817 ## The Data Folder
1918
20 As explained on the [directory structure](../structure/) page, the `_data`
21 folder is where you can store additional data for Jekyll to use when generating
22 your site. These files must be YAML, JSON, or CSV files (using either
23 the `.yml`, `.yaml`, `.json` or `.csv` extension), and they will be
19 The `_data` folder is where you can store additional data for Jekyll to use when
20 generating your site. These files must be YAML, JSON, TSV or CSV files (using either
21 the `.yml`, `.yaml`, `.json`, `.tsv`, or `.csv` extension), and they will be
2422 accessible via `site.data`.
2523
2624 ## Example: List of members
4341
4442 Or `_data/members.csv`:
4543
46 ```text
44 ```
4745 name,github
4846 Eric Mill,konklone
4947 Parker Moore,parkr
5048 Liu Fengyun,liufengyun
5149 ```
5250
53 This data can be accessed via `site.data.members` (notice that the filename
54 determines the variable name).
51 This data can be accessed via `site.data.members` (notice that the file's *basename* determines the variable name and
52 therefore one should avoid having data files with the same basename but different extensions, in the same directory).
5553
5654 You can now render the list of members in a template:
5755
6967 ```
7068 {% endraw %}
7169
72 {: .note .info }
73 If your Jekyll site has a lot of pages, such as with documentation websites, see the detailed examples in [how to build robust navigation for your site]({% link _tutorials/navigation.md %}).
74
75 ## Example: Organizations
70 ## Subfolders
7671
7772 Data files can also be placed in sub-folders of the `_data` folder. Each folder
7873 level will be added to a variable's namespace. The example below shows how
133128 twitter: DavidSilvaSmith
134129 ```
135130
136 The author can then be specified as a page variable in a post's frontmatter:
131 The author can then be specified as a page variable in a post's front matter:
137132
138133 {% raw %}
139134 ```liquid
151146 ```
152147 {% endraw %}
153148
154 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).
149 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 }}).
150
151 ## CSV/TSV Parse Options
152
153 The way Ruby parses CSV and TSV files can be customized with the `csv_reader` and `tsv_reader`
154 configuration options. Each configuration key exposes the same options:
155
156 `converters`: What [CSV converters](https://ruby-doc.org/stdlib-2.5.0/libdoc/csv/rdoc/CSV.html#Converters) should be
157 used when parsing the file. Available options are `integer`, `float`, `numeric`, `date`, `date_time` and
158 `all`. By default, this list is empty.
159 `encoding`: What encoding the files are in. Defaults to the site `encoding` configuration option.
160 `headers`: Boolean field for whether to parse the first line of the file as headers. When `false`, it treats the
161 first row as data. Defaults to `true`.
162
163 Examples:
164
165 ```yaml
166 csv_reader:
167 converters:
168 - numeric
169 - datetime
170 headers: true
171 encoding: utf-8
172 tsv_reader:
173 converters:
174 - all
175 headers: false
176 ```
0 ---
1 title: Automated Deployment
2 permalink: /docs/deployment/automated/
3 ---
4 There are a number of ways to easily automate the deployment of a Jekyll site.
5
6 ## Continuous Integration Service
7
8 One of the easiest ways to set up an automated deployment flow is by using a
9 CI.
10
11 These services run a script when there's a commit on your Git repository.
12 You might want this script to build the site, run tests over the output then deploy it to the
13 service of your choice.
14
15 We have guides for the following providers:
16
17 * [GitHub Actions]({{ '/docs/continuous-integration/github-actions/' | relative_url }})
18 * [Travis CI]({{ '/docs/continuous-integration/travis-ci/' | relative_url }})
19 * [CircleCI]({{ '/docs/continuous-integration/circleci/' | relative_url }})
20 * [Buddy]({{ '/docs/continuous-integration/buddyworks/' | relative_url }})
21 * [Razorops CI/CD]({{ '/docs/continuous-integration/razorops/' | relative_url }})
22
23 ## Git post-receive hook
24
25 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:
26
27 ```sh
28 laptop$ ssh deployer@example.com
29 server$ mkdir myrepo.git
30 server$ cd myrepo.git
31 server$ git --bare init
32 server$ cp hooks/post-receive.sample hooks/post-receive
33 server$ mkdir /var/www/myrepo
34 ```
35
36 Next, add the following lines to hooks/post-receive and be sure Jekyll is
37 installed on the server:
38
39 ```bash
40 #!/bin/bash -l
41
42 # Install Ruby Gems to ~/gems
43 export GEM_HOME=$HOME/gems
44 export PATH=$GEM_HOME/bin:$PATH
45
46 TMP_GIT_CLONE=$HOME/tmp/myrepo
47 GEMFILE=$TMP_GIT_CLONE/Gemfile
48 PUBLIC_WWW=/var/www/myrepo
49
50 git clone $GIT_DIR $TMP_GIT_CLONE
51 BUNDLE_GEMFILE=$GEMFILE bundle install
52 BUNDLE_GEMFILE=$GEMFILE bundle exec jekyll build -s $TMP_GIT_CLONE -d $PUBLIC_WWW
53 rm -Rf $TMP_GIT_CLONE
54 exit
55 ```
56
57 Finally, run the following command on any users laptop that needs to be able to
58 deploy using this hook:
59
60 ```sh
61 laptops$ git remote add deploy deployer@example.com:~/myrepo.git
62 ```
63
64 Deploying is now as easy as telling nginx or Apache to look at
65 `/var/www/myrepo` and running the following:
66
67 ```sh
68 laptops$ git push deploy master
69 ```
0 ---
1 title: Manual Deployment
2 permalink: /docs/deployment/manual/
3 ---
4
5 Jekyll generates your static site to the `_site` directory by default. You can
6 transfer the contents of this directory to almost any hosting provider to get
7 your site live. Here are some manual ways of achieving this:
8
9 ## rsync
10
11 Rsync is similar to scp except it can be faster as it will only send changed
12 parts of files as opposed to the entire file. You can learn more about using
13 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).
14
15 ## Amazon S3
16
17 If you want to host your site in Amazon S3, you can do so by
18 using the [s3_website](https://github.com/laurilehmijoki/s3_website)
19 application. It will push your site to Amazon S3 where it can be served like
20 any web server,
21 dynamically scaling to almost unlimited traffic. This approach has the
22 benefit of being about the cheapest hosting option available for
23 low-volume blogs as you only pay for what you use.
24
25 ## FTP
26
27 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.
28
29 ## scp
30
31 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.
32
33 ## Rack-Jekyll
34
35 [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).
0 ---
1 title: 3rd Party
2 permalink: /docs/deployment/third-party/
3 ---
4
5
6 ## AWS Amplify
7
8 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.
9
10 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.
11
12 ## Bip
13
14 [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.
15
16 ## CloudCannon
17
18 [CloudCannon](https://cloudcannon.com) has everything you need to build, host
19 and update Jekyll websites. Take advantage of our global CDN, automated SSL,
20 continuous deployment and [more](https://cloudcannon.com/features/).
21
22 ## GitHub Pages
23
24 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/).
25
26 ## GitLab Pages
27
28 [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.
29
30 ## KeyCDN
31
32 [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.
33 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.
34
35 ## Kickster
36
37 Use [Kickster](https://kickster.nielsenramon.com/) for automated deploys to GitHub Pages when using unsupported plugins on GitHub Pages.
38
39 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.
40
41 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).
42
43 ## Netlify
44
45 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.
46
47 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.
48
49 ## Render
50
51 [Render](https://render.com) provides zero config continuous deployment for static sites. The service is free under 100GB monthly bandwidth.
52
53 ## Hostman
54
55 [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.
56
57 ## Static Publisher
58
59 [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).
60
61 ## Vercel
62
63 [Vercel](https://vercel.com/) provides zero config continuous deployment, HTTPS Custom domains, high performance smart CDN, you get instant static deploy for free.
64
65 ## 21YunBox
66
67 [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.
68
69 Read this [Jekyll step-by-step guide](https://www.21yunbox.com/docs/#/deploy-jekyll) to deploy your Jekyll site on 21YunBox.
70
71 ## Layer0
72
73 [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).
+0
-216
docs/_docs/deployment-methods.md less more
0 ---
1 title: Deployment methods
2 permalink: /docs/deployment-methods/
3 ---
4
5 Sites built using Jekyll can be deployed in a large number of ways due to the static nature of the generated output. A few of the most common deployment techniques are described below.
6
7 <div class="note">
8 <h5>ProTip™: Use GitHub Pages for zero-hassle Jekyll hosting</h5>
9 <p>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 <a href="../github-pages/">host your Jekyll-powered website for free</a>.</p>
10 </div>
11
12 ## Netlify
13
14 Netlify provides Global CDN, Continuous Deployment, one click HTTPS and [much more](https://www.netlify.com/features/), providing developers the most robust toolset available for modern web projects, without added complexity. Netlify supports custom plugins for Jekyll and has a free plan for open source projects.
15
16 Read this [Jekyll step-by-step guide](https://www.netlify.com/blog/2015/10/28/a-step-by-step-guide-jekyll-3.0-on-netlify/) to setup your Jekyll site on Netlify.
17
18 ## Aerobatic
19
20 [Aerobatic](https://www.aerobatic.com) has custom domains, global CDN distribution, basic auth, CORS proxying, and a growing list of plugins all included.
21
22 Automating the deployment of a Jekyll site is simple. See their [Jekyll docs](https://www.aerobatic.com/docs/static-site-generators/#jekyll) for more details. Your built `_site` folder is deployed to their highly-available, globally distributed hosting service.
23
24 ## Kickster
25
26 Use [Kickster](http://kickster.nielsenramon.com/) for easy (automated) deploys to GitHub Pages when using unsupported plugins on GitHub Pages.
27
28 Kickster provides a basic Jekyll project setup packed with web best practises and useful optimization tools increasing your overall project quality. Kickster ships with automated and worry-free deployment scripts for GitHub Pages.
29
30 Setting up Kickster is very easy, just install the 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).
31
32 ## Web hosting providers (FTP)
33
34 Just about any traditional web hosting provider will let you upload files to their servers over FTP. To upload a Jekyll site to a web host using FTP, simply 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.
35
36 ## Self-managed web server
37
38 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. Just remember to make sure the contents of the generated `_site` folder get placed in the appropriate web root directory for your web server.
39
40 ## Automated methods
41
42 There are also a number of ways to easily automate the deployment of a Jekyll site. If you’ve got another method that isn’t listed below, we’d love it if you [contributed](../contributing/) so that everyone else can benefit too.
43
44 ### Git post-update hook
45
46 If you store your Jekyll site in [Git](https://git-scm.com/) (you are using
47 version control, right?), it’s pretty easy to automate the
48 deployment process by setting up a post-update hook in your Git
49 repository, [like
50 this](http://web.archive.org/web/20091223025644/http://www.taknado.com/en/2009/03/26/deploying-a-jekyll-generated-site/).
51
52 ### Git post-receive hook
53
54 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:
55
56 ```sh
57 laptop$ ssh deployer@example.com
58 server$ mkdir myrepo.git
59 server$ cd myrepo.git
60 server$ git --bare init
61 server$ cp hooks/post-receive.sample hooks/post-receive
62 server$ mkdir /var/www/myrepo
63 ```
64
65 Next, add the following lines to hooks/post-receive and be sure Jekyll is
66 installed on the server:
67
68 ```bash
69 GIT_REPO=$HOME/myrepo.git
70 TMP_GIT_CLONE=$HOME/tmp/myrepo
71 GEMFILE=$TMP_GIT_CLONE/Gemfile
72 PUBLIC_WWW=/var/www/myrepo
73
74 git clone $GIT_REPO $TMP_GIT_CLONE
75 BUNDLE_GEMFILE=$GEMFILE bundle install
76 BUNDLE_GEMFILE=$GEMFILE bundle exec jekyll build -s $TMP_GIT_CLONE -d $PUBLIC_WWW
77 rm -Rf $TMP_GIT_CLONE
78 exit
79 ```
80
81 Finally, run the following command on any users laptop that needs to be able to
82 deploy using this hook:
83
84 ```sh
85 laptops$ git remote add deploy deployer@example.com:~/myrepo.git
86 ```
87
88 Deploying is now as easy as telling nginx or Apache to look at
89 `/var/www/myrepo` and running the following:
90
91 ```sh
92 laptops$ git push deploy master
93 ```
94
95 ### Static Publisher
96
97 [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).
98
99 ### Rake
100
101 Another way to deploy your Jekyll site is to use [Rake](https://github.com/ruby/rake), [HighLine](https://github.com/JEG2/highline), and
102 [Net::SSH](https://github.com/net-ssh/net-ssh). A more complex example of deploying Jekyll with Rake that deals with multiple branches can be found in [Git Ready](https://github.com/gitready/gitready/blob/cdfbc4ec5321ff8d18c3ce936e9c749dbbc4f190/Rakefile).
103
104
105 ### scp
106
107 Once you’ve generated the `_site` directory, you can easily scp its content using a
108 `tasks/deploy` shell script similar to [this deploy script][]. You’d obviously
109 need to change the values to reflect your site’s details. There is even [a
110 matching TextMate command][] that will help you run this script.
111
112 [this deploy script]: https://github.com/henrik/henrik.nyh.se/blob/master/script/deploy
113
114 [a matching TextMate command]: https://gist.github.com/henrik/214959
115
116 ### rsync
117
118 Once you’ve generated the `_site` directory, you can easily rsync its content using a `tasks/deploy` shell script similar to [this deploy script here](https://github.com/vitalyrepin/vrepinblog/blob/master/transfer.sh). You’d obviously need to change the values to reflect your site’s details.
119
120 Certificate-based authorization is another way to simplify the publishing
121 process. It makes sense to restrict rsync access only to the directory which it is supposed to sync. This can be done using rrsync.
122
123 #### Step 1: Install rrsync to your home folder (server-side)
124
125 If it is not already installed by your host, you can do it yourself:
126
127 - [Download rrsync](https://ftp.samba.org/pub/unpacked/rsync/support/rrsync)
128 - Place it in the `bin` subdirectory of your home folder (`~/bin`)
129 - Make it executable (`chmod +x`)
130
131 #### Step 2: Set up certificate-based SSH access (server side)
132
133 This [process](https://wiki.gentoo.org/wiki/SSH#Passwordless_Authentication) is
134 described in several places online. What is different from the typical approach
135 is to put the restriction to certificate-based authorization in
136 `~/.ssh/authorized_keys`. Then, launch `rrsync` and supply
137 it with the folder it shall have read-write access to:
138
139 ```sh
140 command="$HOME/bin/rrsync <folder>",no-agent-forwarding,no-port-forwarding,no-pty,no-user-rc,no-X11-forwarding ssh-rsa <cert>
141 ```
142
143 `<folder>` is the path to your site. E.g., `~/public_html/you.org/blog-html/`.
144
145 #### Step 3: Rsync (client-side)
146
147 Add the `deploy` script to the site source folder:
148
149 ```sh
150 #!/bin/sh
151
152 rsync -crvz --rsh='ssh -p2222' --delete-after --delete-excluded <folder> <user>@<site>:
153 ```
154
155 Command line parameters are:
156
157 - `--rsh=ssh -p2222` &mdash; The port for SSH access. It is required if
158 your host uses a different port than the default (e.g, HostGator)
159 - `<folder>` &mdash; The name of the local output folder (defaults to `_site`)
160 - `<user>` &mdash; The username for your hosting account
161 - `<site>` &mdash; Your hosting server
162
163 Using this setup, you might run the following command:
164
165 ```sh
166 rsync -crvz --rsh='ssh -p2222' --delete-after --delete-excluded _site/ hostuser@example.org:
167 ```
168
169 Don't forget the column `:` after server name!
170
171 #### Step 4 (Optional): Exclude the transfer script from being copied to the output folder.
172
173 This step is recommended if you use these instructions to deploy your site. If
174 you put the `deploy` script in the root folder of your project, Jekyll will
175 copy it to the output folder. This behavior can be changed in `_config.yml`.
176
177 Just add the following line:
178
179 ```yaml
180 # Do not copy these files to the output directory
181 exclude: ["deploy"]
182 ```
183
184 Alternatively, you can use an `rsync-exclude.txt` file to control which files will be transferred to your server.
185
186 #### Done!
187
188 Now it's possible to publish your website simply by running the `deploy`
189 script. If your SSH certificate is [passphrase-protected](https://martin.kleppmann.com/2013/05/24/improving-security-of-ssh-private-keys.html), you will be asked to enter it when the
190 script executes.
191
192 ## Rack-Jekyll
193
194 [Rack-Jekyll](https://github.com/adaoraul/rack-jekyll/) is an easy way 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).
195
196 Read [this post](http://andycroll.com/ruby/serving-a-jekyll-blog-using-heroku/) on how to deploy to Heroku using Rack-Jekyll.
197
198 ## Jekyll-Admin for Rails
199
200 If you want to maintain Jekyll inside your existing Rails app, [Jekyll-Admin](https://github.com/zkarpinski/Jekyll-Admin) contains drop in code to make this possible. See Jekyll-Admin’s [README](https://github.com/zkarpinski/Jekyll-Admin/blob/master/README) for more details.
201
202 ## Amazon S3
203
204 If you want to host your site in Amazon S3, you can do so by
205 using the [s3_website](https://github.com/laurilehmijoki/s3_website)
206 application. It will push your site to Amazon S3 where it can be served like
207 any web server,
208 dynamically scaling to almost unlimited traffic. This approach has the
209 benefit of being about the cheapest hosting option available for
210 low-volume blogs as you only pay for what you use.
211
212 ## OpenShift
213
214 If you'd like to deploy your site to an OpenShift gear, there's [a cartridge
215 for that](https://github.com/openshift-quickstart/jekyll-openshift).
0 ---
1 title: Deployment
2 permalink: /docs/deployment/
3 redirect_from: "/docs/deployment-methods/index.html"
4 ---
5
6 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:
7
8 * [Manually]({{ '/docs/deployment/manual/' | relative_url }})
9 * [Automated]({{ '/docs/deployment/automated/' | relative_url }})
10 * [Third Party]({{ '/docs/deployment/third-party/' | relative_url }})
+0
-19
docs/_docs/drafts.md less more
0 ---
1 title: Working with drafts
2 permalink: /docs/drafts/
3 ---
4
5 Drafts are posts without a date. They're posts you're still working on and
6 don't want to publish yet. To get up and running with drafts, create a
7 `_drafts` folder in your site's root (as described in the [site structure](/docs/structure/) section) and create your
8 first draft:
9
10 ```text
11 |-- _drafts/
12 | |-- a-draft-post.md
13 ```
14
15 To preview your site with drafts, simply run `jekyll serve` or `jekyll build`
16 with the `--drafts` switch. Each will be assigned the value modification time
17 of the draft file for its date, and thus you will see currently edited drafts
18 as the latest posts.
+0
-31
docs/_docs/extras.md less more
0 ---
1 title: Extras
2 permalink: /docs/extras/
3 ---
4
5 There are a number of (optional) extra features that Jekyll supports that you
6 may want to install, depending on how you plan to use Jekyll.
7
8 ## Web Highlights and Commenting
9
10 Register your site with [txtpen](https://txtpen.com). Then append
11
12 ```html
13 <script src="https://txtpen.com/embed.js?site=<your site name>"></script>
14 ```
15
16 to your template files in `/_layout` folder.
17
18 ## Math Support
19
20 Kramdown comes with optional support for LaTeX to PNG rendering via [MathJax](https://www.mathjax.org) within math blocks. See the Kramdown documentation on [math blocks](http://kramdown.gettalong.org/syntax.html#math-blocks) and [math support](http://kramdown.gettalong.org/converter/html.html#math-support) for more details. MathJax requires you to include JavaScript or CSS to render the LaTeX, e.g.
21
22 ```html
23 <script src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.0/MathJax.js?config=TeX-AMS-MML_HTMLorMML" type="text/javascript"></script>
24 ```
25
26 For more information about getting started, check out [this excellent blog post](http://gastonsanchez.com/visually-enforced/opinion/2014/02/16/Mathjax-with-jekyll/).
27
28 ## Alternative Markdown Processors
29
30 See the Markdown section on the [configuration page](/docs/configuration/#markdown-options) for instructions on how to use and configure alternative Markdown processors, as well as how to create [custom processors](/docs/configuration/#custom-markdown-processors).
0 ---
1 title: Front Matter
2 permalink: /docs/front-matter/
3 redirect_from: /docs/frontmatter/index.html
4 ---
5
6 Any file that contains a [YAML](https://yaml.org/) front matter block will be
7 processed by Jekyll as a special file. The front matter must be the first thing
8 in the file and must take the form of valid YAML set between triple-dashed
9 lines. Here is a basic example:
10
11 ```yaml
12 ---
13 layout: post
14 title: Blogging Like a Hacker
15 ---
16 ```
17
18 Between these triple-dashed lines, you can set predefined variables (see below
19 for a reference) or even create custom ones of your own. These variables will
20 then be available for you to access using Liquid tags both further down in the
21 file and also in any layouts or includes that the page or post in question
22 relies on.
23
24 <div class="note warning">
25 <h5>UTF-8 Character Encoding Warning</h5>
26 <p>
27 If you use UTF-8 encoding, make sure that no <code>BOM</code> header
28 characters exist in your files or very, very bad things will happen to
29 Jekyll. This is especially relevant if you’re running
30 <a href="{{ '/docs/installation/windows/' | relative_url }}">Jekyll on Windows</a>.
31 </p>
32 </div>
33
34 <div class="note">
35 <h5>Front Matter Variables Are Optional</h5>
36 <p>
37 If you want to use <a href="{{ '/docs/variables/' | relative_url }}">Liquid tags and variables</a>
38 but don’t need anything in your front matter, just leave it empty! The set
39 of triple-dashed lines with nothing in between will still get Jekyll to
40 process your file. (This is useful for things like CSS and RSS feeds!)
41 </p>
42 </div>
43
44 ## Predefined Global Variables
45
46 There are a number of predefined global variables that you can set in the
47 front matter of a page or post.
48
49 <div class="mobile-side-scroller">
50 <table>
51 <thead>
52 <tr>
53 <th>Variable</th>
54 <th>Description</th>
55 </tr>
56 </thead>
57 <tbody>
58 <tr>
59 <td>
60 <p><code>layout</code></p>
61 </td>
62 <td>
63 <p>
64
65 If set, this specifies the layout file to use. Use the layout file
66 name without the file extension. Layout files must be placed in the
67 <code>_layouts</code> directory.
68
69 </p>
70 <ul>
71 <li>
72 Using <code>null</code> will produce a file without using a layout
73 file. This is overridden if the file is a post/document and has a
74 layout defined in the <a href="{{ '/docs/configuration/front-matter-defaults/' | relative_url }}">
75 front matter defaults</a>.
76 </li>
77 <li>
78 Starting from version 3.5.0, using <code>none</code> in a post/document will
79 produce a file without using a layout file regardless of front matter defaults.
80 Using <code>none</code> in a page will cause Jekyll to attempt to
81 use a layout named "none".
82 </li>
83 </ul>
84 </td>
85 </tr>
86 <tr>
87 <td>
88 <p><code>permalink</code></p>
89 </td>
90 <td>
91 <p>
92
93 If you need your processed blog post URLs to be something other than
94 the site-wide style (default <code>/year/month/day/title.html</code>), then you can set
95 this variable and it will be used as the final URL.
96
97 </p>
98 </td>
99 </tr>
100 <tr>
101 <td>
102 <p><code>published</code></p>
103 </td>
104 <td>
105 <p>
106 Set to false if you don’t want a specific post to show up when the
107 site is generated.
108 </p>
109 </td>
110 </tr>
111 </tbody>
112 </table>
113 </div>
114
115 <div class="note">
116 <h5>Render Posts Marked As Unpublished</h5>
117 <p>
118 To preview unpublished pages, run `jekyll serve` or `jekyll build`
119 with the `--unpublished` switch. Jekyll also has a handy <a href="{{ '/docs/posts/#drafts' | relative_url }}">drafts</a>
120 feature tailored specifically for blog posts.
121 </p>
122 </div>
123
124 ## Custom Variables
125
126 You can also set your own front matter variables you can access in Liquid. For
127 instance, if you set a variable called `food`, you can use that in your page:
128
129 {% raw %}
130 ```liquid
131 ---
132 food: Pizza
133 ---
134
135 <h1>{{ page.food }}</h1>
136 ```
137 {% endraw %}
138
139 ## Predefined Variables for Posts
140
141 These are available out-of-the-box to be used in the front matter for a post.
142
143 <div class="mobile-side-scroller">
144 <table>
145 <thead>
146 <tr>
147 <th>Variable</th>
148 <th>Description</th>
149 </tr>
150 </thead>
151 <tbody>
152 <tr>
153 <td>
154 <p><code>date</code></p>
155 </td>
156 <td>
157 <p>
158 A date here overrides the date from the name of the post. This can be
159 used to ensure correct sorting of posts. A date is specified in the
160 format <code>YYYY-MM-DD HH:MM:SS +/-TTTT</code>; hours, minutes, seconds, and timezone offset
161 are optional.
162 </p>
163 </td>
164 </tr>
165 <tr>
166 <td>
167 <p><code>category</code></p>
168 <p><code>categories</code></p>
169 </td>
170 <td>
171 <p>
172
173 Instead of placing posts inside of folders, you can specify one or
174 more categories that the post belongs to. When the site is generated
175 the post will act as though it had been set with these categories
176 normally. Categories (plural key) can be specified as a <a
177 href="https://en.wikipedia.org/wiki/YAML#Basic_components">YAML list</a> or a
178 space-separated string.
179
180 </p>
181 </td>
182 </tr>
183 <tr>
184 <td>
185 <p><code>tags</code></p>
186 </td>
187 <td>
188 <p>
189
190 Similar to categories, one or multiple tags can be added to a post.
191 Also like categories, tags can be specified as a <a
192 href="https://en.wikipedia.org/wiki/YAML#Basic_components">YAML list</a> or a
193 space-separated string.
194
195 </p>
196 </td>
197 </tr>
198 </tbody>
199 </table>
200 </div>
201
202 <div class="note">
203 <h5>Don't repeat yourself</h5>
204 <p>
205 If you don't want to repeat your frequently used front matter variables
206 over and over, define
207 <a href="{{ '/docs/configuration/front-matter-defaults/' | relative_url }}" title="Front Matter defaults">defaults</a>
208 for them and only override them where necessary (or not at all). This works
209 both for predefined and custom variables.
210 </p>
211 </div>
+0
-213
docs/_docs/frontmatter.md less more
0 ---
1 title: Front Matter
2 permalink: /docs/frontmatter/
3 ---
4
5 The front matter is where Jekyll starts to get really cool. Any file that
6 contains a [YAML](http://yaml.org/) front matter block will be processed by
7 Jekyll as a special file. The front matter must be the first thing in the file
8 and must take the form of valid YAML set between triple-dashed lines. Here is a
9 basic example:
10
11 ```yaml
12 ---
13 layout: post
14 title: Blogging Like a Hacker
15 ---
16 ```
17
18 Between these triple-dashed lines, you can set predefined variables (see below
19 for a reference) or even create custom ones of your own. These variables will
20 then be available to you to access using Liquid tags both further down in the
21 file and also in any layouts or includes that the page or post in question
22 relies on.
23
24 <div class="note warning">
25 <h5>UTF-8 Character Encoding Warning</h5>
26 <p>
27 If you use UTF-8 encoding, make sure that no <code>BOM</code> header
28 characters exist in your files or very, very bad things will happen to
29 Jekyll. This is especially relevant if you’re running
30 <a href="../windows/">Jekyll on Windows</a>.
31 </p>
32 </div>
33
34 <div class="note">
35 <h5>ProTip™: Front Matter Variables Are Optional</h5>
36 <p>
37 If you want to use <a href="../variables/">Liquid tags and variables</a>
38 but don’t need anything in your front matter, just leave it empty! The set
39 of triple-dashed lines with nothing in between will still get Jekyll to
40 process your file. (This is useful for things like CSS and RSS feeds!)
41 </p>
42 </div>
43
44 ## Predefined Global Variables
45
46 There are a number of predefined global variables that you can set in the
47 front matter of a page or post.
48
49 <div class="mobile-side-scroller">
50 <table>
51 <thead>
52 <tr>
53 <th>Variable</th>
54 <th>Description</th>
55 </tr>
56 </thead>
57 <tbody>
58 <tr>
59 <td>
60 <p><code>layout</code></p>
61 </td>
62 <td>
63 <p>
64
65 If set, this specifies the layout file to use. Use the layout file
66 name without the file extension. Layout files must be placed in the
67 <code>_layouts</code> directory.
68
69 </p>
70 <ul>
71 <li>
72 Using <code>null</code> will produce a file without using a layout
73 file. However this is overridden if the file is a post/document and has a
74 layout defined in the <a href="../configuration/#front-matter-defaults">
75 frontmatter defaults</a>.
76 </li>
77 <li>
78 Starting from version 3.5.0, using <code>none</code> in a post/document will
79 produce a file without using a layout file regardless of frontmatter defaults.
80 Using <code>none</code> in a page, however, will cause Jekyll to attempt to
81 use a layout named "none".
82 </li>
83 </ul>
84 </td>
85 </tr>
86 <tr>
87 <td>
88 <p><code>permalink</code></p>
89 </td>
90 <td>
91 <p>
92
93 If you need your processed blog post URLs to be something other than
94 the site-wide style (default <code>/year/month/day/title.html</code>), then you can set
95 this variable and it will be used as the final URL.
96
97 </p>
98 </td>
99 </tr>
100 <tr>
101 <td>
102 <p><code>published</code></p>
103 </td>
104 <td>
105 <p>
106 Set to false if you don’t want a specific post to show up when the
107 site is generated.
108 </p>
109 </td>
110 </tr>
111 </tbody>
112 </table>
113 </div>
114
115 <div class="note">
116 <h5>ProTip™: Render Posts Marked As Unpublished</h5>
117 <p>
118 To preview unpublished pages, simply run `jekyll serve` or `jekyll build`
119 with the `--unpublished` switch. Jekyll also has a handy <a href="../drafts/">drafts</a>
120 feature tailored specifically for blog posts.
121 </p>
122 </div>
123
124 ## Custom Variables
125
126 Any variables in the front matter that are not predefined are mixed into the
127 data that is sent to the Liquid templating engine during the conversion. For
128 instance, if you set a title, you can use that in your layout to set the page
129 title:
130
131 ```liquid
132 <!DOCTYPE HTML>
133 <html>
134 <head>
135 <title>{% raw %}{{ page.title }}{% endraw %}</title>
136 </head>
137 <body>
138
139 ```
140
141 ## Predefined Variables for Posts
142
143 These are available out-of-the-box to be used in the front matter for a post.
144
145 <div class="mobile-side-scroller">
146 <table>
147 <thead>
148 <tr>
149 <th>Variable</th>
150 <th>Description</th>
151 </tr>
152 </thead>
153 <tbody>
154 <tr>
155 <td>
156 <p><code>date</code></p>
157 </td>
158 <td>
159 <p>
160 A date here overrides the date from the name of the post. This can be
161 used to ensure correct sorting of posts. A date is specified in the
162 format <code>YYYY-MM-DD HH:MM:SS +/-TTTT</code>; hours, minutes, seconds, and timezone offset
163 are optional.
164 </p>
165 </td>
166 </tr>
167 <tr>
168 <td>
169 <p><code>category</code></p>
170 <p><code>categories</code></p>
171 </td>
172 <td>
173 <p>
174
175 Instead of placing posts inside of folders, you can specify one or
176 more categories that the post belongs to. When the site is generated
177 the post will act as though it had been set with these categories
178 normally. Categories (plural key) can be specified as a <a
179 href="https://en.wikipedia.org/wiki/YAML#Basic_components">YAML list</a> or a
180 space-separated string.
181
182 </p>
183 </td>
184 </tr>
185 <tr>
186 <td>
187 <p><code>tags</code></p>
188 </td>
189 <td>
190 <p>
191
192 Similar to categories, one or multiple tags can be added to a post.
193 Also like categories, tags can be specified as a <a
194 href="https://en.wikipedia.org/wiki/YAML#Basic_components">YAML list</a> or a
195 space-separated string.
196
197 </p>
198 </td>
199 </tr>
200 </tbody>
201 </table>
202 </div>
203
204 <div class="note">
205 <h5>ProTip™: Don't repeat yourself</h5>
206 <p>
207 If you don't want to repeat your frequently used front matter variables
208 over and over, just define <a href="../configuration/#front-matter-defaults" title="Front Matter defaults">defaults</a>
209 for them and only override them where necessary (or not at all). This works
210 both for predefined and custom variables.
211 </p>
212 </div>
1010
1111 Your site is automatically generated by GitHub Pages when you push your source
1212 files. Note that GitHub Pages works equally well for regular HTML content,
13 simply because Jekyll treats files without YAML front matter as static assets.
13 simply because Jekyll treats files without front matter as static assets.
1414 So if you only need to push generated HTML, you're good to go without any
1515 further setup.
1616
17 Never built a website with GitHub Pages before? [See this marvelous guide by
18 Jonathan McGlone](http://jmcglone.com/guides/github-pages/) to get you up and
19 running. This guide will teach you what you need to know about Git, GitHub, and
20 Jekyll to create your very own website on GitHub Pages.
17 The [GitHub Pages Documentation](https://docs.github.com/en/free-pro-team@latest/github/working-with-github-pages)
18 is comprehensive and includes a [a guide to setting up a GitHub Pages site using
19 Jekyll](https://docs.github.com/en/free-pro-team@latest/github/working-with-github-pages/setting-up-a-github-pages-site-with-jekyll).
20 We recommend following this guide.
2121
22 ## The github-pages gem
23
24 Our friends at GitHub have provided the
25 [github-pages](https://github.com/github/pages-gem) gem which is used to manage
26 [Jekyll and its dependencies on GitHub Pages](https://pages.github.com/versions/).
27 Using it in your projects means that when you deploy your site to GitHub Pages,
28 you will not be caught by unexpected differences between various versions of the
29 gems.
30
31 Note that GitHub Pages runs in `safe` mode and only allows [a set of whitelisted
32 plugins](https://help.github.com/articles/configuring-jekyll-plugins/#default-plugins).
33
34 To use the currently-deployed version of the gem in your project, add the
35 following to your `Gemfile`:
36
37 ```ruby
38 source "https://rubygems.org"
39
40 gem "github-pages", group: :jekyll_plugins
41 ```
42
43 Be sure to run `bundle update` often.
22 This page contains some additional information which may be useful when working
23 on GitHub Pages sites with Jekyll.
4424
4525 <div class="note">
4626 <h5>GitHub Pages Documentation, Help, and Support</h5>
4727 <p>
4828 For more information about what you can do with GitHub Pages, as well as for
4929 troubleshooting guides, you should check out
50 <a href="https://help.github.com/categories/github-pages-basics/">GitHub’s Pages Help section</a>.
30 <a href="https://docs.github.com/en/free-pro-team@latest/github/working-with-github-pages">GitHub’s Pages Help section</a>.
5131 If all else fails, you should contact <a href="https://github.com/contact">GitHub Support</a>.
5232 </p>
5333 </div>
5535 ### Project Page URL Structure
5636
5737 Sometimes it's nice to preview your Jekyll site before you push your `gh-pages`
58 branch to GitHub. However, the subdirectory-like URL structure GitHub uses for
38 branch to GitHub. The subdirectory-like URL structure GitHub uses for
5939 Project Pages complicates the proper resolution of URLs. In order to assure your
60 site builds properly, use the handy [URL filters](../templates/#filters):
40 site builds properly, use the handy [URL filters]({{ '/docs/liquid/filters/' | relative_url }}):
6141
6242 {% raw %}
6343 ```liquid
6444 <!-- For styles with static names... -->
65 <link href="{{ "/assets/css/style.css" | relative_url }}" rel="stylesheet">
45 <link href="{{ 'assets/css/style.css' | relative_url }}" rel="stylesheet">
6646 <!-- For documents/pages whose URLs can change... -->
6747 [{{ page.title }}]("{{ page.url | relative_url }}")
6848 ```
7555 ## Deploying Jekyll to GitHub Pages
7656
7757 GitHub Pages work by looking at certain branches of repositories on GitHub.
78 There are two basic types available: [user/organization and project pages](https://help.github.com/articles/user-organization-and-project-pages/).
58 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).
7959 The way to deploy these two types of sites are nearly identical, except for a
8060 few minor details.
8161
11494 folder]({{ site.repository }}/tree/master/docs) of the same repository.
11595
11696 Please refer to GitHub official documentation on
117 [user, organization and project pages](https://help.github.com/articles/user-organization-and-project-pages/)
97 [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)
11898 to see more detailed examples.
11999
120100 <div class="note warning">
121101 <h5>Source files must be in the root directory</h5>
122102 <p>
123 GitHub Pages <a href="https://help.github.com/articles/troubleshooting-github-pages-build-failures#source-setting">overrides</a>
124 the <a href="/docs/configuration/#global-configuration">“Site Source”</a>
103 GitHub Pages <a href="https://help.github.com/en/github/working-with-github-pages/troubleshooting-jekyll-build-errors-for-github-pages-sites">overrides</a>
104 the <a href="{{ '/docs/configuration/options/' | relative_url }}">“Site Source”</a>
125105 configuration value, so if you locate your files anywhere other than the
126106 root directory, your site may not build correctly.
127107 </p>
134114 While Windows is not officially supported, it is possible
135115 to install the <code>github-pages</code> gem on Windows.
136116 Special instructions can be found on our
137 <a href="../windows/#installation">Windows-specific docs page</a>.
117 <a href="{{ '/docs/installation/windows/' | relative_url }}">Windows-specific docs page</a>.
138118 </p>
139119 </div>
120
121 ### Running and Testing Locally
122
123 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.
124
125 ```sh
126 bundle exec jekyll serve --baseurl=""
127 ```
128
129 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.
130
33 note: This file is autogenerated. Edit /History.markdown instead.
44 ---
55
6 ## 4.3.1 / 2022-10-26
7 {: #v4-3-1}
8
9 ### Bug Fixes
10 {: #bug-fixes-v4-3-1}
11
12 - Respect user-defined name attribute in documents ([#9167]({{ site.repository }}/issues/9167))
13 - Revert &#34;Incrementally rebuild when a data file is changed&#34; ([#9170]({{ site.repository }}/issues/9170))
14
15 ### Documentation
16
17 - Release post for v4.3.1 ([#9171]({{ site.repository }}/issues/9171))
18
19
20 ## 4.3.0 / 2022-10-20
21 {: #v4-3-0}
22
23 ### Minor Enhancements
24 {: #minor-enhancements-v4-3-0}
25
26 - Add webrick as a dependency ([#8524]({{ site.repository }}/issues/8524))
27 - Regenerate supported mime types ([#8542]({{ site.repository }}/issues/8542))
28 - Update include tag to be more permissive ([#8618]({{ site.repository }}/issues/8618))
29 - Optimize `Jekyll::Utils.parse_date` ([#8425]({{ site.repository }}/issues/8425))
30 - Update rubocop from 1.12 to 1.18 and min ruby from 2.4 to 2.5 ([#8741]({{ site.repository }}/issues/8741))
31 - Always hide cache-dir contents from Git ([#8798]({{ site.repository }}/issues/8798))
32 - Remove the warning about auto-regeneration on Windows ([#8821]({{ site.repository }}/issues/8821))
33 - Propagate _data folder from theme ([#8815]({{ site.repository }}/issues/8815))
34 - Support both tzinfo v1 and v2 along with non-half hour offsets. ([#8880]({{ site.repository }}/issues/8880))
35 - Run vendor-mimes to update mime.types ([#8940]({{ site.repository }}/issues/8940))
36 - Expose collection static files via `site.static_files` ([#8961]({{ site.repository }}/issues/8961))
37 - Expose `basename` from `document.rb` as `name` to Liquid templates ([#8761]({{ site.repository }}/issues/8761))
38 - Allow Configurable Converters on CSV ([#8858]({{ site.repository }}/issues/8858))
39 - Introduce `theme` drop to expose theme-gem details ([#9129]({{ site.repository }}/issues/9129))
40 - Relax version constraint to allow Rouge 4.x ([#9134]({{ site.repository }}/issues/9134))
41 - Incrementally rebuild when a data file is changed ([#8771]({{ site.repository }}/issues/8771))
42 - Support jekyll-sass-converter 3.x ([#9132]({{ site.repository }}/issues/9132))
43
44 ### Bug Fixes
45 {: #bug-fixes-v4-3-0}
46
47 - fix: pin rubocop to 1.12 due to error with ruby 2.4 ([#8651]({{ site.repository }}/issues/8651))
48 - Load Jekyll plugins from BUNDLE_GEMFILE location ([#8585]({{ site.repository }}/issues/8585))
49 - fix(security): CVE-2021-28834 ([#8680]({{ site.repository }}/issues/8680))
50 - Inject livereload script using `location.protocol` instead of `http:` ([#8718]({{ site.repository }}/issues/8718))
51 - Respect collections_dir config within include tag ([#8756]({{ site.repository }}/issues/8756))
52 - Fix regression in Convertible module from v4.2.0 ([#8786]({{ site.repository }}/issues/8786))
53 - Revert [#7253]({{ site.repository }}/issues/7253): &#34;Don&#39;t reset site.url to localhost:4000 by default&#34; ([#8620]({{ site.repository }}/issues/8620))
54 - Improve readability of CI logs ([#8877]({{ site.repository }}/issues/8877))
55 - Fix deprecation message for missing doc method ([#8960]({{ site.repository }}/issues/8960))
56 - Fix response header for content served via `jekyll serve` ([#8965]({{ site.repository }}/issues/8965))
57 - Trigger livereload in sites without pages ([#8337]({{ site.repository }}/issues/8337))
58 - Only enable BOM encoding option on UTF encodings ([#8363]({{ site.repository }}/issues/8363))
59 - Ensure theme config is a `Jekyll::Configuration` object ([#8988]({{ site.repository }}/issues/8988))
60 - Remove misleading totals row from `--profile` table ([#9039]({{ site.repository }}/issues/9039))
61 - Unlock Psych dependency ([#9135]({{ site.repository }}/issues/9135))
62 - Fix false positive conflicts for static files in a collection ([#9141]({{ site.repository }}/issues/9141))
63
64 ### Development Fixes
65 {: #development-fixes-v4-3-0}
66
67 - style: enable new cops ([#8538]({{ site.repository }}/issues/8538))
68 - Allow dependabot to keep github actions up-to-date ([#8540]({{ site.repository }}/issues/8540))
69 - Update actions/cache requirement to v2.1.3 ([#8543]({{ site.repository }}/issues/8543))
70 - Pin rubocop version ([#8564]({{ site.repository }}/issues/8564))
71 - style: add rubocop 1.9 cops ([#8567]({{ site.repository }}/issues/8567))
72 - Cross Version Testing Locally and Faster CI ([#8610]({{ site.repository }}/issues/8610))
73 - Use official Ruby setup GH action ([#8614]({{ site.repository }}/issues/8614))
74 - Spell check action for markdown documentation ([#8675]({{ site.repository }}/issues/8675))
75 - Update expect to cover docs/_posts ([#8677]({{ site.repository }}/issues/8677))
76 - Bump check-spelling/check-spelling from 0.0.18 to 0.0.19 ([#8740]({{ site.repository }}/issues/8740))
77 - Enable Rubocop accessor grouping, fix existing offenses ([#8293]({{ site.repository }}/issues/8293))
78 - Tags:Highlight: Decomposed HTMLLegacy formatter ([#8623]({{ site.repository }}/issues/8623))
79 - Relax Rubocop Dependency ([#8831]({{ site.repository }}/issues/8831))
80 - Add a workflow to build gems consistently ([#8830]({{ site.repository }}/issues/8830))
81 - Fix random test failures in TestExcerpt #to_liquid ([#8884]({{ site.repository }}/issues/8884))
82 - Lock gem `psych` to `v3.x` ([#8918]({{ site.repository }}/issues/8918))
83 - Fix typo in Bug Report template ([#8951]({{ site.repository }}/issues/8951))
84 - Check symlink outside site_source without Pathutil ([#9015]({{ site.repository }}/issues/9015))
85 - Stop testing with Rubies older than 2.7 on non-Windows ([#8955]({{ site.repository }}/issues/8955))
86 - Bump actions/checkout from 2 to 3 ([#8986]({{ site.repository }}/issues/8986))
87 - Remove git.io shortlinks from repo ([#9045]({{ site.repository }}/issues/9045))
88 - Bump rubocop to 1.32 ([#9093]({{ site.repository }}/issues/9093))
89 - Bump RuboCop to `1.36.x` ([#9125]({{ site.repository }}/issues/9125))
90 - Use check-spelling/check-spelling@v0.0.20 ([#9111]({{ site.repository }}/issues/9111))
91 - Disable pending cops when running rubocop ([#9136]({{ site.repository }}/issues/9136))
92 - Relax RDoc version dependency ([#9142]({{ site.repository }}/issues/9142))
93
94 ### Documentation
95
96 - typo - do instead of don&#39;t ([#8518]({{ site.repository }}/issues/8518))
97 - Document support for TSV files consistently ([#8488]({{ site.repository }}/issues/8488))
98 - Add a disclaimer to tutorials involving Ruby code ([#8525]({{ site.repository }}/issues/8525))
99 - Improve documentation on developing generators ([#8527]({{ site.repository }}/issues/8527))
100 - Fixes typo in layouts_dir documentation ([#8532]({{ site.repository }}/issues/8532))
101 - Fix i.e. typos in collections.md ([#8529]({{ site.repository }}/issues/8529))
102 - Remove GitHub Pages content which is in GitHub docs ([#8533]({{ site.repository }}/issues/8533))
103 - Step By Step Instructions Review ([#8399]({{ site.repository }}/issues/8399))
104 - Fix typo in migrating from 3.0 to 4.0 page ([#8572]({{ site.repository }}/issues/8572))
105 - Fix for important missing step in macOS Installation Docs: Add the Homebrew gems directory to the PATH ([#8496]({{ site.repository }}/issues/8496))
106 - Use latest Jekyll-action configuration ([#8579]({{ site.repository }}/issues/8579))
107 - docs: troubleshoot macOS with ARM64 architecture ([#8560]({{ site.repository }}/issues/8560))
108 - docs: add overview of .jekyll-cache dir ([#8648]({{ site.repository }}/issues/8648))
109 - docs: clarify where .jekyll-metadata comes from ([#8646]({{ site.repository }}/issues/8646))
110 - Razorops CI/CD added ([#8656]({{ site.repository }}/issues/8656))
111 - Specify default port and host for serve commands in docs ([#8624]({{ site.repository }}/issues/8624))
112 - Update third-party.md ([#8652]({{ site.repository }}/issues/8652))
113 - Add documentation for Sass configuration options ([#8587]({{ site.repository }}/issues/8587))
114 - Add formcarry to forms section ([#8471]({{ site.repository }}/issues/8471))
115 - Add step to set SDKROOT ([#8478]({{ site.repository }}/issues/8478))
116 - Improve the &#34;Markdown Options&#34; Docs ([#8681]({{ site.repository }}/issues/8681))
117 - Add &#39;webrick&#39; warning note to &#34;Quickstart&#34; Docs ([#8727]({{ site.repository }}/issues/8727))
118 - Update windows.md ([#8701]({{ site.repository }}/issues/8701))
119 - IRC networks - Libera, Freenode ([#8706]({{ site.repository }}/issues/8706))
120 - Improve GitHub Flavored Markdown Docs ([#8684]({{ site.repository }}/issues/8684))
121 - Fixing URL in MacOS install for rbenv-doctor ([#8693]({{ site.repository }}/issues/8693))
122 - Fix adjective in `troubleshooting.md` document ([#8777]({{ site.repository }}/issues/8777))
123 - Goodbye Frank. We&#39;ll miss you. 💔 ([#8807]({{ site.repository }}/issues/8807))
124 - Update index.html: Grammar fix. ([#8803]({{ site.repository }}/issues/8803))
125 - Prefer Libera. Remove Freenode. ([#8811]({{ site.repository }}/issues/8811))
126 - Update feature_request.md ([#8797]({{ site.repository }}/issues/8797))
127 - Remove AWS Amplify from the showcase ([#8812]({{ site.repository }}/issues/8812))
128 - Move Frank to Emeritus Core Team Members ([#8813]({{ site.repository }}/issues/8813))
129 - Release post for v4.2.1 ([#8818]({{ site.repository }}/issues/8818))
130 - Update CircleCI example ([#8829]({{ site.repository }}/issues/8829))
131 - Fix typo ([#8835]({{ site.repository }}/issues/8835))
132 - Added docs for running locally ([#8852]({{ site.repository }}/issues/8852))
133 - Linting README.markdown ([#8900]({{ site.repository }}/issues/8900))
134 - Remove text on GITHUB_TOKEN which is now built-in ([#8907]({{ site.repository }}/issues/8907))
135 - Add Security Policy document ([#8823]({{ site.repository }}/issues/8823))
136 - Manage repository meta documents consistently ([#8908]({{ site.repository }}/issues/8908))
137 - docs: add Layer0 deployment guide ([#8915]({{ site.repository }}/issues/8915))
138 - docs: Update README generated by `jekyll new-theme` ([#8919]({{ site.repository }}/issues/8919))
139 - Update resources.md ([#8925]({{ site.repository }}/issues/8925))
140 - Rewrite documentation on installing plugins ([#8921]({{ site.repository }}/issues/8921))
141 - Improve maintainers guide on releasing a new version ([#8928]({{ site.repository }}/issues/8928))
142 - Fix link for &#34;CloudSh&#34; ([#8934]({{ site.repository }}/issues/8934))
143 - Recommend using `actions/cache` in GitHub Actions documentation ([#8948]({{ site.repository }}/issues/8948))
144 - Remove references to EOL hakiri.io service ([#8946]({{ site.repository }}/issues/8946))
145 - Release post for v4.2.2 ([#8982]({{ site.repository }}/issues/8982))
146 - Document releasing off `*-stable` branches ([#8984]({{ site.repository }}/issues/8984))
147 - Update document by fix yaml syntax error ([#8991]({{ site.repository }}/issues/8991))
148 - Enhance option&#39;s case for Jekyll configuration ([#8992]({{ site.repository }}/issues/8992))
149 - Fix typo in `_docs/deployment/manual.md` ([#8997]({{ site.repository }}/issues/8997))
150 - Add quiet/verbose options ([#8996]({{ site.repository }}/issues/8996))
151 - Update README.markdown re IRC Pointer ([#9005]({{ site.repository }}/issues/9005))
152 - Remove Aerobatic ([#9007]({{ site.repository }}/issues/9007))
153 - Add Jekyll 3.9.2 release post to &#39;master&#39; branch ([#9013]({{ site.repository }}/issues/9013))
154 - Simplify macOS installation docs ([#8993]({{ site.repository }}/issues/8993))
155 - Improve document about Github Actions section ([#8853]({{ site.repository }}/issues/8853))
156 - Update permalinks.md ([#9017]({{ site.repository }}/issues/9017))
157 - Add clarity to docs on permalinks placeholders and built-ins ([#8995]({{ site.repository }}/issues/8995))
158 - Remove Ionic Framework site from showcase ([#9057]({{ site.repository }}/issues/9057))
159 - Windows: describe which option to choose ([#9049]({{ site.repository }}/issues/9049))
160 - Improve links (http -&gt; https) ([#9064]({{ site.repository }}/issues/9064))
161 - Update ruby version for macos guide ([#9086]({{ site.repository }}/issues/9086))
162 - Update posts.md ([#9151]({{ site.repository }}/issues/9151))
163 - Release post for v4.3.0 ([#9157]({{ site.repository }}/issues/9157))
164
165 ### Site Enhancements
166 {: #site-enhancements-v4-3-0}
167
168 - Improvements to CSS ([#7834]({{ site.repository }}/issues/7834))
169 - Slightly update lang `sh` code-block styling ([#8857]({{ site.repository }}/issues/8857))
170
171
172 ## 4.2.2 / 2022-03-03
173 {: #v4-2-2}
174
175 ### Bug Fixes
176 {: #bug-fixes-v4-2-2}
177
178 - Lock `http_parser.rb` gem to `v0.6.x` on JRuby.
179
180 ### Development Fixes
181 {: #development-fixes-v4-2-2}
182
183 - Backport [#8830]({{ site.repository }}/issues/8830) for v4.2.x: Add a workflow to build gems consistently ([#8869]({{ site.repository }}/issues/8869))
184 - Lock `rubocop-performance` to `v1.11.x`.
185
186
187 ## 4.2.1 / 2021-09-27
188 {: #v4-2-1}
189
190 ### Bug Fixes
191 {: #bug-fixes-v4-2-1}
192
193 - 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))
194 - Backport [#8756]({{ site.repository }}/issues/8756) for v4.2.x: Respect collections_dir config within include tag ([#8794]({{ site.repository }}/issues/8794))
195 - Backport [#8786]({{ site.repository }}/issues/8786) for v4.2.x: Fix regression in Convertible module from v4.2.0 ([#8793]({{ site.repository }}/issues/8793))
196
197
198 ## 4.2.0 / 2020-12-14
199 {: #v4-2-0}
200
201 ### Minor Enhancements
202 {: #minor-enhancements-v4-2-0}
203
204 - Warn on command-line with permalink conflict ([#8342]({{ site.repository }}/issues/8342))
205 - Suppress warning issued for redirect pages ([#8347]({{ site.repository }}/issues/8347))
206 - Enhance detection of conflicting destination URLs ([#8459]({{ site.repository }}/issues/8459))
207 - Add `:post_convert` hook to modify HTML content before layout ([#8368]({{ site.repository }}/issues/8368))
208 - Allow triggering `:post_convert` events atomically ([#8465]({{ site.repository }}/issues/8465))
209 - Debug reading Page and Layout objects ([#8100]({{ site.repository }}/issues/8100))
210 - Do not reset `site.url` to `http://localhost:4000` by default ([#7253]({{ site.repository }}/issues/7253))
211 - Add custom debug strings for Jekyll objects ([#8473]({{ site.repository }}/issues/8473))
212 - Debug reading data files in a site ([#8481]({{ site.repository }}/issues/8481))
213
214 ### Bug Fixes
215 {: #bug-fixes-v4-2-0}
216
217 - Replace nested conditional with guard clauses ([#8294]({{ site.repository }}/issues/8294))
218 - Fix: security bump ([#8349]({{ site.repository }}/issues/8349))
219 - Fix path matching regex in post_url Liquid tag ([#8375]({{ site.repository }}/issues/8375))
220 - Enable `Performance/ChainArrayAllocation` cop ([#8404]({{ site.repository }}/issues/8404))
221 - Enable Lint/NoReturnInBeginEndBlocks Cop ([#8457]({{ site.repository }}/issues/8457))
222 - Generate items from `site.include` list only once ([#8463]({{ site.repository }}/issues/8463))
223 - Explicitly return nil after site process phase ([#8472]({{ site.repository }}/issues/8472))
224
225 ### Optimization Fixes
226 {: #optimization-fixes-v4-2-0}
227
228 - Implement custom delegators for drop methods ([#8183]({{ site.repository }}/issues/8183))
229 - Handle `nil` argument to `Jekyll.sanitized_path` ([#8415]({{ site.repository }}/issues/8415))
230 - Cache `Jekyll.sanitized_path` ([#8424]({{ site.repository }}/issues/8424))
231 - Memoize array of drop getter method names ([#8421]({{ site.repository }}/issues/8421))
232 - Reduce string allocations from the `link` tag ([#8387]({{ site.repository }}/issues/8387))
233 - Optimize parsing of parameters in `include` tag ([#8192]({{ site.repository }}/issues/8192))
234 - Stash documents `write?` attribute in a variable ([#8389]({{ site.repository }}/issues/8389))
235 - Reduce string allocations from generating doc URLs ([#8392]({{ site.repository }}/issues/8392))
236 - Check if site is in incremental mode optimally ([#8401]({{ site.repository }}/issues/8401))
237 - Utilize flexibility of `Site#in_dest_dir` ([#8403]({{ site.repository }}/issues/8403))
238 - Reduce allocations from rendering item as liquid ([#8406]({{ site.repository }}/issues/8406))
239 - Compute relative_path of pages using PathManager ([#8408]({{ site.repository }}/issues/8408))
240 - Reduce allocation from `normalize_whitespace` filter ([#8400]({{ site.repository }}/issues/8400))
241 - Use `Regexp#match?` when `MatchData` is not required ([#8427]({{ site.repository }}/issues/8427))
242 - Check default front matter scope against symbols ([#8393]({{ site.repository }}/issues/8393))
243 - Stash frequently used `Drop` setter keys for reuse ([#8394]({{ site.repository }}/issues/8394))
244 - Memoize defaults computed for Convertibles ([#8451]({{ site.repository }}/issues/8451))
245 - Reduce array allocations from merging categories ([#8453]({{ site.repository }}/issues/8453))
246 - Memoize destination of pages, documents and staticfiles ([#8458]({{ site.repository }}/issues/8458))
247 - Reduce allocations from computing item property ([#8485]({{ site.repository }}/issues/8485))
248 - Optimize `Page#dir` with a private method ([#8489]({{ site.repository }}/issues/8489))
249 - Stash attribute hash for Liquid computed for pages ([#8497]({{ site.repository }}/issues/8497))
250
251 ### Development Fixes
252 {: #development-fixes-v4-2-0}
253
254 - Update cucumber gem to version 4.1 ([#8278]({{ site.repository }}/issues/8278))
255 - Move permalink styles data to constant ([#8282]({{ site.repository }}/issues/8282))
256 - Update rubocop gem to 0.87.1 ([#8287]({{ site.repository }}/issues/8287))
257 - Update RuboCop to-do file ([#8296]({{ site.repository }}/issues/8296))
258 - Fix `rake console` generating LoadError ([#8312]({{ site.repository }}/issues/8312))
259 - Configure Performance cops ([#8369]({{ site.repository }}/issues/8369))
260 - Update rubocop gem to 0.90.0 ([#8313]({{ site.repository }}/issues/8313))
261 - Refactor `Jekyll::Utils::Platforms` ([#7236]({{ site.repository }}/issues/7236))
262 - Bump RuboCop to v0.91.x ([#8391]({{ site.repository }}/issues/8391))
263 - Add workflow to build and profile third-party repo ([#8398]({{ site.repository }}/issues/8398))
264 - Bump RuboCop to v0.92.x
265 - Update cucumber gem version to 5.1.2 ([#8413]({{ site.repository }}/issues/8413))
266 - Fix test suite compatibility with JRuby ([#8418]({{ site.repository }}/issues/8418))
267 - chore(deps): bump Rubocop to 0.93.0 ([#8430]({{ site.repository }}/issues/8430))
268 - Use Ruby 2.7.1 in GitHub Actions ([#8444]({{ site.repository }}/issues/8444))
269 - Test that Liquid expressions are not deeply evaled ([#8292]({{ site.repository }}/issues/8292))
270 - Test rendering arbitrary Liquid variables by default ([#7414]({{ site.repository }}/issues/7414))
271 - Migrate TravisCI jobs to GitHub Actions ([#8492]({{ site.repository }}/issues/8492))
272
273 ### Documentation
274
275 - Update pointer to special permalink variables for collections ([#8274]({{ site.repository }}/issues/8274))
276 - Fix special treatment for &#39;page 1&#39; in docs of pagination ([#8230]({{ site.repository }}/issues/8230))
277 - Add Formcake to forms section ([#8283]({{ site.repository }}/issues/8283))
278 - Add a note on the rendering process in the docs ([#8291]({{ site.repository }}/issues/8291))
279 - Add refactoring type to PULL_REQUEST_TEMPLATE ([#8297]({{ site.repository }}/issues/8297))
280 - Update resources.md ([#7864]({{ site.repository }}/issues/7864))
281 - Extra apostrophes in an URL ([#8319]({{ site.repository }}/issues/8319))
282 - Clarify target of subordinate clause ([#8320]({{ site.repository }}/issues/8320))
283 - Cherry-pick commits from conflicting branch `docs-40`
284 - Update documentation on third party site ([#8352]({{ site.repository }}/issues/8352))
285 - Update default.md with info requested in [#8314]({{ site.repository }}/issues/8314) ([#8353]({{ site.repository }}/issues/8353))
286 - Clarify description of `safe` option ([#8354]({{ site.repository }}/issues/8354))
287 - Simplifying the Git post-receive hook-example ([#8358]({{ site.repository }}/issues/8358))
288 - Add missing doc for build and serve commands ([#8365]({{ site.repository }}/issues/8365))
289 - Docs Review: Getting Started ([#8372]({{ site.repository }}/issues/8372))
290 - Add note about rebooting system after installation ([#8359]({{ site.repository }}/issues/8359))
291 - Use data file to render table at `/docs/configuration/options/#global-configuration` ([#8377]({{ site.repository }}/issues/8377))
292 - Use data file(s) to render table(s) at `/docs/configuration/options/` ([#8380]({{ site.repository }}/issues/8380))
293 - Improve maintainability of config option data ([#8383]({{ site.repository }}/issues/8383))
294 - Remove CircleCI v1 docs ([#8410]({{ site.repository }}/issues/8410))
295 - Remove `NOKOGIRI_USE_SYSTEM_LIBRARIES` from Travis CI docs ([#8409]({{ site.repository }}/issues/8409))
296 - Add links to all Jekyll themes on GitHub tagged with #jekyll-theme ([#8447]({{ site.repository }}/issues/8447))
297 - Document initializing project Gemfile from scratch ([#8450]({{ site.repository }}/issues/8450))
298 - Document installation of additional dependencies for installing Jekyll on Fedora ([#8456]({{ site.repository }}/issues/8456))
299 - Improve documentation on Hooks in Jekyll ([#8467]({{ site.repository }}/issues/8467))
300 - Build docs site with GitHub Actions ([#8201]({{ site.repository }}/issues/8201))
301 - Add link to Assets page from `_sass` section in `_docs/structure.md` ([#8486]({{ site.repository }}/issues/8486))
302
303 ### Site Enhancements
304 {: #site-enhancements-v4-2-0}
305
306 - Fix rendering of *showcase* images ([#8504]({{ site.repository }}/issues/8504))
307
308
309 ## 4.1.1 / 2020-06-24
310 {: #v4-1-1}
311
312 ### Bug Fixes
313 {: #bug-fixes-v4-1-1}
314
315 - Disable page excerpts by default ([#8222]({{ site.repository }}/issues/8222))
316 - Revert introduction of PageDrop ([#8221]({{ site.repository }}/issues/8221))
317 - Don&#39;t generate excerpts for non-html pages ([#8234]({{ site.repository }}/issues/8234))
318 - Make page excerpts consistent with doc excerpts ([#8236]({{ site.repository }}/issues/8236))
319
320 ### Documentation
321
322 - Replace deprecated &#39;show&#39; command with &#39;info&#39; ([#8235]({{ site.repository }}/issues/8235))
323 - Change name to ▲Vercel ([#8247]({{ site.repository }}/issues/8247))
324 - Add language and examples to describe how to use the configuration op… ([#8249]({{ site.repository }}/issues/8249))
325 - Fix missing yaml front matter colon and adjust/add clarifying language. ([#8250]({{ site.repository }}/issues/8250))
326 - correct typo ([#8261]({{ site.repository }}/issues/8261))
327 - Allow hyperlinks to specific filter documentation ([#8231]({{ site.repository }}/issues/8231))
328 - Update link to Netlify step-by-step guide ([#8264]({{ site.repository }}/issues/8264))
329 - Fix grammar in documentation section ([#8265]({{ site.repository }}/issues/8265))
330
331 ### Site Enhancements
332 {: #site-enhancements-v4-1-1}
333
334 - Including correct Sketch website ([#8241]({{ site.repository }}/issues/8241))
335 - Release post for v4.1.1 ([#8243]({{ site.repository }}/issues/8243))
336
337 ### Development Fixes
338 {: #development-fixes-v4-1-1}
339
340 - Bump RuboCop to v0.85.x ([#8223]({{ site.repository }}/issues/8223))
341 - Expect drive letter only on vanilla windows ([#8227]({{ site.repository }}/issues/8227))
342
343
344 ## 4.1.0 / 2020-05-27
345 {: #v4-1-0}
346
347 ### Bug Fixes
348 {: #bug-fixes-v4-1-0}
349
350 - Memoize `absolute_url` and `relative_url` filters ([#7793]({{ site.repository }}/issues/7793))
351 - Fix documentation comment for `Jekyll::Converters::Identity` ([#7883]({{ site.repository }}/issues/7883))
352 - Optimize `Jekyll::Filters#item_property` ([#7696]({{ site.repository }}/issues/7696))
353 - Allow multiple binary operators in `where_exp` filter ([#8047]({{ site.repository }}/issues/8047))
354 - Fix documents custom-ordering logic ([#8028]({{ site.repository }}/issues/8028))
355 - Use `layout.path` when rendering the Liquid layout ([#8069]({{ site.repository }}/issues/8069))
356 - Reduce array allocations from `StaticFile#path` ([#8083]({{ site.repository }}/issues/8083))
357 - Simplify `Jekyll::Renderer#validate_layout` ([#8064]({{ site.repository }}/issues/8064))
358 - Add static file's basename to its `url_placeholder` ([#7908]({{ site.repository }}/issues/7908))
359 - Clear cached Liquid template scope before render ([#7967]({{ site.repository }}/issues/7967))
360 - Cache `URLFilter` results of string inputs per site ([#7990]({{ site.repository }}/issues/7990))
361 - Use `platforms` instead of `install_if` in Gemfile ([#8140]({{ site.repository }}/issues/8140))
362 - Config include trailing slash ([#8113]({{ site.repository }}/issues/8113))
363 - Improve path normalization in liquid_renderer ([#8075]({{ site.repository }}/issues/8075))
364 - Switch slugify regex to support more Unicode character groups ([#8167]({{ site.repository }}/issues/8167))
365 - Check if entry is a directory once per enumerator ([#8177]({{ site.repository }}/issues/8177))
366 - Filter out exclusively excluded entries sooner ([#7482]({{ site.repository }}/issues/7482))
367 - Return `relative_url` if site.url is an empty string ([#7988]({{ site.repository }}/issues/7988))
368 - Configure kramdown toc_levels as array by default ([#8015]({{ site.repository }}/issues/8015))
369 - Reduce `Pathname` objects from front matter defaults ([#8067]({{ site.repository }}/issues/8067))
370 - Simplify `Jekyll::Hooks.trigger` logic ([#8044]({{ site.repository }}/issues/8044))
371 - Quicker categories for documents without superdirs ([#7987]({{ site.repository }}/issues/7987))
372 - Reduce `Jekyll::Renderer` instances during a build ([#7570]({{ site.repository }}/issues/7570))
373 - Escape regex characters in paths to match ([#8138]({{ site.repository }}/issues/8138))
374 - Provide invokables for common drop query keys ([#8165]({{ site.repository }}/issues/8165))
375 - Optimize path sanitization of default front matter ([#8154]({{ site.repository }}/issues/8154))
376 - Initialize static files' data hash only if needed ([#8188]({{ site.repository }}/issues/8188))
377 - Initialize include-files as Jekyll objects ([#8158]({{ site.repository }}/issues/8158))
378
379 ### Minor Enhancements
380 {: #minor-enhancements-v4-1-0}
381
382 - serve: add support for ECC certificates ([#7768]({{ site.repository }}/issues/7768))
383 - Update `item_property` to recognize integers ([#7878]({{ site.repository }}/issues/7878))
384 - Include `_config.yml` in a new theme's gemspec ([#7865]({{ site.repository }}/issues/7865))
385 - Add an option to easily disable disk-cache ([#7928]({{ site.repository }}/issues/7928))
386 - Optimize markdown parsing with Kramdown by reusing the options and parser objects ([#8013]({{ site.repository }}/issues/8013))
387 - Add `PageDrop` to provide Liquid templates with data ([#7992]({{ site.repository }}/issues/7992))
388 - Optimize `Kramdown::JekyllDocument#to_html` calls ([#8041]({{ site.repository }}/issues/8041))
389 - Configure default language for syntax-highlighting ([#8035]({{ site.repository }}/issues/8035))
390 - Remove dev dependencies from new theme-gem gemspec ([#8042]({{ site.repository }}/issues/8042))
391 - Allow disabling import of theme configuration ([#8131]({{ site.repository }}/issues/8131))
392 - Allow excerpts to be generated for `Page` objects ([#7642]({{ site.repository }}/issues/7642))
393 - Profile various stages of a site's build process ([#6760]({{ site.repository }}/issues/6760))
394 - Add find filters to optimize where-first chains ([#8171]({{ site.repository }}/issues/8171))
395 - Make `number_of_words` filter respect CJK characters ([#7813]({{ site.repository }}/issues/7813))
396 - Allow extensionless document in a strict site ([#7950]({{ site.repository }}/issues/7950))
397 - Add `:slugified_categories` URL placeholder ([#8094]({{ site.repository }}/issues/8094))
398
399 ### Documentation
400
401 - Add dropped 'title: Staff' to the code ([#7805]({{ site.repository }}/issues/7805))
402 - Clarify docs for static files in collection ([#7812]({{ site.repository }}/issues/7812))
403 - Rephrase the CircleCI v2 section ([#7815]({{ site.repository }}/issues/7815))
404 - Update old GitHub wiki URL with new one ([#7823]({{ site.repository }}/issues/7823))
405 - Update JekyllConf page with 2019 talks ([#7826]({{ site.repository }}/issues/7826))
406 - link for memberships ([#7825]({{ site.repository }}/issues/7825))
407 - Doc: minor fix, should be greater or equal to min version ([#7856]({{ site.repository }}/issues/7856))
408 - Update third-party.md - Fix broken link ([#7857]({{ site.repository }}/issues/7857))
409 - clarify _config.yml/collections type ([#7873]({{ site.repository }}/issues/7873))
410 - Replace backticks with HTML tags in data file ([#7879]({{ site.repository }}/issues/7879))
411 - add new theme source ([#7875]({{ site.repository }}/issues/7875))
412 - fixed grammatical error (it&#39;s --&gt; its) ([#7887]({{ site.repository }}/issues/7887))
413 - Docs: Clarify organizing pages into subfolders ([#7896]({{ site.repository }}/issues/7896))
414 - Disambiguate the placeholder of permalink ([#7906]({{ site.repository }}/issues/7906))
415 - docs: add short serve command for livereload ([#7919]({{ site.repository }}/issues/7919))
416 - docs: add options for watch and force polling ([#7918]({{ site.repository }}/issues/7918))
417 - add install instructions for ArchLinux and openSUSE ([#7920]({{ site.repository }}/issues/7920))
418 - Improve index page of Jekyll documentation ([#7926]({{ site.repository }}/issues/7926))
419 - Include path in `jekyll new` commands (Usage docs) ([#7931]({{ site.repository }}/issues/7931))
420 - Change `affect` to `effect` in the collections docs ([#7937]({{ site.repository }}/issues/7937))
421 - Changed deprecated command in themes documentation ([#7941]({{ site.repository }}/issues/7941))
422 - Adds some documentation for the `:clean`, `:on_obsolete` hook ([#7954]({{ site.repository }}/issues/7954))
423 - docs: fix broken link ([#7955]({{ site.repository }}/issues/7955))
424 - Corrected typo ([#7975]({{ site.repository }}/issues/7975))
425 - docs: remove watch option in config ([#7940]({{ site.repository }}/issues/7940))
426 - Correct a sentence in the documentation ([#7978]({{ site.repository }}/issues/7978))
427 - Fix YAML representation of `group_by` result ([#7979]({{ site.repository }}/issues/7979))
428 - Move `--baseurl` to build command options ([#7985]({{ site.repository }}/issues/7985))
429 - Correct documentation of filters ([#7989]({{ site.repository }}/issues/7989))
430 - Document sorting two documents by their `date` ([#7870]({{ site.repository }}/issues/7870))
431 - Fix English grammar error ([#7994]({{ site.repository }}/issues/7994))
432 - Update 03-front-matter.md ([#7996]({{ site.repository }}/issues/7996))
433 - Add Kentico Kontent CMS integration to resources ([#8000]({{ site.repository }}/issues/8000))
434 - Update 07-assets.md ([#7413]({{ site.repository }}/issues/7413))
435 - Fix file references in Step by Step Tutorial's Assets step ([#8007]({{ site.repository }}/issues/8007))
436 - docs: improve highlighting of code blocks ([#8017]({{ site.repository }}/issues/8017))
437 - remove leading slash from Sass file location ([#8021]({{ site.repository }}/issues/8021))
438 - [Docs] Fix asset link ref in step-by-step tutorial ([#8026]({{ site.repository }}/issues/8026))
439 - Corrected command to modify PATH ([#8029]({{ site.repository }}/issues/8029))
440 - Corrected command to modify PATH ([#8030]({{ site.repository }}/issues/8030))
441 - Docs: Render full contents of just the latest post ([#8032]({{ site.repository }}/issues/8032))
442 - docs: improvements for note boxes ([#8037]({{ site.repository }}/issues/8037))
443 - Non-deprecated `vendor/bundle` path configuration ([#8048]({{ site.repository }}/issues/8048))
444 - Update 09-collections.md ([#8060]({{ site.repository }}/issues/8060))
445 - Remove extra paragraph tags ([#8063]({{ site.repository }}/issues/8063))
446 - Add default front matter for tutorials collection ([#8081]({{ site.repository }}/issues/8081))
447 - Create CSV to table tutorial ([#8090]({{ site.repository }}/issues/8090))
448 - Add version badge for Custom Sorting of Documents ([#8098]({{ site.repository }}/issues/8098))
449 - Docs: Fix grammar in `_docs/front-matter.md` ([#8097]({{ site.repository }}/issues/8097))
450 - Update variables.md ([#8106]({{ site.repository }}/issues/8106))
451 - Add help about Gentoo/Linux ([#8002]({{ site.repository }}/issues/8002))
452 - Update documentation on third party site ([#8122]({{ site.repository }}/issues/8122))
453 - Added Clear Linux ([#8132]({{ site.repository }}/issues/8132))
454 - Added note about OS specific installation instructions. ([#8135]({{ site.repository }}/issues/8135))
455 - Fix broken URL in the Resources Page on the Documentation Site ([#8136]({{ site.repository }}/issues/8136))
456 - Docs: Deploy Jekyll site with GitHub Actions ([#8119]({{ site.repository }}/issues/8119))
457 - Clarify `bundle config` in Bundler tutorial ([#8150]({{ site.repository }}/issues/8150))
458 - docs: update your-first-plugin.md ([#8147]({{ site.repository }}/issues/8147))
459 - Fix typo in documentation on GitHub Actions ([#8162]({{ site.repository }}/issues/8162))
460 - Ease discovery of CLI commands (in their entirety) ([#8178]({{ site.repository }}/issues/8178))
461 - Remove `sudo` from Travis CI tutorial ([#8187]({{ site.repository }}/issues/8187))
462 - Add GitLab Pages to 3rd party list ([#8191]({{ site.repository }}/issues/8191))
463 - docs: add 21yunbox for deployment ([#8193]({{ site.repository }}/issues/8193))
464 - Improve documentation on tags and categories ([#8196]({{ site.repository }}/issues/8196))
465
466 ### Development Fixes
467 {: #development-fixes-v4-1-0}
468
469 - Ci/GitHub actions ([#7822]({{ site.repository }}/issues/7822))
470 - Rubocop version upgrade ([#7846]({{ site.repository }}/issues/7846))
471 - Split action steps to avoid using `&&` on Windows ([#7885]({{ site.repository }}/issues/7885))
472 - Upgrade rake to use version 13 ([#7910]({{ site.repository }}/issues/7910))
473 - Update dependency constraint to allow RuboCop v0.76 ([#7893]({{ site.repository }}/issues/7893))
474 - Use bash executable consistently ([#7909]({{ site.repository }}/issues/7909))
475 - Test with JRuby 9.2.9.0 ([#7779]({{ site.repository }}/issues/7779))
476 - Bump RuboCop to v0.79.x ([#7970]({{ site.repository }}/issues/7970))
477 - Remove post-install message from gemspec ([#7974]({{ site.repository }}/issues/7974))
478 - Attain Ruby 3.0 compatibility ([#7948]({{ site.repository }}/issues/7948))
479 - Test `where` filter handling numeric property values ([#7821]({{ site.repository }}/issues/7821))
480 - chore(deps): rubocop 0.80.0 ([#8012]({{ site.repository }}/issues/8012))
481 - Update unit tests for Kramdown-based converter ([#8014]({{ site.repository }}/issues/8014))
482 - Add Visual Studio Code Development Container ([#8016]({{ site.repository }}/issues/8016))
483 - chore: simplify require for `Jekyll::VERSION` ([#8057]({{ site.repository }}/issues/8057))
484 - Remove version-constraint relaxation for i18n gem ([#8055]({{ site.repository }}/issues/8055))
485 - Mirror `spec.homepage` as `metadata["homepage_uri"]` ([#8056]({{ site.repository }}/issues/8056))
486 - Bump Ruby versions on Travis builds ([#8088]({{ site.repository }}/issues/8088))
487 - chore(ci): cache dependencies ([#8168]({{ site.repository }}/issues/8168))
488
489 ### Site Enhancements
490 {: #site-enhancements-v4-1-0}
491
492 - Optimize rendering of the documentation site ([#8020]({{ site.repository }}/issues/8020))
493 - Utilize `relative_url` filter in documentation site ([#8089]({{ site.repository }}/issues/8089))
494 - Render tutorial metadata in documentation site ([#8092]({{ site.repository }}/issues/8092))
495 - Improve syntax-highlighting in documentation site ([#8079]({{ site.repository }}/issues/8079))
496 - Site: Filter through just the *docs* collection ([#8170]({{ site.repository }}/issues/8170))
497
498
499 ## 4.0.1 / 2020-05-08
500 {: #v4-0-1}
501
502 ### Bug Fixes
503 {: #bug-fixes-v4-0-1}
504
505 - Prevent console warning with Ruby 2.7 ([#8124]({{ site.repository }}/issues/8124))
506 - Clear cached Liquid template scope before render ([#8141]({{ site.repository }}/issues/8141))
507 - Add static file's basename to its url_placeholder ([#8142]({{ site.repository }}/issues/8142))
508 - Update item_property to recognize integers ([#8160]({{ site.repository }}/issues/8160))
509
510 ### Development Fixes
511 {: #development-fixes-v4-0-1}
512
513 - Fix Kramdown converter based tests for v4.0.x ([#8143]({{ site.repository }}/issues/8143))
514
515
516 ## 3.9.2 / 2022-03-27
517 {: #v3-9-2}
518
519 ### Bug Fixes
520 {: #bug-fixes-v3-9-2}
521
522 - Lock `http_parser.rb` gem to `v0.6.x` on JRuby ([#8943]({{ site.repository }}/issues/8943))
523 - Backport [#8756]({{ site.repository }}/issues/8756) for v3.9.x: Respect collections_dir config within include tag ([#8795]({{ site.repository }}/issues/8795))
524 - Backport [#8965]({{ site.repository }}/issues/8965) for v3.9.x: Fix response header for content served via `jekyll serve` ([#8976]({{ site.repository }}/issues/8976))
525
526 ### Development Fixes
527 {: #development-fixes-v3-9-2}
528
529 - Update and fix CI for `3.9-stable` on Ruby 3.x ([#8942]({{ site.repository }}/issues/8942))
530 - Fix CI for commits to `3.9-stable` branch ([#8788]({{ site.repository }}/issues/8788))
531
532
533 ## 3.9.1 / 2021-04-08
534 {: #v3-9-1}
535
536 ### Bug Fixes
537 {: #bug-fixes-v3-9-1}
538
539 - Backport [#8618]({{ site.repository }}/issues/8618) for v3.9.x: Update include tag to be more permissive ([#8629]({{ site.repository }}/issues/8629))
540
541
542 ## 3.9.0 / 2020-08-05
543 {: #v3-9-0}
544
545 ### Minor Enhancements
546 {: #minor-enhancements-v3-9-0}
547
548 - Allow use of kramdown v2 ([#8322]({{ site.repository }}/issues/8322))
549 - Add default language for kramdown syntax highlighting ([#8325]({{ site.repository }}/issues/8325))
550
551
6552 ## 3.8.7 / 2020-05-08
7553 {: #v3-8-7}
8554
10556 {: #bug-fixes-v3-8-7}
11557
12558 - Prevent console warnings with Ruby 2.7 ([#8125]({{ site.repository }}/issues/8125))
559
560
561 ## 4.0.0 / 2019-08-19
562 {: #v4-0-0}
563
564 ### Major Enhancements
565 {: #major-enhancements-v4-0-0}
566
567 - Drop ruby 2.3 ([#7454]({{ site.repository }}/issues/7454))
568 - Drop support for Ruby 2.1 and 2.2 ([#6560]({{ site.repository }}/issues/6560))
569 - Drop support for older versions of Rouge ([#6978]({{ site.repository }}/issues/6978))
570 - Drop support for pygments as syntax-highlighter ([#7118]({{ site.repository }}/issues/7118))
571 - Drop support for Redcarpet ([#6987]({{ site.repository }}/issues/6987))
572 - Drop support for rdiscount ([#6988]({{ site.repository }}/issues/6988))
573 - Drop support for `jekyll-watch-1.4.0` and older ([#7287]({{ site.repository }}/issues/7287))
574 - Incorporate `relative_url` filter in `link` tag ([#6727]({{ site.repository }}/issues/6727))
575 - Upgrade kramdown dependency to v2.x ([#7492]({{ site.repository }}/issues/7492))
576 - Upgrade jekyll-sass-converter to v2.x - Sassc + sourcemaps ([#7778]({{ site.repository }}/issues/7778))
577 - Upgrade i18n to v1.x ([#6931]({{ site.repository }}/issues/6931))
578 - Add `Jekyll::Cache` class to handle caching on disk ([#7169]({{ site.repository }}/issues/7169))
579 - Cache converted markdown ([#7159]({{ site.repository }}/issues/7159))
580 - Cache: Do not dump undumpable objects ([#7190]({{ site.repository }}/issues/7190))
581 - Cache matched defaults sets for given parameters ([#6888]({{ site.repository }}/issues/6888))
582 - Ignore cache directory ([#7184]({{ site.repository }}/issues/7184))
583 - Add `Site#in_cache_dir` helper method ([#7160]({{ site.repository }}/issues/7160))
584 - Remove &#39;cache_dir&#39; during `jekyll clean` ([#7158]({{ site.repository }}/issues/7158))
585 - Cache parsed Liquid templates in memory ([#7136]({{ site.repository }}/issues/7136))
586 - Only read layouts from source_dir or theme_dir ([#6788]({{ site.repository }}/issues/6788))
587 - Allow custom sorting of collection documents ([#7427]({{ site.repository }}/issues/7427))
588 - Always exclude certain paths from being processed ([#7188]({{ site.repository }}/issues/7188))
589 - Remove Jekyll::Utils#strip_heredoc in favor of a Ruby &gt; 2.3 built in ([#7584]({{ site.repository }}/issues/7584))
590 - Incorporate `relative_url` within `post_url` tag ([#7589]({{ site.repository }}/issues/7589))
591 - Remove patch to modify config for kramdown ([#7699]({{ site.repository }}/issues/7699))
592
593 ### Minor Enhancements
594 {: #minor-enhancements-v4-0-0}
595
596 - Enhance `--blank` scaffolding ([#7310]({{ site.repository }}/issues/7310))
597 - Use `jekyll-compose` if installed ([#6932]({{ site.repository }}/issues/6932))
598 - Disable Liquid via front matter ([#6824]({{ site.repository }}/issues/6824))
599 - Configure cache_dir ([#7232]({{ site.repository }}/issues/7232))
600 - ISO week date drops ([#5981]({{ site.repository }}/issues/5981))
601 - Fix custom 404 page for GitHub pages ([#7132]({{ site.repository }}/issues/7132))
602 - Load config file from within current theme-gem ([#7304]({{ site.repository }}/issues/7304))
603 - Suggest re-running command with `--trace` on fail ([#6551]({{ site.repository }}/issues/6551))
604 - Support for binary operators in where_exp filter ([#6998]({{ site.repository }}/issues/6998))
605 - Automatically load `_config.toml` ([#7299]({{ site.repository }}/issues/7299))
606 - Add vendor folder to a newly installed site&#39;s .gitignore ([#6968]({{ site.repository }}/issues/6968))
607 - Output Jekyll Version while debugging ([#7173]({{ site.repository }}/issues/7173))
608 - Memoize computing excerpt&#39;s relative_path ([#6951]({{ site.repository }}/issues/6951))
609 - Skip processing posts that can not be read ([#7302]({{ site.repository }}/issues/7302))
610 - Memoize the return value of Site#documents ([#7273]({{ site.repository }}/issues/7273))
611 - Cache globbed paths in front matter defaults ([#7345]({{ site.repository }}/issues/7345))
612 - Cache computed item property ([#7301]({{ site.repository }}/issues/7301))
613 - Cleanup Markdown converter ([#7519]({{ site.repository }}/issues/7519))
614 - Do not process Liquid in post excerpt when disabled in front matter ([#7146]({{ site.repository }}/issues/7146))
615 - Liquefied link tag ([#6269]({{ site.repository }}/issues/6269))
616 - Update item_property to return numbers as numbers instead of strings ([#6608]({{ site.repository }}/issues/6608))
617 - Use `.markdown` extension for page templates ([#7126]({{ site.repository }}/issues/7126))
618 - Add support for `*.xhtml` files ([#6854]({{ site.repository }}/issues/6854))
619 - Allow i18n v0.9.5 and higher ([#7044]({{ site.repository }}/issues/7044))
620 - Ignore permission error of /proc/version ([#7267]({{ site.repository }}/issues/7267))
621 - Strip extra slashes via `Jekyll.sanitized_path` ([#7182]({{ site.repository }}/issues/7182))
622 - Site template: remove default config for markdown ([#7285]({{ site.repository }}/issues/7285))
623 - Add a custom inspect string for StaticFile objects ([#7422]({{ site.repository }}/issues/7422))
624 - Remind user to include gem in the Gemfile on error ([#7476]({{ site.repository }}/issues/7476))
625 - Search Front matter defaults for Page objects with relative_path ([#7261]({{ site.repository }}/issues/7261))
626 - Lock use of `tzinfo` gem to v1.x ([#7521]({{ site.repository }}/issues/7521), [#7562]({{ site.repository }}/issues/7562))
627 - Utilize absolute paths of user-provided file paths ([#7450]({{ site.repository }}/issues/7450))
628 - Detect `nil` and empty values in objects with `where` filter ([#7580]({{ site.repository }}/issues/7580))
629 - Initialize mutations for Drops only if necessary ([#7657]({{ site.repository }}/issues/7657))
630 - Reduce Array allocations via Jekyll::Cleaner ([#7659]({{ site.repository }}/issues/7659))
631 - Encode and unencode urls only as required ([#7654]({{ site.repository }}/issues/7654))
632 - Reduce string allocations with better alternatives ([#7643]({{ site.repository }}/issues/7643))
633 - Reduce allocations from Jekyll::Document instances ([#7625]({{ site.repository }}/issues/7625))
634 - Add `type` attribute to Document instances ([#7406]({{ site.repository }}/issues/7406))
635 - Reduce allocations from where-filter ([#7653]({{ site.repository }}/issues/7653))
636 - Memoize SiteDrop#documents to reduce allocations ([#7697]({{ site.repository }}/issues/7697))
637 - Add PathManager class to cache interim paths ([#7732]({{ site.repository }}/issues/7732))
638 - Remove warnings and fixes for deprecated config ([#7440]({{ site.repository }}/issues/7440))
639 - Delegate --profile tabulation to `terminal-table` ([#7627]({{ site.repository }}/issues/7627))
640
641 ### Bug Fixes
642 {: #bug-fixes-v4-0-0}
643
644 - Security: fix `include` bypass of `EntryFilter#filter` symlink check ([#7226]({{ site.repository }}/issues/7226))
645 - Theme gems: ensure directories aren&#39;t symlinks ([#7419]({{ site.repository }}/issues/7419))
646 - Add call to unused method `validate_options` in `commands/serve.rb` ([#7122]({{ site.repository }}/issues/7122))
647 - Check if scope applies to type before given path ([#7263]({{ site.repository }}/issues/7263))
648 - Document two methods, simplify one of the methods ([#7270]({{ site.repository }}/issues/7270))
649 - Check key in collections only if it isn&#39;t &#34;posts&#34; ([#7277]({{ site.repository }}/issues/7277))
650 - Interpolate Jekyll::Page subclass on inspection ([#7203]({{ site.repository }}/issues/7203))
651 - Measure the no. of times a template gets rendered ([#7316]({{ site.repository }}/issues/7316))
652 - Reduce array traversal in Jekyll::Reader ([#7157]({{ site.repository }}/issues/7157))
653 - Re-implement handling Liquid blocks in excerpts ([#7250]({{ site.repository }}/issues/7250))
654 - Documents should be able to render their date ([#7404]({{ site.repository }}/issues/7404))
655 - Fix Interpreter warning from Jekyll::Renderer ([#7448]({{ site.repository }}/issues/7448))
656 - Loggers should accept both numbers and symbols ([#6967]({{ site.repository }}/issues/6967))
657 - Replace regex arg to :gsub with a string arg ([#7189]({{ site.repository }}/issues/7189))
658 - Dont write static files from unrendered collection ([#7410]({{ site.repository }}/issues/7410))
659 - Excerpt handling of custom and intermediate tags ([#7382]({{ site.repository }}/issues/7382))
660 - Change future post loglevel to warn to help user narrow down issues ([#7527]({{ site.repository }}/issues/7527))
661 - Handle files with trailing dots in their basename ([#7315]({{ site.repository }}/issues/7315))
662 - Fix unnecessary allocations via StaticFileReader ([#7572]({{ site.repository }}/issues/7572))
663 - Don&#39;t check if site URL is absolute if it is nil ([#7498]({{ site.repository }}/issues/7498))
664 - Avoid unnecessary duplication of pages array ([#7272]({{ site.repository }}/issues/7272))
665 - Memoize Site#post_attr_hash ([#7276]({{ site.repository }}/issues/7276))
666 - Memoize Document#excerpt_separator ([#7569]({{ site.repository }}/issues/7569))
667 - Optimize Document::DATE_FILENAME_MATCHER to match valid filenames ([#7292]({{ site.repository }}/issues/7292))
668 - Escape valid special chars in a site&#39;s path name ([#7568]({{ site.repository }}/issues/7568))
669 - Replace `name` in Page#inspect with relative_path ([#7434]({{ site.repository }}/issues/7434))
670 - Log a warning when the slug is empty ([#7357]({{ site.repository }}/issues/7357))
671 - Push Markdown link refs to excerpt only as required ([#7577]({{ site.repository }}/issues/7577))
672 - Fix broken include_relative usage in excerpt ([#7633]({{ site.repository }}/issues/7633))
673 - Initialize and reset glob_cache only as necessary ([#7658]({{ site.repository }}/issues/7658))
674 - Revert memoizing Site#docs_to_write and #documents ([#7684]({{ site.repository }}/issues/7684))
675 - Backport [#7684]({{ site.repository }}/issues/7684) for v3.8.x: Revert memoizing Site#docs_to_write and refactor #documents ([#7689]({{ site.repository }}/issues/7689))
676 - 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))
677 - Don&#39;t read symlinks in site.include in safe mode ([#7711]({{ site.repository }}/issues/7711))
678 - Replace `String#=~` with `String#match?` ([#7723]({{ site.repository }}/issues/7723))
679 - Update log output for an invalid theme directory ([#7679]({{ site.repository }}/issues/7679))
680 - Remove configuration of theme sass files from Core ([#7290]({{ site.repository }}/issues/7290))
681 - Actually conditionally include liquid-c ([#7792]({{ site.repository }}/issues/7792))
682 - Test number_like regex on stringified property ([#7788]({{ site.repository }}/issues/7788))
683
684 ### Development Fixes
685 {: #development-fixes-v4-0-0}
686
687 - Upgrade liquid-c to v4.0 ([#7375]({{ site.repository }}/issues/7375))
688 - Bump RuboCop to v0.71.0 ([#7687]({{ site.repository }}/issues/7687))
689 - Target Ruby 2.4 syntax ([#7583]({{ site.repository }}/issues/7583))
690 - Fix: RuboCop offenses ([#7769]({{ site.repository }}/issues/7769))
691 - Use communicative method parameters ([#7566]({{ site.repository }}/issues/7566))
692 - Scan `assert_equal` methods and rectify any offenses with a custom RuboCop cop ([#7130]({{ site.repository }}/issues/7130))
693 - CI: Test with Ruby 2.6 ([#7438]({{ site.repository }}/issues/7438))
694 - CI: Test with Ruby 2.6 on AppVeyor ([#7518]({{ site.repository }}/issues/7518))
695 - CI: Update RuboCop config ([#7050]({{ site.repository }}/issues/7050))
696 - CI: Add a script to profile docs ([#7540]({{ site.repository }}/issues/7540))
697 - CI(Appveyor): shallow clone with 5 last commits ([#7312]({{ site.repository }}/issues/7312))
698 - CI: Test with oldest and latest Ruby only ([#7412]({{ site.repository }}/issues/7412))
699 - CI: Update excludes for CodeClimate Analyses ([#7365]({{ site.repository }}/issues/7365))
700 - CI: Lock Travis to Bundler-1.16.2 ([#7144]({{ site.repository }}/issues/7144))
701 - CI: Bump tested version of JRuby to 9.2.7.0 ([#7612]({{ site.repository }}/issues/7612))
702 - CI: Do not install docs on updating gems on Travis ([#7706]({{ site.repository }}/issues/7706))
703 - Update gemspec ([#7425]({{ site.repository }}/issues/7425))
704 - deps: relax version constraint on classifier-reborn gem ([#7471]({{ site.repository }}/issues/7471))
705 - deps: update yajl-ruby ([#7278]({{ site.repository }}/issues/7278))
706 - deps: bump yajl-ruby to v1.4.0 ([#6976]({{ site.repository }}/issues/6976))
707 - Create symlink only if target is accessible ([#7429]({{ site.repository }}/issues/7429))
708 - Switch to `:install_if` for wdm gem ([#7372]({{ site.repository }}/issues/7372))
709 - Add cucumber feature to test include_relative tag ([#7213]({{ site.repository }}/issues/7213))
710 - Small benchmark refactoring ([#7211]({{ site.repository }}/issues/7211))
711 - Fix incorrectly passed arguments to assert_equal ([#7134]({{ site.repository }}/issues/7134))
712 - fix up refute_equal call ([#7133]({{ site.repository }}/issues/7133))
713 - Fix RuboCop offences in test files ([#7128]({{ site.repository }}/issues/7128))
714 - Use assert_include ([#7093]({{ site.repository }}/issues/7093))
715 - Remember to release docs gem ([#7066]({{ site.repository }}/issues/7066))
716 - Useless privates removed ([#6768]({{ site.repository }}/issues/6768))
717 - Load Rouge for TestKramdown ([#7007]({{ site.repository }}/issues/7007))
718 - Update instructions for releasing docs Gem ([#6975]({{ site.repository }}/issues/6975))
719 - We are not using Ruby 2.2 anymore ([#6977]({{ site.repository }}/issues/6977))
720 - Remove unnecessary Jekyll::Page constant ([#6770]({{ site.repository }}/issues/6770))
721 - Remove unused error class ([#6511]({{ site.repository }}/issues/6511))
722 - Add a Cucumber feature for post_url tag ([#7586]({{ site.repository }}/issues/7586))
723 - Generate a &#34;TOTAL&#34; row for build-profile table ([#7614]({{ site.repository }}/issues/7614))
724 - Refactor Jekyll::Cache ([#7532]({{ site.repository }}/issues/7532))
725 - Store list of expected extnames in a constant ([#7638]({{ site.repository }}/issues/7638))
726 - Profile allocations from a build session ([#7646]({{ site.repository }}/issues/7646))
727 - Update small typo in contributing.md ([#7671]({{ site.repository }}/issues/7671))
728 - Remove override to Jekyll::Document#respond_to? ([#7695]({{ site.repository }}/issues/7695))
729 - Update TestTags in sync with Rouge v3.4 ([#7709]({{ site.repository }}/issues/7709))
730 - Use regexp to filter special entries ([#7702]({{ site.repository }}/issues/7702))
731 - Reduce Array objects generated from utility method ([#7749]({{ site.repository }}/issues/7749))
732 - Update mime.types ([#7756]({{ site.repository }}/issues/7756))
733 - Replace redundant Array#map with Array#each ([#7761]({{ site.repository }}/issues/7761))
734 - Reduce allocations by using #each_with_object ([#7758]({{ site.repository }}/issues/7758))
735 - Memoize fallback_data for Drop ([#7728]({{ site.repository }}/issues/7728))
736 - Use String#end_with? to check if entry is a backup ([#7701]({{ site.repository }}/issues/7701))
737
738 ### Documentation
739
740 - Refactor docs ([#7205]({{ site.repository }}/issues/7205))
741 - Add a link to Giraffe Academy&#39;s tutorial ([#7325]({{ site.repository }}/issues/7325))
742 - Do not advise users to install Jekyll outside of Bundler ([#6927]({{ site.repository }}/issues/6927))
743 - Remove documentation for using Redcarpet ([#6990]({{ site.repository }}/issues/6990))
744 - Install Docs that Work on MacOS 10.14 ([#7561]({{ site.repository }}/issues/7561))
745 - Add Installation Instructions for Ubuntu ([#6925]({{ site.repository }}/issues/6925))
746 - Don&#39;t prompt for sudo when installing with Ubuntu WSL ([#6781]({{ site.repository }}/issues/6781))
747 - Installation instructions for Fedora ([#7198]({{ site.repository }}/issues/7198))
748 - Update Windows install docs ([#6926]({{ site.repository }}/issues/6926))
749 - List all standard liquid filters ([#7333]({{ site.repository }}/issues/7333))
750 - List all static files variables ([#7002]({{ site.repository }}/issues/7002))
751 - Improve how to include Rouge stylesheets ([#7752]({{ site.repository }}/issues/7752))
752 - Mention CommonMark plugins ([#7418]({{ site.repository }}/issues/7418))
753 - Add TSV to list of supported _data files. ([#7168]({{ site.repository }}/issues/7168))
754 - How to deploy using pre-push git hook ([#7179]({{ site.repository }}/issues/7179))
755 - Hosting with AWS Amplify ([#7510]({{ site.repository }}/issues/7510))
756 - CircleCI deployment through CircleCI v2 ([#7024]({{ site.repository }}/issues/7024))
757 - GitHub Pages: use themes from other repos ([#7112]({{ site.repository }}/issues/7112))
758 - Document page.dir and page.name ([#7373]({{ site.repository }}/issues/7373))
759 - Document custom tag blocks ([#7359]({{ site.repository }}/issues/7359))
760 - Document converter methods ([#7289]({{ site.repository }}/issues/7289))
761 - Document {% raw %}`{{ page.collection }}`{% endraw %} ([#7430]({{ site.repository }}/issues/7430))
762 - Document Jekyll Filters with YAML data ([#7335]({{ site.repository }}/issues/7335))
763 - Document where Jekyll looks for layouts in a site ([#7564]({{ site.repository }}/issues/7564))
764 - plugin: liquid tag jekyll-flickr ([#6946]({{ site.repository }}/issues/6946))
765 - plugin: jekyll-target-blank ([#7046]({{ site.repository }}/issues/7046))
766 - plugin: json-get. ([#7086]({{ site.repository }}/issues/7086))
767 - plugin: `jekyll-info` ([#7091]({{ site.repository }}/issues/7091))
768 - plugin: jekyll-xml-source ([#7114]({{ site.repository }}/issues/7114))
769 - plugin: jekyll-firstimage filter ([#7127]({{ site.repository }}/issues/7127))
770 - plugin: CAT ([#7011]({{ site.repository }}/issues/7011))
771 - Resources: Statictastic ([#7593]({{ site.repository }}/issues/7593))
772 - Resources: Bonsai Search ([#7543]({{ site.repository }}/issues/7543))
773 - Resources: Formspark ([#7601]({{ site.repository }}/issues/7601))
774 - Resources: Jekpack([#7598]({{ site.repository }}/issues/7598))
775 - Resources: formX ([#7536]({{ site.repository }}/issues/7536))
776 - Resources: 99inbound&#39;s Jekyll post ([#7348]({{ site.repository }}/issues/7348))
777 - Resources: CloudSh ([#7497]({{ site.repository }}/issues/7497))
778 - Community: DEV Community&#39;s Jekyll tag ([#7139]({{ site.repository }}/issues/7139))
779 - Showcase: developer.spotify.com ([#7217]({{ site.repository }}/issues/7217))
780 - Showcase: Isomer ([#7300]({{ site.repository }}/issues/7300))
781 - Add version number for group_by_exp doc ([#6956]({{ site.repository }}/issues/6956))
782 - Updated nginx configuration for custom-404-page documentation ([#6994]({{ site.repository }}/issues/6994))
783 - Clarify definition of &#39;draft&#39; ([#7037]({{ site.repository }}/issues/7037))
784 - _drafts need to be contained within the custom collection directory ([#6985]({{ site.repository }}/issues/6985))
785 - Updated to supported version ([#7031]({{ site.repository }}/issues/7031))
786 - Add Hints for some Improved Travis Config in Doc ([#7049]({{ site.repository }}/issues/7049))
787 - Update travis-ci.md to point out &#34;this is an example Gemfile&#34; ([#7089]({{ site.repository }}/issues/7089))
788 - Instructions to view theme’s files under Linux ([#7095]({{ site.repository }}/issues/7095))
789 - Use a real theme in the example ([#7125]({{ site.repository }}/issues/7125))
790 - Update docs about post creation ([#7138]({{ site.repository }}/issues/7138))
791 - Initialize upgrading doc for v4.0 ([#7140]({{ site.repository }}/issues/7140))
792 - Add version badge for date filters with ordinal ([#7162]({{ site.repository }}/issues/7162))
793 - Corrected sample usage of postfiles ([#7181]({{ site.repository }}/issues/7181))
794 - Resolve &#34;Unable to locate package ruby2.4&#34; error ([#7196]({{ site.repository }}/issues/7196))
795 - Correct stylesheet url in tutorial step 7 ([#7210]({{ site.repository }}/issues/7210))
796 - Removes quotes from markdown for assets ([#7223]({{ site.repository }}/issues/7223))
797 - Clarified front matter requirement ([#7234]({{ site.repository }}/issues/7234))
798 - Explicit location of where to create blog.html ([#7241]({{ site.repository }}/issues/7241))
799 - Reference the build command options that allows multiple config files ([#7266]({{ site.repository }}/issues/7266))
800 - Add more issue template(s) and pull request template ([#7269]({{ site.repository }}/issues/7269))
801 - Suggest sites use OpenSSL instead of GnuTLS for their site&#39;s CI ([#7010]({{ site.repository }}/issues/7010))
802 - Fix broken Contributors link in README.markdown ([#7200]({{ site.repository }}/issues/7200))
803 - Add title tag to item in RSS template ([#7282]({{ site.repository }}/issues/7282))
804 - Add link tag to item in RSS template ([#7291]({{ site.repository }}/issues/7291))
805 - Remove redundant instruction comment ([#7342]({{ site.repository }}/issues/7342))
806 - Textile is only supported through a converter plugin ([#7003]({{ site.repository }}/issues/7003))
807 - Add recursive navigation tutorial ([#7720]({{ site.repository }}/issues/7720))
808 - Remove installation instructions with Homebrew ([#7381]({{ site.repository }}/issues/7381))
809 - Fix dead link and misleading prose ([#7383]({{ site.repository }}/issues/7383))
810 - Fix content management section ([#7385]({{ site.repository }}/issues/7385))
811 - Apply ruby official guide documents ([#7393]({{ site.repository }}/issues/7393))
812 - Fix group_by_exp filter example ([#7394]({{ site.repository }}/issues/7394))
813 - Remove alt attribute from a tags ([#7407]({{ site.repository }}/issues/7407))
814 - Fix BASH code-block in ubuntu.md ([#7420]({{ site.repository }}/issues/7420))
815 - zlib is missing ([#7428]({{ site.repository }}/issues/7428))
816 - Fixed unnecessary articles and pronouns ([#7466]({{ site.repository }}/issues/7466))
817 - Store SSL key and cert in site source ([#7473]({{ site.repository }}/issues/7473))
818 - Fix typo in tutorial for converting existing site ([#7524]({{ site.repository }}/issues/7524))
819 - Check if var exists before include tag ([#7530]({{ site.repository }}/issues/7530))
820 - Clarify docs on collections regarding the need for front matter ([#7538]({{ site.repository }}/issues/7538))
821 - Fix incorrect Windows path in themes.md ([#7525]({{ site.repository }}/issues/7525))
822 - Addresses bundle not found. ([#7351]({{ site.repository }}/issues/7351))
823 - Update the contribution docs for draft pull requests ([#7619]({{ site.repository }}/issues/7619))
824 - Data file section adds TSV ([#7640]({{ site.repository }}/issues/7640))
825 - Indicate where the _sass folder is by default ([#7644]({{ site.repository }}/issues/7644))
826 - Docs: add version tags to new placeholders ([#5981]({{ site.repository }}/issues/5981)) for permalinks ([#7647]({{ site.repository }}/issues/7647))
827 - Solve &#34;GitHub Page build failure&#34; in 10-deployment.md ([#7648]({{ site.repository }}/issues/7648))
828 - fix link to Site Source config ([#7708]({{ site.repository }}/issues/7708))
829 - Introduce frontmatter in step 2 ([#7704]({{ site.repository }}/issues/7704))
830 - Add @ashmaroli to Core Team listing ([#7398]({{ site.repository }}/issues/7398))
831 - Link to Tidelift in site&#39;s footer ([#7377]({{ site.repository }}/issues/7377))
832 - Link to OpenCollective backing ([#7378]({{ site.repository }}/issues/7378)
833 - Link to sponsor listing in README ([#7405]({{ site.repository }}/issues/7405))
834 - Adjust team page listings ([#7395]({{ site.repository }}/issues/7395))
835 - Updates to CODE OF CONDUCT (v1.4.0) ([#7105]({{ site.repository }}/issues/7105))
836 - More inclusive writing ([#7283]({{ site.repository }}/issues/7283))
837 - Update Ruby version used in Travis-CI example ([#7783]({{ site.repository }}/issues/7783))
838 - Documentation for binary operators in where_exp ([#7786]({{ site.repository }}/issues/7786))
839 - Adding SmartForms as Forms service ([#7794]({{ site.repository }}/issues/7794))
840
841 ### Site Enhancements
842 {: #site-enhancements-v4-0-0}
843
844 - Better Performance ([#7388]({{ site.repository }}/issues/7388))
845 - Add some minor improvements to image loading in Showcase page ([#7214]({{ site.repository }}/issues/7214))
846 - Simplify assigning classname to docs&#39; aside-links ([#7609]({{ site.repository }}/issues/7609))
847 - Simplify couple of includes in the docs site ([#7607]({{ site.repository }}/issues/7607))
848 - Avoid generating empty classnames ([#7610]({{ site.repository }}/issues/7610))
849 - Minimize rendering count ([#7343]({{ site.repository }}/issues/7343))
850
851 ### Release
852
853 - Jekyll v4.0 release ([#7782]({{ site.repository }}/issues/7782))
854 - Release post for v4.0.0 beta1 ([#7716]({{ site.repository }}/issues/7716))
855 - Release post for v4.0.0.pre.alpha1 ([#7574]({{ site.repository }}/issues/7574))
856 - Release post for v3.8.0 ([#6849]({{ site.repository }}/issues/6849))
857 - Release post for v3.6.3, v3.7.4 and v3.8.4 ([#7259]({{ site.repository }}/issues/7259))
858 - Post: v4.0 development ([#6934]({{ site.repository }}/issues/6934))
13859
14860
15861 ## 3.8.6 / 2019-07-02
47893 ### Bug Fixes
48894 {: #bug-fixes-v3-8-4}
49895
50 - security: fix `include` bypass of `EntryFilter#filter` symlink check ([#7228]({{ site.repository }}/issues/7228))
896 - 3.8.x: security: fix `include` bypass of `EntryFilter#filter` symlink check ([#7228]({{ site.repository }}/issues/7228))
51897
52898
53899 ## 3.8.3 / 2018-06-05
105951 - Minimize array allocations in the `where` filter ([#6860]({{ site.repository }}/issues/6860))
106952 - Bump JRuby ([#6878]({{ site.repository }}/issues/6878))
107953 - Assert existence of &lt;collection&gt;.files ([#6907]({{ site.repository }}/issues/6907))
108 - Bump Rubocop to 0.54.x ([#6915]({{ site.repository }}/issues/6915))
954 - Bump RuboCop to 0.54.x ([#6915]({{ site.repository }}/issues/6915))
109955 - Regenerate unconditionally unless its an incremental build ([#6917]({{ site.repository }}/issues/6917))
110956 - Centralize require statements ([#6910]({{ site.repository }}/issues/6910))
111 - Bump to Rubocop 0.55 ([#6929]({{ site.repository }}/issues/6929))
957 - Bump to RuboCop 0.55 ([#6929]({{ site.repository }}/issues/6929))
112958 - Refactor private method `HighlightBlock#parse_options` ([#6822]({{ site.repository }}/issues/6822))
113959
114960 ### Minor Enhancements
1581004 - doc: add liquid tag plugin jekyll-onebox for html previews ([#6898]({{ site.repository }}/issues/6898))
1591005 - Add `jekyll-w2m` to plugins ([#6855]({{ site.repository }}/issues/6855))
1601006 - Fix tutorials navigation HTML ([#6919]({{ site.repository }}/issues/6919))
161 - add Arch Linux instalation troubleshoot ([#6782]({{ site.repository }}/issues/6782))
1007 - add Arch Linux installation troubleshoot ([#6782]({{ site.repository }}/issues/6782))
1621008 - Docs: Install Jekyll on macOS ([#6881]({{ site.repository }}/issues/6881))
1631009 - Fix CodeClimate badges [ci skip] ([#6930]({{ site.repository }}/issues/6930))
1641010 - Update index.md ([#6933]({{ site.repository }}/issues/6933))
1811027 - `include_relative` tag should find related documents in collections gathered within custom `collections_dir` ([#6818]({{ site.repository }}/issues/6818))
1821028 - Handle liquid tags in excerpts robustly ([#6891]({{ site.repository }}/issues/6891))
1831029 - Allow front matter defaults to be applied properly to documents gathered under custom `collections_dir` ([#6885]({{ site.repository }}/issues/6885))
1030
1031
1032 ## 3.7.4 / 2018-09-07
1033 {: #v3-7-4}
1034
1035 ### Bug Fixes
1036 {: #bug-fixes-v3-7-4}
1037
1038 - Security: fix `include` bypass of EntryFilter#filter symlink check ([#7224]({{ site.repository }}/issues/7224))
1841039
1851040
1861041 ## 3.7.3 / 2018-02-25
3241179 - Fix list appearance by adding missing `ol` tag ([#6421]({{ site.repository }}/issues/6421))
3251180 - Explain how to override output collection index page ([#6424]({{ site.repository }}/issues/6424))
3261181 - Added github-cards to the list of plugins ([#6425]({{ site.repository }}/issues/6425))
327 - CoC violation correspondants ([#6429]({{ site.repository }}/issues/6429))
1182 - CoC violation correspondents ([#6429]({{ site.repository }}/issues/6429))
3281183 - Add a note about Liquid and syntax highlighting ([#6466]({{ site.repository }}/issues/6466))
3291184 - Remove `sudo` from macOS troubleshooting instructions ([#6486]({{ site.repository }}/issues/6486))
3301185 - Add a note on `:jekyll_plugins` group in the docs ([#6488]({{ site.repository }}/issues/6488))
3641219 - Fix permalink icon markup in news-item layout ([#6639]({{ site.repository }}/issues/6639))
3651220
3661221
1222 ## 3.6.3 / 2018-09-18
1223 {: #v3-6-3}
1224
1225 ### Bug Fixes
1226 {: #bug-fixes-v3-6-3}
1227
1228 - 3.6.x: security: fix `include` bypass of `EntryFilter#filter` symlink check ([#7229]({{ site.repository }}/issues/7229))
1229
1230
3671231 ## 3.6.2 / 2017-10-21
3681232 {: #v3-6-2}
3691233
4461310 - add SUPPORT file for GitHub ([#6324]({{ site.repository }}/issues/6324))
4471311 - Rename CODE_OF_CONDUCT to show in banner ([#6325]({{ site.repository }}/issues/6325))
4481312 - Docs : illustrate page.id for a collection&#39;s document ([#6329]({{ site.repository }}/issues/6329))
449 - Docs: post&#39;s date can be overriden in YAML front matter ([#6334]({{ site.repository }}/issues/6334))
1313 - Docs: post&#39;s date can be overridden in front matter ([#6334]({{ site.repository }}/issues/6334))
4501314 - Docs: `site.url` behavior on development and production environments ([#6270]({{ site.repository }}/issues/6270))
4511315 - Fix typo in site.url section of variables.md :-[ ([#6337]({{ site.repository }}/issues/6337))
4521316 - Docs: updates ([#6343]({{ site.repository }}/issues/6343))
5031367 ### Bug Fixes
5041368 {: #bug-fixes-v3-5-1}
5051369
506 - Backward compatiblize URLFilters module ([#6163]({{ site.repository }}/issues/6163))
1370 - Backward compatibilize URLFilters module ([#6163]({{ site.repository }}/issues/6163))
5071371 - Static files contain front matter default keys when `to_liquid`'d ([#6162]({{ site.repository }}/issues/6162))
5081372 - Always normalize the result of the `relative_url` filter ([#6185]({{ site.repository }}/issues/6185))
5091373
10161880 ### Minor Enhancements
10171881 {: #minor-enhancements-v3-2-0}
10181882
1019 - Stop testing with Ruby 2.0.x, which is EOL'd. ([#4381]({{ site.repository }}/issues/4381))
1883 - Stop testing with Ruby 2.0.x EOL ([#4381]({{ site.repository }}/issues/4381))
10201884 - Allow collections to have documents that have no file extension ([#4545]({{ site.repository }}/issues/4545))
10211885 - Add size property to `group_by` result ([#4557]({{ site.repository }}/issues/4557))
10221886 - Site Template: Removed unnecessary nesting from `_base.scss` ([#4637]({{ site.repository }}/issues/4637))
10421906 - Add 'jekyll new-theme' command to help users get up and running creating a theme ([#4848]({{ site.repository }}/issues/4848))
10431907 - `markdownify` and `smartify` should convert input to string before conversion ([#4958]({{ site.repository }}/issues/4958))
10441908 - Run `Site#generate` for 'jekyll doctor' to catch plugin issues ([#5005]({{ site.repository }}/issues/5005))
1045 - Add `normalize_whitepace` filter ([#4917]({{ site.repository }}/issues/4917))
1909 - Add `normalize_whitespace` filter ([#4917]({{ site.repository }}/issues/4917))
10461910 - Move bin/jekyll to exe/jekyll to prevent collision with binstubs ([#5014]({{ site.repository }}/issues/5014))
10471911 - Cleaning up site template & theme updates. ([#4922]({{ site.repository }}/issues/4922))
10481912 - Add fetch method to Drops ([#5056]({{ site.repository }}/issues/5056))
10941958 - Fix state leakage in Kramdown test ([#4618]({{ site.repository }}/issues/4618))
10951959 - Unify method for copying special files from repo to site ([#4601]({{ site.repository }}/issues/4601))
10961960 - Refresh the contributing file ([#4596]({{ site.repository }}/issues/4596))
1097 - change smartify doc from copy/paste of mardownify doc ([#4653]({{ site.repository }}/issues/4653))
1961 - change smartify doc from copy/paste of markdownify doc ([#4653]({{ site.repository }}/issues/4653))
10981962 - Update Rake & disable warnings when running tests ([#4720]({{ site.repository }}/issues/4720))
10991963 - Fix many warnings ([#4537]({{ site.repository }}/issues/4537))
11001964 - Don't blindly assume the last system when determining "open" cmd ([#4717]({{ site.repository }}/issues/4717))
11682032 - Fix typo on Chocolatey name in Windows documentation ([#4686]({{ site.repository }}/issues/4686))
11692033 - Use the correct URL, Fixes [#4698]({{ site.repository }}/issues/4698) ([#4699]({{ site.repository }}/issues/4699))
11702034 - Add jekyll-paspagon plugin ([#4700]({{ site.repository }}/issues/4700))
1171 - Bold-italicize note in assets documentation about needing yaml front matter ([#4706]({{ site.repository }}/issues/4706))
2035 - Bold-italicize note in assets documentation about needing front matter ([#4706]({{ site.repository }}/issues/4706))
11722036 - Highlight the `script/` calls in the Contributing documentation ([#4712]({{ site.repository }}/issues/4712))
11732037 - Add Hawkins to the list of third-party plugins ([#4755]({{ site.repository }}/issues/4755))
11742038 - Fix a typo in pagination doc ([#4763]({{ site.repository }}/issues/4763))
11932057 - Corrected pagination docs for hidden: true feature ([#4903]({{ site.repository }}/issues/4903))
11942058 - Remove a Broken Link for Refheap Plugin ([#4971]({{ site.repository }}/issues/4971))
11952059 - Instructions on how to install github-gem on Windows ([#4975]({{ site.repository }}/issues/4975))
1196 - Minor tweak to fix missing apostrophne ([#4962]({{ site.repository }}/issues/4962))
2060 - Minor tweak to fix missing apostrophe ([#4962]({{ site.repository }}/issues/4962))
11972061 - Instructions on how to install github-gem on Windows (v2) ([#4977]({{ site.repository }}/issues/4977))
11982062 - Fix inaccurate HTTP response header field name ([#4976]({{ site.repository }}/issues/4976))
11992063 - Add post about GSoC project ([#4980]({{ site.repository }}/issues/4980))
12012065 - Update normalize.css to v4.0.0. ([#4989]({{ site.repository }}/issues/4989))
12022066 - Add jekyll-tags-list-plugin to list of third-party plugins ([#5000]({{ site.repository }}/issues/5000))
12032067 - Windows docs: Command needs to be called from blog path ([#5006]({{ site.repository }}/issues/5006))
1204 - Update text to be consitent with example ([#5010]({{ site.repository }}/issues/5010))
2068 - Update text to be consistent with example ([#5010]({{ site.repository }}/issues/5010))
12052069 - Update template links to point to core Liquid site ([#5012]({{ site.repository }}/issues/5012))
12062070 - Add generator-jekyllized to third-party plugins ([#5027]({{ site.repository }}/issues/5027))
1207 - Add Jekyll Art Hallery generator plugin to list of third-party plugins ([#5043]({{ site.repository }}/issues/5043))
2071 - Add Jekyll Art Gallery generator plugin to list of third-party plugins ([#5043]({{ site.repository }}/issues/5043))
12082072 - Add Formingo to the list of Jekyll form SaaS ([#5054]({{ site.repository }}/issues/5054))
12092073 - Highlight help nav item when navigated to. ([#5058]({{ site.repository }}/issues/5058))
12102074 - Update normalize.css to v4.2.0. ([#5096]({{ site.repository }}/issues/5096))
13612225 - Drop: fix hash setter precedence ([#4312]({{ site.repository }}/issues/4312))
13622226 - utils: `has_yaml_header?` should accept files with extraneous spaces ([#4290]({{ site.repository }}/issues/4290))
13632227 - Escape html from site.title and page.title in site template ([#4307]({{ site.repository }}/issues/4307))
1364 - Allow custom file extensions if defined in `permalink` YAML front matter ([#4314]({{ site.repository }}/issues/4314))
2228 - Allow custom file extensions if defined in `permalink` front matter ([#4314]({{ site.repository }}/issues/4314))
13652229 - Fix deep_merge_hashes! handling of drops and hashes ([#4359]({{ site.repository }}/issues/4359))
13662230 - Page should respect output extension of its permalink ([#4373]({{ site.repository }}/issues/4373))
13672231 - Disable auto-regeneration when running server detached ([#4376]({{ site.repository }}/issues/4376))
13822246 - Reorganize and cleanup the Gemfile, shorten required depends. ([#4318]({{ site.repository }}/issues/4318))
13832247 - Remove script/rebund. ([#4341]({{ site.repository }}/issues/4341))
13842248 - Implement codeclimate platform ([#4340]({{ site.repository }}/issues/4340))
1385 - Remove ObectSpace dumping and start using inherited, it's faster. ([#4342]({{ site.repository }}/issues/4342))
2249 - Remove ObjectSpace dumping and start using inherited, it's faster. ([#4342]({{ site.repository }}/issues/4342))
13862250 - Add script/travis so all people can play with Travis-CI images. ([#4338]({{ site.repository }}/issues/4338))
1387 - Move Cucumber to using RSpec-Expections and furthering JRuby support. ([#4343]({{ site.repository }}/issues/4343))
2251 - Move Cucumber to using RSpec-Expectations and furthering JRuby support. ([#4343]({{ site.repository }}/issues/4343))
13882252 - Rearrange Cucumber and add some flair. ([#4347]({{ site.repository }}/issues/4347))
13892253 - Remove old FIXME ([#4349]({{ site.repository }}/issues/4349))
13902254 - Clean up the Gemfile (and keep all the necessary dependencies) ([#4350]({{ site.repository }}/issues/4350))
15852449 - Internal: trigger hooks by owner symbol ([#3871]({{ site.repository }}/issues/3871))
15862450 - Update MIME types from mime-db ([#3933]({{ site.repository }}/issues/3933))
15872451 - Add header to site template `_config.yml` for clarity & direction ([#3997]({{ site.repository }}/issues/3997))
1588 - Site template: add timezone offset to post date frontmatter ([#4001]({{ site.repository }}/issues/4001))
2452 - Site template: add timezone offset to post date front matter ([#4001]({{ site.repository }}/issues/4001))
15892453 - Make a constant for the regex to find hidden files ([#4032]({{ site.repository }}/issues/4032))
15902454 - Site template: refactor github & twitter icons into includes ([#4049]({{ site.repository }}/issues/4049))
15912455 - Site template: add background to Kramdown Rouge-ified backtick code blocks ([#4053]({{ site.repository }}/issues/4053))
16032467 - Fix nav items alignment when on multiple rows ([#3264]({{ site.repository }}/issues/3264))
16042468 - Highlight: Only Strip Newlines/Carriage Returns, not Spaces ([#3278]({{ site.repository }}/issues/3278))
16052469 - Find variables in front matter defaults by searching with relative file path. ([#2774]({{ site.repository }}/issues/2774))
1606 - Allow variables (e.g `:categories`) in YAML front matter permalinks ([#3320]({{ site.repository }}/issues/3320))
2470 - Allow variables (e.g `:categories`) in front matter permalinks ([#3320]({{ site.repository }}/issues/3320))
16072471 - Handle nil URL placeholders in permalinks ([#3325]({{ site.repository }}/issues/3325))
16082472 - Template: Fix nav items alignment when in "burger" mode ([#3329]({{ site.repository }}/issues/3329))
16092473 - Template: Remove `!important` from nav SCSS introduced in [#3329]({{ site.repository }}/issues/3329) ([#3375]({{ site.repository }}/issues/3375))
16202484 - Add WOFF2 font MIME type to Jekyll server MIME types ([#3647]({{ site.repository }}/issues/3647))
16212485 - Be smarter about extracting the extname in `StaticFile` ([#3632]({{ site.repository }}/issues/3632))
16222486 - Process metadata for all dependencies ([#3608]({{ site.repository }}/issues/3608))
1623 - Show error message if the YAML front matter on a page/post is invalid. ([#3643]({{ site.repository }}/issues/3643))
2487 - Show error message if the front matter on a page/post is invalid. ([#3643]({{ site.repository }}/issues/3643))
16242488 - Upgrade redcarpet to 3.2 (Security fix: OSVDB-120415) ([#3652]({{ site.repository }}/issues/3652))
16252489 - Create #mock_expects that goes directly to RSpec Mocks. ([#3658]({{ site.repository }}/issues/3658))
16262490 - Open `.jekyll-metadata` in binary mode to read binary Marshal data ([#3713]({{ site.repository }}/issues/3713))
17002564 - Add a Resources link to tutorial on building dynamic navbars ([#3185]({{ site.repository }}/issues/3185))
17012565 - Semantic structure improvements to the post and page layouts ([#3251]({{ site.repository }}/issues/3251))
17022566 - Add new AsciiDoc plugin to list of third-party plugins. ([#3277]({{ site.repository }}/issues/3277))
1703 - Specify that all transformable collection documents must contain YAML front matter ([#3271]({{ site.repository }}/issues/3271))
2567 - Specify that all transformable collection documents must contain front matter ([#3271]({{ site.repository }}/issues/3271))
17042568 - Assorted accessibility fixes ([#3256]({{ site.repository }}/issues/3256))
17052569 - Update configuration docs to mention `keep_files` for `destination` ([#3288]({{ site.repository }}/issues/3288), [#3296]({{ site.repository }}/issues/3296))
17062570 - Break when we successfully generate nav link to save CPU cycles. ([#3291]({{ site.repository }}/issues/3291))
17282592 - Add a link on all the docs pages to "Improve this page". ([#3510]({{ site.repository }}/issues/3510))
17292593 - Add jekyll-auto-image generator to the list of third-party plugins ([#3489]({{ site.repository }}/issues/3489))
17302594 - Replace link to the proposed `picture` element spec ([#3530]({{ site.repository }}/issues/3530))
1731 - Add frontmatter date formatting information ([#3469]({{ site.repository }}/issues/3469))
2595 - Add front matter date formatting information ([#3469]({{ site.repository }}/issues/3469))
17322596 - Improve consistency and clarity of plugins options note ([#3546]({{ site.repository }}/issues/3546))
17332597 - Add permalink warning to pagination docs ([#3551]({{ site.repository }}/issues/3551))
17342598 - Fix grammar in Collections docs API stability warning ([#3560]({{ site.repository }}/issues/3560))
17402604 - Define the `install` step in the CI example `.travis.yml` ([#3622]({{ site.repository }}/issues/3622))
17412605 - Expand collections documentation. ([#3638]({{ site.repository }}/issues/3638))
17422606 - Add the "warning" note label to excluding `vendor` in the CI docs page ([#3623]({{ site.repository }}/issues/3623))
1743 - Upgrade pieces of the Ugrading guide for Jekyll 3 ([#3607]({{ site.repository }}/issues/3607))
2607 - Upgrade pieces of the Upgrading guide for Jekyll 3 ([#3607]({{ site.repository }}/issues/3607))
17442608 - Showing how to access specific data items ([#3468]({{ site.repository }}/issues/3468))
17452609 - Clarify pagination works from within HTML files ([#3467]({{ site.repository }}/issues/3467))
17462610 - Add note to `excerpt_separator` documentation that it can be set globally ([#3667]({{ site.repository }}/issues/3667))
23003164 - Clean up the `<head>` in the site template ([#2186]({{ site.repository }}/issues/2186))
23013165 - Permit YAML blocks to end with three dots to better conform with the YAML spec ([#2110]({{ site.repository }}/issues/2110))
23023166 - Use `File.exist?` instead of deprecated `File.exists?` ([#2214]({{ site.repository }}/issues/2214))
2303 - Require newline after start of YAML Front Matter header ([#2211]({{ site.repository }}/issues/2211))
3167 - Require newline after start of front matter header ([#2211]({{ site.repository }}/issues/2211))
23043168 - Add the ability for pages to be marked as `published: false` ([#1492]({{ site.repository }}/issues/1492))
23053169 - Add `Jekyll::LiquidExtensions` with `.lookup_variable` method for easy looking up of variable values in a Liquid context. ([#2253]({{ site.repository }}/issues/2253))
23063170 - Remove literal lang name from class ([#2292]({{ site.repository }}/issues/2292))
28663730 - Add ReadInXMinutes plugin to the plugin list ([#1222]({{ site.repository }}/issues/1222))
28673731 - Remove plugins from the plugin list that have equivalents in Jekyll proper ([#1223]({{ site.repository }}/issues/1223))
28683732 - Add jekyll-assets to the plugin list ([#1225]({{ site.repository }}/issues/1225))
2869 - Add jekyll-pandoc-mulitple-formats to the plugin list ([#1229]({{ site.repository }}/issues/1229))
3733 - Add jekyll-pandoc-multiple-formats to the plugin list ([#1229]({{ site.repository }}/issues/1229))
28703734 - Remove dead link to "Using Git to maintain your blog" ([#1227]({{ site.repository }}/issues/1227))
28713735 - Tidy up the third-party plugins listing ([#1228]({{ site.repository }}/issues/1228))
28723736 - Update contributor information ([#1192]({{ site.repository }}/issues/1192))
30413905 - Adds excerpt attribute to posts which contains first paragraph of content ([#837]({{ site.repository }}/issues/837))
30423906 - Accept custom configuration file via CLI ([#863]({{ site.repository }}/issues/863))
30433907 - Load in GitHub Pages MIME Types on `jekyll serve` ([#847]({{ site.repository }}/issues/847), [#871]({{ site.repository }}/issues/871))
3044 - Improve debugability of error message for a malformed highlight tag ([#785]({{ site.repository }}/issues/785))
3908 - Improve debuggability of error message for a malformed highlight tag ([#785]({{ site.repository }}/issues/785))
30453909 - Allow symlinked files in unsafe mode ([#824]({{ site.repository }}/issues/824))
30463910 - Add 'gist' Liquid tag to core ([#822]({{ site.repository }}/issues/822), [#861]({{ site.repository }}/issues/861))
30473911 - New format of Jekyll output ([#795]({{ site.repository }}/issues/795))
30683932 - Bullet-proof `limit_posts` option ([#1004]({{ site.repository }}/issues/1004))
30693933 - Read in YAML as UTF-8 to accept non-ASCII chars ([#836]({{ site.repository }}/issues/836))
30703934 - Fix the CLI option `--plugins` to actually accept dirs and files ([#993]({{ site.repository }}/issues/993))
3071 - Allow 'excerpt' in YAML front matter to override the extracted excerpt ([#946]({{ site.repository }}/issues/946))
3935 - Allow 'excerpt' in front matter to override the extracted excerpt ([#946]({{ site.repository }}/issues/946))
30723936 - Fix cascade problem with site.baseurl, site.port and site.host. ([#935]({{ site.repository }}/issues/935))
30733937 - Filter out directories with valid post names ([#875]({{ site.repository }}/issues/875))
30743938 - Fix symlinked static files not being correctly built in unsafe mode ([#909]({{ site.repository }}/issues/909))
30803944 - Patch for multibyte URI problem with `jekyll serve` ([#723]({{ site.repository }}/issues/723))
30813945 - Order plugin execution by priority ([#864]({{ site.repository }}/issues/864))
30823946 - Fixed Page#dir and Page#url for edge cases ([#536]({{ site.repository }}/issues/536))
3083 - Fix broken `post_url` with posts with a time in their YAML front matter ([#831]({{ site.repository }}/issues/831))
3947 - Fix broken `post_url` with posts with a time in their front matter ([#831]({{ site.repository }}/issues/831))
30843948 - Look for plugins under the source directory ([#654]({{ site.repository }}/issues/654))
30853949 - Tumblr Migrator: finds `_posts` dir correctly, fixes truncation of long post names ([#775]({{ site.repository }}/issues/775))
30863950 - Force Categories to be Strings ([#767]({{ site.repository }}/issues/767))
33114175
33124176 - Bug Fixes
33134177 - Require redcloth >= 4.2.1 in tests ([#92]({{ site.repository }}/issues/92))
3314 - Don't break on triple dashes in yaml front matter ([#93]({{ site.repository }}/issues/93))
4178 - Don't break on triple dashes in front matter ([#93]({{ site.repository }}/issues/93))
33154179
33164180 ### Minor Enhancements
33174181 {: #minor-enhancements-v0-5-6}
33524216 - Added --paginate option to the executable along with a paginator object for the payload (@calavera)
33534217 - Upgraded RedCloth to 4.2.1, which makes `<notextile>` tags work once again.
33544218 - Configuration options set in config.yml are now available through the site payload (@vilcans)
3355 - Posts can now have an empty YAML front matter or none at all (@ bahuvrihi)
4219 - Posts can now have an empty front matter or none at all (@ bahuvrihi)
33564220 - Bug Fixes
33574221 - Fixing Ruby 1.9 issue that requires `#to_s` on the err object (@Chrononaut)
33584222 - Fixes for pagination and ordering posts on the same day (@ujh)
33604224 - Index.html file should always have index.html permalink (@eugenebolshakov)
33614225 - Added trailing slash to pretty permalink style so Apache is happy (@eugenebolshakov)
33624226 - Bad markdown processor in config fails sooner and with better message (@ gcnovus)
3363 - Allow CRLFs in yaml front matter (@juretta)
4227 - Allow CRLFs in front matter (@juretta)
33644228 - Added Date#xmlschema for Ruby versions < 1.9
33654229
33664230
34554319 - Added post categories based on directories containing `_posts` (@mreid)
34564320 - Added post topics based on directories underneath `_posts`
34574321 - Added new date filter that shows the full month name (@mreid)
3458 - Merge Post's YAML front matter into its to_liquid payload (@remi)
4322 - Merge Post's front matter into its to_liquid payload (@remi)
34594323 - Restrict includes to regular files underneath `_includes`
34604324 - Bug Fixes
34614325 - Change YAML delimiter matcher so as to not chew up 2nd level markdown headers (@mreid)
4747
4848 {% raw %}
4949 ```liquid
50 {% include {{ page.my_variable }} %}
50 {% if page.my_variable %}
51 {% include {{ page.my_variable }} %}
52 {% endif %}
5153 ```
5254 {% endraw %}
5355
6668 ```
6769 {% endraw %}
6870
69 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:
71 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:
7072
7173 {% raw %}
7274 ```liquid
8284
8385 ```html
8486 <figure>
85 <a href="http://jekyllrb.com">
87 <a href="https://jekyllrb.com">
8688 <img src="logo.png" style="max-width: 200px;"
8789 alt="Jekyll logo" />
90 </a>
8891 <figcaption>This is the Jekyll logo</figcaption>
8992 </figure>
9093 ```
97100 <a href="{{ include.url }}">
98101 <img src="{{ include.file }}" style="max-width: {{ include.max-width }};"
99102 alt="{{ include.alt }}"/>
103 </a>
100104 <figcaption>{{ include.caption }}</figcaption>
101105 </figure>
102106 ```
124128
125129 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/).
126130
127 Overall, you can create includes that act as templates for a variety of uses &mdash; inserting audio or video clips, alerts, special formatting, and more. However, 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.)
131 Overall, you can create includes that act as templates for a variety of uses &mdash; 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.)
128132
129133 ### Passing parameter variables to includes
130134
149153 {% include note.html content=download_note %}
150154 ```
151155 {% endraw %}
152
153 ### Passing references to YAML files as parameter values
154
155 Instead of passing string variables to the include, you can pass a reference to a YAML data file stored in the `_data` folder.
156
157 Here's an example. In the `_data` folder, suppose you have a YAML file called `profiles.yml`. Its content looks like this:
158
159 ```yaml
160 - name: John Doe
161 login_age: old
162 image: johndoe.jpg
163
164 - name: Jane Doe
165 login_age: new
166 image: janedoe.jpg
167 ```
168
169 In the `_includes` folder, assume you have a file called `spotlight.html` with this code:
170
171 {% raw %}
172 ```liquid
173 {% for person in include.participants %}
174 {% if person.login_age == "new" %}
175 {{ person.name }}
176 {% endif %}
177 {% endfor %}
178 ```
179 {% endraw %}
180
181 Now when you insert the `spotlight.html` include file, you can submit the YAML file as a parameter:
182
183 {% raw %}
184 ```liquid
185 {% include spotlight.html participants=site.data.profiles %}
186 ```
187 {% endraw %}
188
189 In this instance, `site.data.profiles` gets inserted in place of {% raw %}`include.participants`{% endraw %} in the include file, and the Liquid logic processes. The result will be `Jane Doe`.
00 ---
1 title: Welcome
2 permalink: /docs/home/
3 redirect_from: /docs/index.html
1 title: Quickstart
2 permalink: /docs/
3 redirect_from:
4 - /docs/home/
5 - /docs/quickstart/
6 - /docs/extras/
47 ---
8 Jekyll is a static site generator. It takes text written in your
9 favorite markup language and uses layouts to create a static website. You can
10 tweak the site's look and feel, URLs, the data displayed on the page, and more.
511
6 This site aims to be a comprehensive guide to Jekyll. We’ll cover topics such as getting your site up and running, creating and managing content, customizing your build, and deploying.
12 ## Prerequisites
713
8 ## What is Jekyll, exactly?
14 Jekyll requires the following:
915
10 Jekyll is a simple, blog-aware, static site generator.
16 * Ruby version **{{ site.data.ruby.min_version }}** or higher
17 * RubyGems
18 * GCC and Make
1119
12 You create your content as text files ([Markdown](https://daringfireball.net/projects/markdown/)), and organize them into folders. Then, you build the shell of your site using [Liquid](https://shopify.github.io/liquid/)-enhanced HTML templates. Jekyll automatically stitches the content and templates together, generating a website made entirely of static assets, suitable for uploading to any server.
20 See [Requirements]({{ '/docs/installation/#requirements' | relative_url }}) for guides and details.
1321
14 Jekyll happens to be the engine behind [GitHub Pages](https://pages.github.com), so you can host your project’s Jekyll page/blog/website on GitHub’s servers **for free**.
22 ## Instructions
1523
16 ## Navigating the Guide
24 1. Install all [prerequisites]({{ '/docs/installation/' | relative_url }}).
25 2. Install the jekyll and bundler [gems]({{ '/docs/ruby-101/#gems' | relative_url }}).
26 ```sh
27 gem install jekyll bundler
28 ```
29 3. Create a new Jekyll site at `./myblog`.
30 ```sh
31 jekyll new myblog
32 ```
33 4. Change into your new directory.
34 ```sh
35 cd myblog
36 ```
37 5. Build the site and make it available on a local server.
38 ```sh
39 bundle exec jekyll serve
40 ```
41 6. Browse to [http://localhost:4000](http://localhost:4000){:target="_blank"}
1742
18 Throughout this guide, you'll see these special sections that help you get the most out of Jekyll:
43 {: .note .warning}
44 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`
1945
20 <div class="note">
21 <h5>ProTips™</h5>
22 <p>Tips and tricks that'll make you a Jekyll wizard!</p>
23 </div>
46 {: .note .info}
47 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`
2448
25 <div class="note info">
26 <h5>Notes</h5>
27 <p>Extra tidbits that are sometimes necessary to understand Jekyll.</p>
28 </div>
2949
30 <div class="note warning">
31 <h5>Warnings</h5>
32 <p>Common pitfalls to avoid.</p>
33 </div>
50 If you encounter any errors during this process, check that you have installed all the prerequisites in [Requirements]({{ '/docs/installation/#requirements' | relative_url }}).
51 If you still have issues, see [Troubleshooting]({{ '/docs/troubleshooting/#configuration-problems' | relative_url }}).
3452
35 <div class="note unreleased">
36 <h5>Unreleased</h5>
37 <p>Features planned for future versions of Jekyll, but not available yet.</p>
38 </div>
39
40 If you find anything we haven’t covered, or would like to share a tip that others might find handy, please [file an issue]({{ site.repository }}/issues/new) and we’ll see about adding it to the guide.
53 {: .note .info}
54 Installation varies based on your operating system. See our [guides]({{ '/docs/installation/#guides' | relative_url }}) for OS-specific instructions.
0 ---
1 title: Jekyll on macOS
2 permalink: /docs/installation/macos/
3 ---
4
5 ## Supported macOS versions
6
7 - Monterey (macOS 12)
8 - Big Sur (macOS 11)
9 - Catalina (macOS 10.15)
10
11 Older macOS versions might work, but we don't officially support them.
12
13 ## Install Ruby
14
15 To install Jekyll on macOS, you need a proper Ruby development environment.
16 While macOS comes preinstalled with Ruby, we don't recommend using that version
17 to install Jekyll. This external article goes over the various reasons
18 [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/).
19
20 Instead, you'll need to install a separate and newer version of Ruby using a
21 version manager such as [asdf], [chruby], [rbenv], or [rvm]. Version managers
22 allow you to easily install multiple versions of Ruby, and switch between them.
23
24 We recommend `chruby` because it's the simplest and least likely to cause issues.
25
26 The instructions below are an excerpt from this detailed external guide to
27 [install Ruby on Mac]. They work best if you're setting up development tools
28 for the first time on your Mac. If you've already tried to install Ruby or
29 Jekyll on your Mac, or if you run into any issues, read that guide.
30
31 [asdf]: https://asdf-vm.com/
32 [chruby]: https://github.com/postmodern/chruby
33 [rbenv]: https://github.com/rbenv/rbenv
34 [rvm]: https://rvm.io/
35 [install Ruby on Mac]: https://www.moncefbelyamani.com/how-to-install-xcode-homebrew-git-rvm-ruby-on-mac/
36
37 ### Step 1: Install Homebrew
38
39 [Homebrew](https://brew.sh/) makes it easy to install development tools on a Mac.
40
41 ```sh
42 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
43 ```
44
45 ### Step 2: Install chruby and the latest Ruby with ruby-install
46
47 Install `chruby` and `ruby-install` with Homebrew:
48
49 ```sh
50 brew install chruby ruby-install xz
51 ```
52
53 Install the latest stable version of Ruby:
54
55 ```sh
56 ruby-install ruby
57 ```
58
59 This will take a few minutes, and once it's done, configure your shell to
60 automatically use `chruby`:
61
62 ```sh
63 echo "source $(brew --prefix)/opt/chruby/share/chruby/chruby.sh" >> ~/.zshrc
64 echo "source $(brew --prefix)/opt/chruby/share/chruby/auto.sh" >> ~/.zshrc
65 echo "chruby ruby-{{ site.data.ruby.current_version }}" >> ~/.zshrc # run 'chruby' to see actual version
66 ```
67
68 If you're using Bash, replace `.zshrc` with `.bash_profile`. If you're not sure,
69 read this external guide to
70 [find out which shell you're using](https://www.moncefbelyamani.com/which-shell-am-i-using-how-can-i-switch/).
71
72 Quit and relaunch Terminal, then check that everything is working:
73
74 ```sh
75 ruby -v
76 ```
77
78 It should show {{ site.data.ruby.current_version_output }} or a newer version.
79
80 Next, read that same external guide for important notes about
81 [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).
82
83 ## Install Jekyll
84
85 After installing Ruby with chruby, install the latest Jekyll gem:
86
87 ```sh
88 gem install jekyll
89 ```
90
91 ## Troubleshooting
92
93 See [Troubleshooting]({{ '/docs/troubleshooting/' | relative_url }}) or [ask for help on our forum](https://talk.jekyllrb.com).
0 ---
1 title: Jekyll on Linux
2 permalink: /docs/installation/other-linux/
3 ---
4
5 Installation on other Linux distributions works similarly to installing on [Ubuntu](../ubuntu/).
6
7 ## Install prerequisites
8
9 ### Fedora
10
11 ```sh
12 sudo dnf install ruby ruby-devel openssl-devel redhat-rpm-config @development-tools
13 ```
14 ### RHEL8/CentOS8
15
16 ```sh
17 sudo dnf install ruby ruby-devel
18 sudo dnf group install "Development Tools"
19 ```
20
21 ### Debian
22
23 ```sh
24 sudo apt-get install ruby-full build-essential
25 ```
26
27 ### Gentoo
28
29 ```sh
30 sudo emerge -av jekyll
31 ```
32
33 or
34
35 ```sh
36 sudo emerge --ask --verbose jekyll
37 ```
38
39 ### ArchLinux
40
41 ```sh
42 sudo pacman -S ruby base-devel
43 ```
44
45 ### OpenSUSE
46
47 ```sh
48 sudo zypper install -t pattern devel_ruby devel_C_C++
49 sudo zypper install ruby-devel
50 ```
51
52 ### Clear Linux
53
54 ```sh
55 sudo swupd bundle-add ruby-basic
56 ```
57 ## Install Jekyll
58
59 Follow the instructions for [Ubuntu](../ubuntu/).
0 ---
1 title: Jekyll on Ubuntu
2 permalink: /docs/installation/ubuntu/
3 ---
4
5 ## Install dependencies
6
7 Install Ruby and other [prerequisites]({{ '/docs/installation/#requirements' | relative_url }}):
8
9 ```sh
10 sudo apt-get install ruby-full build-essential zlib1g-dev
11 ```
12
13 Avoid installing RubyGems packages (called gems) as the root user. Instead,
14 set up a gem installation directory for your user account. The following
15 commands will add environment variables to your `~/.bashrc` file to configure
16 the gem installation path:
17
18 ```sh
19 echo '# Install Ruby Gems to ~/gems' >> ~/.bashrc
20 echo 'export GEM_HOME="$HOME/gems"' >> ~/.bashrc
21 echo 'export PATH="$HOME/gems/bin:$PATH"' >> ~/.bashrc
22 source ~/.bashrc
23 ```
24
25 Finally, install Jekyll and Bundler:
26
27 ```sh
28 gem install jekyll bundler
29 ```
30
31 That's it! You're ready to start using Jekyll.
0 ---
1 title: Jekyll on Windows
2 permalink: /docs/installation/windows/
3 redirect_from:
4 - /docs/windows/
5 ---
6
7 While Windows is not an officially-supported platform, it can be used to run Jekyll with the proper tweaks.
8
9 ## Installing Ruby and Jekyll
10
11 ### Installation via RubyInstaller
12
13 The easiest way to install Ruby and Jekyll is by using the [RubyInstaller](https://rubyinstaller.org/) for Windows.
14
15 RubyInstaller is a self-contained Windows-based installer that includes the Ruby language, an execution environment,
16 important documentation, and more.
17
18 We only cover RubyInstaller-2.4 and newer here. Older versions need to
19 [install the Devkit](https://github.com/oneclick/rubyinstaller/wiki/Development-Kit) manually.
20
21 1. Download and install a **Ruby+Devkit** version from [RubyInstaller Downloads](https://rubyinstaller.org/downloads/).
22 Use default options for installation.
23 2. Run the `ridk install` step on the last stage of the installation wizard. This is needed for installing gems with native
24 extensions. You can find additional information regarding this in the
25 [RubyInstaller Documentation](https://github.com/oneclick/rubyinstaller2#using-the-installer-on-a-target-system).
26 From the options choose `MSYS2 and MINGW development tool chain`.
27 3. Open a new command prompt window from the start menu, so that changes to the `PATH` environment variable becomes effective.
28 Install Jekyll and Bundler using `gem install jekyll bundler`
29 4. Check if Jekyll has been installed properly: `jekyll -v`
30
31 {: .note .info}
32 You may receive an error when checking if Jekyll has not been installed properly. Reboot your system and run `jekyll -v` again.
33 If the error persists, please open a [RubyInstaller issue](https://github.com/oneclick/rubyinstaller2/issues/new).
34
35 That's it, you're ready to use Jekyll!
36
37 ### Installation via Bash on Windows 10
38
39 If you are using Windows 10 version 1607 or later, another option to run Jekyll is by
40 [installing](https://msdn.microsoft.com/en-us/commandline/wsl/install_guide) the Windows Subsystem for Linux.
41
42 {: .note .info}
43 You must have [Windows Subsystem for Linux](https://msdn.microsoft.com/en-us/commandline/wsl/about) enabled.
44
45 Make sure all your packages and repositories are up to date. Open a new Command Prompt or PowerShell window and type `bash`.
46
47 Your terminal should now be a Bash instance. Next, update your repository lists and packages:
48
49 ```sh
50 sudo apt-get update -y && sudo apt-get upgrade -y
51 ```
52
53 Next, install Ruby. To do this, let's use a repository from [BrightBox](https://www.brightbox.com/docs/ruby/ubuntu/),
54 which hosts optimized versions of Ruby for Ubuntu.
55
56 ```sh
57 sudo apt-add-repository ppa:brightbox/ruby-ng
58 sudo apt-get update
59 sudo apt-get install ruby2.5 ruby2.5-dev build-essential dh-autoreconf
60 ```
61
62 Next, update your Ruby gems:
63
64 ```sh
65 gem update
66 ```
67
68 Install Jekyll:
69
70 ```sh
71 gem install jekyll bundler
72 ```
73
74 {: .note .info}
75 No `sudo` here.
76
77 Check your Jekyll version:
78
79 ```sh
80 jekyll -v
81 ```
82
83 That's it! You're ready to start using Jekyll.
84
85 You can make sure time management is working properly by inspecting your `_posts` folder. You should see a markdown file
86 with the current date in the filename.
87
88 <div class="note info">
89 <h5>Non-superuser account issues</h5>
90 <p>If the `jekyll new` command prints the error "Your user account isn't allowed to install to the system RubyGems", see
91 the "Running Jekyll as Non-Superuser" instructions in
92 <a href="{{ '/docs/troubleshooting/#no-sudo' | relative_url }}">Troubleshooting</a>.</p>
93 </div>
94
95 {: .note .info}
96 Bash on Ubuntu on Windows is still under development, so you may run into issues.
97
98 ## Encoding
99
100 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.
101
102 Additionally, you might need to change the code page of the console window to UTF-8 in case you get a
103 `Liquid Exception: Incompatible character encoding` error during the site generation process. Run the following:
104
105 ```sh
106 chcp 65001
107 ```
108
109 ## Time Zone Management
110
111 Since Windows doesn't have a native source of zoneinfo data, the Ruby Interpreter doesn't understand IANA Timezones.
112 Using them had the `TZ` environment variable default to UTC/GMT 00:00.
113
114 Though Windows users could alternatively define their blog's timezone by setting the key to use the POSIX format of defining
115 timezones, it wasn't as user-friendly when it came to having the clock altered to changing DST-rules.
116
117 Jekyll now uses a rubygem to internally configure Timezone based on established
118 [IANA Timezone Database](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones).
119
120 While 'new' blogs created with Jekyll v3.4 and greater, will have the following added to their `Gemfile` by default, existing
121 sites *will* have to update their `Gemfile` (and installed gems) to enable development on Windows:
122
123 ```ruby
124 # Windows and JRuby does not include zoneinfo files, so bundle the tzinfo-data gem
125 # and associated library.
126 platforms :mingw, :x64_mingw, :mswin, :jruby do
127 gem "tzinfo", ">= 1", "< 3"
128 gem "tzinfo-data"
129 end
130 ```
131
132 ## Auto Regeneration
133
134 Jekyll uses the `listen` gem to watch for changes when the `--watch` switch is specified during a build or serve.
135 While `listen` has built-in support for UNIX systems, it may require an extra gem for compatibility with Windows.
136
137 Add the following to the `Gemfile` for your site if you have issues with auto-regeneration on Windows alone:
138
139 ```ruby
140 gem 'wdm', '~> 0.1.1', :install_if => Gem.win_platform?
141 ```
142
143 You have to use a [Ruby+Devkit](https://rubyinstaller.org/downloads/) version of the RubyInstaller and install
144 the MSYS2 build tools to successfully install the `wdm` gem.
33 permalink: /docs/installation/
44 ---
55
6 - [Requirements](#requirements)
7 - [Install on macOS](#macOS)
8 - [Install on Windows](../windows/)
9 - [Upgrade Jekyll](#upgrade-jekyll)
10
11 Installing Jekyll should be straight-forward if your system meets the requirements.
6 Jekyll is a [Ruby Gem]({{ '/docs/ruby-101/#gems' | relative_url }}) that can be installed on most systems.
127
138 ## Requirements
149
15 Before you start, make sure your system has the following:
10 * [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`)
11 * [RubyGems](https://rubygems.org/pages/download) (check your Gems version using `gem -v`)
12 * [GCC](https://gcc.gnu.org/install/) and [Make](https://www.gnu.org/software/make/) (check versions using `gcc -v`,`g++ -v`, and `make -v`)
1613
17 - [Ruby](https://www.ruby-lang.org/en/downloads/) version 2.2.5 or above, including all development headers (ruby installation can be checked by running `ruby -v`)
18 - [RubyGems](https://rubygems.org/pages/download) (which you can check by running `gem -v`)
19 - [GCC](https://gcc.gnu.org/install/) and [Make](https://www.gnu.org/software/make/) (in case your system doesn't have them installed, which you can check by running `gcc -v`,`g++ -v` and `make -v` in your system's command line interface)
14 ## Guides
2015
21 ## Install on macOS {#macOS}
16 For detailed install instructions, follow the guide for your operating system.
2217
23 We only cover macOS High Sierra 10.13 here, which comes with Ruby 2.3.3, older systems will need to [install a more recent Ruby version via Homebrew](#homebrew).
24
25 First, you need to install the command-line tools to be able to compile native extensions, open a terminal and run:
26
27 ```sh
28 xcode-select --install
29 ```
30
31 ### Set up Ruby included with the OS
32
33 Check your Ruby version meet our requirements:
34
35 ```sh
36 ruby -v
37 2.3.3
38 ```
39
40 Great, let's install Jekyll. We also need [Bundler](https://bundler.io/) to help us handle [plugins](../plugins) and [themes](../themes):
41
42 ```sh
43 gem install bundler jekyll
44 ```
45
46 That's it, you're ready to go, either by installing our [default minimal blog theme](https://github.com/jekyll/minima) with `jekyll new jekyll-website` or by starting from scratch:
47
48 ```sh
49 mkdir jekyll-website
50 cd jekyll-website
51
52 # Create a Gemfile
53 bundle init
54
55 # Add Jekyll
56 bundle add jekyll
57
58 # Install gems
59 bundle install
60 ```
61
62 Great, from there you can now either use a [theme](../themes/) or [create your own layouts](../templates/).
63
64 ### Install a newer Ruby version via Homebrew {#homebrew}
65
66 If you wish to install the latest version of Ruby and get faster builds, we recommend to do it via [Homebrew](https://brew.sh) a handy package manager for macOS.
67
68 ```sh
69 /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
70 brew install ruby
71 ruby -v
72 ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-darwin17]
73 ```
74
75 Yay! Now you have a shiny Ruby on your system!
76
77 ### Install multiple Ruby versions with rbenv {#rbenv}
78
79 Developers often use [rbenv](https://github.com/rbenv/rbenv) to manage multiple Ruby versions. This can be useful if you want to run the same Ruby version used by [GitHub Pages](https://pages.github.com/versions/) or [Netlify](https://www.netlify.com/docs/#ruby) for instance.
80
81 ```sh
82 # Install rbenv and ruby-build
83 brew install rbenv
84
85 # Setup rbenv integration to your shell
86 rbenv init
87
88 # Check your install
89 curl -fsSL https://github.com/rbenv/rbenv-installer/raw/master/bin/rbenv-doctor | bash
90 ```
91
92 Restart your terminal for changes to take effect.
93 Now we can install the Ruby version of our choice, let's go with Ruby 2.5.1 here:
94
95 ```sh
96 rbenv install 2.5.1
97 rbenv global 2.5.1
98 ruby -v
99 ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-darwin17]
100 ```
101
102 That's it! Head over [rbenv command references](https://github.com/rbenv/rbenv#command-reference) to learn how to use different versions of Ruby in your projects.
103
104 <div class="note info" markdown="1">
105
106 ##### Problems installing Jekyll?
107
108 Check out the [troubleshooting](../troubleshooting/) page or
109 [ask for help on our forum](https://talk.jekyllrb.com).
110
111 </div>
112
113 ## Upgrade Jekyll
114
115 Before you start developing with Jekyll, you may want to check that you're up to date with the latest version. To find the currently installed version of Jekyll, run one of these commands:
116
117 ```sh
118 jekyll --version
119 gem list jekyll
120 ```
121
122 You can use RubyGems to find [the current version of Jekyll](https://rubygems.org/gems/jekyll). Another way to check if you have the latest version is to run the command `gem outdated`. This will provide a list of all the gems on your system that need to be updated. If you aren't running the latest version, run this command:
123
124 ```sh
125 bundle update jekyll
126 ```
127
128 Alternatively, if you don't have Bundler installed run:
129
130 ```sh
131 gem update jekyll
132 ```
133
134 To upgrade to latest Rubygems, run:
135
136 ```
137 gem update --system
138 ```
139
140 Refer to our [upgrading section](../upgrading/) to upgrade from Jekyll 2.x or 1.x.
141
142 ## Pre-releases
143
144 In order to install a pre-release, make sure you have all the requirements
145 installed properly and run:
146
147 ```sh
148 gem install jekyll --pre
149 ```
150
151 This will install the latest pre-release. If you want a particular pre-release,
152 use the `-v` switch to indicate the version you'd like to install:
153
154 ```sh
155 gem install jekyll -v '2.0.0.alpha.1'
156 ```
157
158 If you'd like to install a development version of Jekyll, the process is a bit
159 more involved. This gives you the advantage of having the latest and greatest,
160 but may be unstable.
161
162 ```sh
163 git clone git://github.com/jekyll/jekyll.git
164 cd jekyll
165 script/bootstrap
166 bundle exec rake build
167 ls pkg/*.gem | head -n 1 | xargs gem install -l
168 ```
169
170 Now that you’ve got everything up-to-date and installed, let’s get to work!
18 * [macOS]({{ '/docs/installation/macos/' | relative_url }})
19 * [Ubuntu]({{ '/docs/installation/ubuntu/' | relative_url }})
20 * [Other Linux]({{ '/docs/installation/other-linux/' | relative_url }})
21 * [Windows]({{ '/docs/installation/windows/' | relative_url }})
0 ---
1 title: Layouts
2 description: placeholder
3 permalink: /docs/layouts/
4 ---
5 Layouts are templates that wrap around your content. They allow you to have the
6 source code for your template in one place so you don't have to repeat things
7 like your navigation and footer on every page.
8
9 Layouts live in the `_layouts` directory. The convention is to have a base
10 template called `default.html` and have other layouts [inherit](#inheritance)
11 from this as needed.
12
13 <div class="note">
14 <h5>Layouts Directory</h5>
15 <p>
16 Jekyll looks for the <code>_layouts</code> directory either at the root of
17 your site's <code>source</code> or at the root of your theme.
18 </p>
19 <p>
20 While you can configure the directory name in which your layouts can reside by
21 setting the <code>layouts_dir</code> key in your config file, the directory
22 itself should be located at the root of your site's <code>source</code> directory.
23 </p>
24 </div>
25
26 ## Usage
27
28 The first step is to put the template source code in `default.html`. `content`
29 is a special variable, the value is the rendered content of the post or page
30 being wrapped.
31
32 {% raw %}
33 ```liquid
34 <!doctype html>
35 <html lang="en">
36 <head>
37 <meta charset="utf-8">
38 <title>{{ page.title }}</title>
39 <link rel="stylesheet" href="/css/style.css">
40 </head>
41 <body>
42 <nav>
43 <a href="/">Home</a>
44 <a href="/blog/">Blog</a>
45 </nav>
46 <h1>{{ page.title }}</h1>
47 <section>
48 {{ content }}
49 </section>
50 <footer>
51 &copy; to me
52 </footer>
53 </body>
54 </html>
55 ```
56 {% endraw %}
57
58 You have full access to the front matter of the origin. In the
59 example above, `page.title` comes from the page front matter.
60
61 Next you need to specify what layout you're using in your page's front matter.
62 You can also use
63 [front matter defaults](/docs/configuration/front-matter-defaults/) to save you
64 from having to set this on every page.
65
66 ```markdown
67 ---
68 title: My First Page
69 layout: default
70 ---
71
72 This is the content of my page
73 ```
74
75 The rendered output of this page is:
76
77 ```html
78 <!doctype html>
79 <html lang="en">
80 <head>
81 <meta charset="utf-8">
82 <title>My First Page</title>
83 <link rel="stylesheet" href="/css/style.css">
84 </head>
85 <body>
86 <nav>
87 <a href="/">Home</a>
88 <a href="/blog/">Blog</a>
89 </nav>
90 <h1>My First Page</h1>
91 <section>
92 This is the content of my page
93 </section>
94 <footer>
95 &copy; to me
96 </footer>
97 </body>
98 </html>
99 ```
100
101 ## Inheritance
102
103 Layout inheritance is useful when you want to add something to an existing
104 layout for a portion of documents on your site. A common example of this is
105 blog posts, you might want a post to display the date and author but otherwise
106 be identical to your base layout.
107
108 To achieve this you need to create another layout which specifies your original
109 layout in front matter. For example this layout will live at
110 `_layouts/post.html`:
111
112 {% raw %}
113 ```liquid
114 ---
115 layout: default
116 ---
117 <p>{{ page.date }} - Written by {{ page.author }}</p>
118
119 {{ content }}
120 ```
121 {% endraw %}
122
123 Now posts can use this layout while the rest of the pages use the default.
124
125 ## Variables
126
127 You can set front matter in layouts, the only difference is when you're
128 using in Liquid, you need to use the `layout` variable instead of `page`. For
129 example:
130
131 {% raw %}
132 ```liquid
133 ---
134 city: San Francisco
135 ---
136 <p>{{ layout.city }}</p>
137
138 {{ content }}
139 ```
140 {% endraw %}
0 ---
1 title: Liquid Filters
2 permalink: "/docs/liquid/filters/"
3 shopify_filter_url: https://shopify.github.io/liquid/filters/
4 shopify_filters:
5 - abs
6 - append
7 - at_least
8 - at_most
9 - capitalize
10 - ceil
11 - compact
12 - concat
13 - date
14 - default
15 - divided_by
16 - downcase
17 - escape
18 - escape_once
19 - first
20 - floor
21 - join
22 - last
23 - lstrip
24 - map
25 - minus
26 - modulo
27 - newline_to_br
28 - plus
29 - prepend
30 - remove
31 - remove_first
32 - replace
33 - replace_first
34 - reverse
35 - round
36 - rstrip
37 - size
38 - slice
39 - sort
40 - sort_natural
41 - split
42 - strip
43 - strip_html
44 - strip_newlines
45 - times
46 - truncate
47 - truncatewords
48 - uniq
49 - upcase
50 - url_decode
51 - url_encode
52 ---
53
54 All of the standard Liquid [filters](#standard-liquid-filters) are supported (see below).
55
56 To make common tasks easier, Jekyll even adds a few handy filters of its own,
57 all of which you can find on this page. You can also create your own filters
58 using [plugins](/docs/plugins/).
59
60 <div class="mobile-side-scroller">
61 <table>
62 <thead>
63 <tr>
64 <th>Description</th>
65 <th><span class="filter">Filter</span> and <span class="output">Output</span></th>
66 </tr>
67 </thead>
68 <tbody>
69 {% for filter in site.data.jekyll_filters %}
70 <tr>
71 <td>
72 <p id="{{ filter.name | slugify }}" class="name"><strong>{{ filter.name }}</strong></p>
73 <p>
74 {{- filter.description -}}
75 {%- if filter.version_badge %}
76 <span class="version-badge" title="This filter is available from version {{ filter.version_badge }}">
77 {{- filter.version_badge -}}
78 </span>
79 {% endif -%}
80 </p>
81 </td>
82 <td class="align-center">
83 {%- for example in filter.examples %}
84 <p><code class="filter">{{ example.input }}</code></p>
85 {% if example.output %}<p><code class="output">{{ example.output }}</code></p>{% endif %}
86 {% endfor -%}
87 </td>
88 </tr>
89 {% endfor %}
90 </tbody>
91 </table>
92 </div>
93
94 ### Options for the `slugify` filter
95
96 The `slugify` filter accepts an option, each specifying what to filter.
97 The default is `default`. They are as follows (with what they filter):
98
99 - `none`: no characters
100 - `raw`: spaces
101 - `default`: spaces and non-alphanumeric characters
102 - `pretty`: spaces and non-alphanumeric characters except for `._~!$&'()+,;=@`
103 - `ascii`: spaces, non-alphanumeric, and non-ASCII characters
104 - `latin`: like `default`, except Latin characters are first transliterated (e.g. `àèïòü` to `aeiou`) {%- include docs_version_badge.html version="3.7.0" -%}.
105
106 ### Detecting `nil` values with `where` filter {%- include docs_version_badge.html version="4.0" -%}
107
108 You can use the `where` filter to detect documents and pages with properties that are `nil` or `""`. For example,
109
110 {% raw %}
111 ```liquid
112 // Using `nil` to select posts that either do not have `my_prop`
113 // defined or `my_prop` has been set to `nil` explicitly.
114 {% assign filtered_posts = site.posts | where: 'my_prop', nil %}
115 ```
116 {% endraw %}
117
118 {% raw %}
119 ```liquid
120 // Using Liquid's special literal `empty` or `blank` to select
121 // posts that have `my_prop` set to an empty value.
122 {% assign filtered_posts = site.posts | where: 'my_prop', empty %}
123 ```
124 {% endraw %}
125
126 ### Binary operators in `where_exp` filter {%- include docs_version_badge.html version="4.0" -%}
127
128 You can use Liquid binary operators `or` and `and` in the expression passed to the `where_exp` filter to employ multiple
129 conditionals in the operation.
130
131 For example, to get a list of documents on English horror flicks, one could use the following snippet:
132
133 {% raw %}
134 ```liquid
135 {{ site.movies | where_exp: "item", "item.genre == 'horror' and item.language == 'English'" }}
136 ```
137 {% endraw %}
138
139 Or to get a list of comic-book based movies, one may use the following:
140
141 {% raw %}
142 ```liquid
143 {{ site.movies | where_exp: "item", "item.sub_genre == 'MCU' or item.sub_genre == 'DCEU'" }}
144 ```
145 {% endraw %}
146
147 ### Standard Liquid Filters
148
149 For your convenience, here is the list of all [Liquid filters]({{ page.shopify_filter_url }}) with links to examples in the official Liquid documentation.
150
151 {% for filter in page.shopify_filters %}
152 - [{{ filter }}]({{ filter | prepend: page.shopify_filter_url | append: '/' }})
153 {% endfor %}
0 ---
1 title: Tags Filters
2 permalink: "/docs/liquid/tags/"
3 ---
4 All of the standard Liquid
5 [tags](https://shopify.github.io/liquid/tags/control-flow/) are supported.
6 Jekyll has a few built in tags to help you build your site. You can also create
7 your own tags using [plugins]({{ '/docs/plugins/' | relative_url }}).
8
9 ## Includes
10
11 If you have page snippets that you use repeatedly across your site, an
12 [include]({{ '/docs/includes/' | relative_url }}) is the perfect way to make this more maintainable.
13
14 ## Code snippet highlighting
15
16 Jekyll has built in support for syntax highlighting of over 100 languages
17 thanks to [Rouge](http://rouge.jneen.net). Rouge is the default highlighter
18 in Jekyll 3 and above.
19
20 {: .note .warning}
21 Using Pygments has been deprecated and is not supported in
22 Jekyll 4; the configuration setting <code>highlighter: pygments</code>
23 now automatically falls back to using <em>Rouge</em> which is written in Ruby
24 and 100% compatible with stylesheets for Pygments.
25
26 To render a code block with syntax highlighting, surround your code as follows:
27
28 {% raw %}
29 ```liquid
30 {% highlight ruby %}
31 def foo
32 puts 'foo'
33 end
34 {% endhighlight %}
35 ```
36 {% endraw %}
37
38 The argument to the `highlight` tag (`ruby` in the example above) is the
39 language identifier. To find the appropriate identifier to use for the language
40 you want to highlight, look for the “short name” on the [Rouge
41 wiki](https://github.com/jayferd/rouge/wiki/List-of-supported-languages-and-lexers).
42
43 <div class="note">
44 <h5>Jekyll processes all Liquid filters in code blocks</h5>
45 <p>If you are using a language that contains curly braces, you
46 will likely need to place <code>{&#37; raw &#37;}</code> and
47 <code>{&#37; endraw &#37;}</code> tags around your code.
48 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>
49 </div>
50
51 ### Line numbers
52
53 There is a second argument to `highlight` called `linenos` that is optional.
54 Including the `linenos` argument will force the highlighted code to include line
55 numbers. For instance, the following code block would include line numbers next
56 to each line:
57
58 {% raw %}
59 ```liquid
60 {% highlight ruby linenos %}
61 def foo
62 puts 'foo'
63 end
64 {% endhighlight %}
65 ```
66 {% endraw %}
67
68 ### Stylesheets for syntax highlighting
69
70 In order for the highlighting to show up, you’ll need to include a highlighting
71 stylesheet. For Pygments or Rouge you can use a stylesheet for Pygments, you
72 can find an example gallery
73 [here](https://jwarby.github.io/jekyll-pygments-themes/languages/ruby.html)
74 or from [its repository](https://github.com/jwarby/jekyll-pygments-themes).
75
76 Copy the CSS file (`native.css` for example) into your css directory and import
77 the syntax highlighter styles into your `main.css`:
78
79 ```css
80 @import "native.css";
81 ```
82
83 ## Links
84
85 {: .note}
86 Since Jekyll {% include docs_version_badge.html version="4.0"%}, you don't need to prepend `link` and `post_url` tags with `site.baseurl`.
87
88 ### Linking to pages {#link}
89
90 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.
91
92 You must include the file's original extension when using the `link` tag. Here are some examples:
93
94 {% raw %}
95 ```liquid
96 {% link _collection/name-of-document.md %}
97 {% link _posts/2016-07-26-name-of-post.md %}
98 {% link news/index.html %}
99 {% link /assets/files/doc.pdf %}
100 ```
101 {% endraw %}
102
103 You can also use the `link` tag to create a link in Markdown as follows:
104
105 {% raw %}
106 ```liquid
107 [Link to a document]({% link _collection/name-of-document.md %})
108 [Link to a post]({% link _posts/2016-07-26-name-of-post.md %})
109 [Link to a page]({% link news/index.html %})
110 [Link to a file]({% link /assets/files/doc.pdf %})
111 ```
112 {% endraw %}
113
114 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.
115
116 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`.
117
118 If you're unsure of the path, add {% raw %}`{{ page.path }}`{% endraw %} to the page and it will display the path.
119
120 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).
121
122 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.
123
124 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:
125
126 ```yaml
127 ---
128 title: My page
129 my_variable: footer_company_a.html
130 ---
131 ```
132
133 You could then reference that variable in your link:
134
135 {% raw %}
136 ```liquid
137 {% link {{ page.my_variable }} %}
138 ```
139 {% endraw %}
140
141 In this example, the `link` tag would render a link to the file `footer_company_a.html`.
142
143 ### Linking to posts
144
145 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.
146
147 {% raw %}
148 ```liquid
149 {% post_url 2010-07-21-name-of-post %}
150 ```
151 {% endraw %}
152
153 If you organize your posts in subdirectories, you need to include subdirectory path to the post:
154
155 {% raw %}
156 ```liquid
157 {% post_url /subdir/2010-07-21-name-of-post %}
158 ```
159 {% endraw %}
160
161 There is no need to include the file extension when using the `post_url` tag.
162
163 You can also use this tag to create a link to a post in Markdown as follows:
164
165 {% raw %}
166 ```liquid
167 [Name of Link]({% post_url 2010-07-21-name-of-post %})
168 ```
169 {% endraw %}
0 ---
1 title: Liquid
2 permalink: /docs/liquid/
3 redirect_from: "/docs/templates/"
4 ---
5
6 Jekyll uses the [Liquid](https://shopify.github.io/liquid/) templating language
7 to process templates.
8
9 Generally in Liquid you output content using two curly braces e.g.
10 {% raw %}`{{ variable }}`{% endraw %} and perform logic statements by
11 surrounding them in a curly brace percentage sign e.g.
12 {% raw %}`{% if statement %}`{% endraw %}. To learn more about Liquid, check
13 out the [official Liquid Documentation](https://shopify.github.io/liquid/).
14
15 Jekyll provides a number of useful Liquid additions to help you build your site:
16
17 * [Filters]({{ '/docs/liquid/filters/' | relative_url }})
18 * [Tags]({{ '/docs/liquid/tags/' | relative_url }})
22 ---
33
44 **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.
5 {: .note .info }
5 {: .note .info}
66
77 ## Affinity teams & their captains
88
1616
1717 Just ask! Feel free to open an issue on `jekyll/jekyll` and add `/cc @jekyll/core`. We can add you. :smile:
1818
19 Alternatively, you can email or otherwise reach out to [@parkr](https://github.com/parkr) directly if you prefer the more private route.
19 Alternatively, you can email or otherwise reach out to [@oe](https://github.com/oe) directly if you prefer the more private route.
2020
2121 ## Ugh, I'm tired and don't have time to be a captain anymore. What now?
2222
23 No sweat at all! Email [@parkr](https://github.com/parkr) 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.
23 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.
2424
2525 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:
2626
22 ---
33
44 **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.
5 {: .note .info }
5 {: .note .info}
66
77 # 1. Use Jekyll
88
22 ---
33
44 **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.
5 {: .note .info }
5 {: .note .info}
66
77 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.
88
1212
1313 ## 2. Help Triage Issues
1414
15 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:
15 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:
1616
1717 ## 3. Write Documentation
1818
2424
2525 ## 5. Review Pull Requests
2626
27 Start by reviewing one pull request a week. Leave detailed comments and [follow our guide for reviewing pull requests](../reviewing-a-pull-request).
27 Start by reviewing one pull request a week. Leave detailed comments and [follow our guide for reviewing pull requests](../reviewing-a-pull-request/).
2828
2929 ## 6. Ask!
3030
33 ---
44
55 **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.
6 {: .note .info }
6 {: .note .info}
77
88 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.
99
1414 - [Avoiding burnout](avoiding-burnout/)
1515 - [Special Labels](special-labels/)
1616 - [Releasing a new version](releasing-a-new-version/)
17 - [Releasing a new version off `*-stable` branches](releasing-off-stable-branches/)
1718
1819 Interested in becoming a maintainer? Here is some documentation for **contributors**:
1920
22 ---
33
44 **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.
5 {: .note .info }
5 {: .note .info}
66
77 ## Code Review
88
9 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-b125a13aa292) 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.
9 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.
1010
11 **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.
11 **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.
1212
1313 ## Merging
1414
1919
2020 To merge a pull request, leave a comment thanking the contributor, then add the special merge request:
2121
22 ```text
22 ```
2323 Thank you very much for your contribution. Folks like you make this project and community strong. :heart:
2424
2525 @jekyllbot: merge +dev
11 title: "Releasing a new version"
22 ---
33
4 **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.
5 {: .note .info }
4 **This guide is for maintainers.** These special people have **write access** to one or more of Jekyll's repositories and help merge the
5 contributions of others. You may find what is written here interesting, but it's definitely not for everyone.
6 {: .note .info}
67
7 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 hestitate to contact the other maintainers if you feel unsure or don't know what to do next.
8 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
9 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
10 don't know what to do next.
811
912 ### Bump the version
1013
1114 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.
1215
13 ### Update the history document
16 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
17 `"major.minor.patch.suffix"`. `suffix` is not standardized and may be anything like `pre.alpha1`, `pre.rc2`, or simply `beta3`, etc.
1418
15 Replace the first header of the history document with a version milestone. This looks like the following:
19 To determine the correct version, consult the `## HEAD` section of our history document, `History.markdown`, first.
20
21 - If there's a subsection titled `Major Enhancements`
22 - Increment the `major` component of the version string and reset both `minor` and `patch` components to `0`.
23 - Add `suffix` if applicable.
24 - For example, `"3.9.1" => "4.0.0"` or, `"3.9.1 => "4.0.0.alpha1"`.
25 - Skip to next step in the release process.
26
27 - If there's a subsection titled `Minor Enhancements`
28 - Increment just the `minor` component and reset the patch component to `0`.
29 - Add `suffix` if applicable.
30 - For example, `"4.0.2" => "4.1.0"` or `"4.1.0" => "4.2.0.pre"`.
31 - Skip to next step in the release process.
32
33 - For anything else, increment just the `patch` component or `suffix` component as applicable. For example, `"4.0.2" => "4.0.3"` or
34 `"4.1.0.beta3" => "4.1.0.rc"`.
35
36 ### Write a release post
37
38 In case this wasn't done already, you can generate a new release post scaffold using the included `rake` command:
39
40 ```sh
41 bundle exec rake site:releases:new[3.8.0]
42 ```
43
44 where `3.8.0` should be replaced with the new version.
45
46 Then, write the post. Be sure to thank all of the collaborators and maintainers who have contributed since the last release. You can generate
47 a log of their names using the following command:
48
49 ```sh
50 git shortlog -sn master...v3.7.2
51 ```
52
53 where `v3.7.2` is the git tag for the previous release. In case the tag doesn't exist in your repository, run:
54
55 ```sh
56 git pull
57 ```
58
59 Be sure to open a pull request for your release post once its finished.
60
61 ### Update the History document
62
63 Replace the first header of `History.markdown` with a version milestone. This looks like the following:
1664
1765 ```diff
18 -## HEAD
19 +## 3.7.1 / 2018-01-25
66 - ## HEAD
67 + ## 3.7.1 / 2018-01-25
2068 ```
2169
22 Adjust the version number and the date. The `## HEAD` heading will be regenerated next time a pull request is merged.
70 Adjust the version number and the date. The `## HEAD` heading will be regenerated the next time a pull request is merged.
71
72 Rearrange the subsections (as a whole) based on decreasing priorities as illustrated below:
73
74 ```
75 ## 4.2.0 / 2020-12-14
76
77 ### Major Enhancements
78
79 ...
80
81 ### Minor Enhancements
82
83 ...
84
85 ### Bug Fixes
86
87 ...
88
89 ### Security Fixes
90
91 ...
92
93 ### Optimization Fixes
94
95 ...
96
97 ### Development Fixes
98
99 ...
100
101 ### Site Enhancements
102
103 ...
104 ```
23105
24106 Once you've done this, update the website by running the following command:
25107
29111
30112 This updates the website's changelog, and pushes the versions in various other places.
31113
32 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.
33
34 ## Write a release post
35
36 In case this isn't done already, you can generate a new release post using the included `rake` command:
37
38 ```sh
39 bundle exec rake site:releases:new[3.8.0]
40 ```
41
42 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:
43
44 ```sh
45 git shortlog -sn master...v3.7.2
46 ```
47
48 where, again `v3.7.2` is the last release. Be sure to open a pull request for your release post.
114 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
115 to fix those manually, and after you're done generating the website changelog, commit your changes.
49116
50117 ### Push the version
51118
52119 Before you do this step, make sure the following things are done:
53120
54 - You have permission to push a new gem version to RubyGems
55 - You're logged into RubyGems on your command line
56 - A release post has been prepared, and is ideally already live
57 - All of the prior steps are done, committed, and pushed to `master`
121 - A release post has been prepared, and is ideally already live via a prior pull request.
122 - All of the prior steps are done, especially the change to `lib/jekyll/version.rb` has been staged for commit.
123 - Commit staged changes to the local `master` branch preferably with commit message `"Release :gem: v[CURRENT_VERSION]"`.
58124
59 Really the only thing left to do is to run this command:
125 The only thing left to do now is to run this command:
60126
61127 ```sh
62 bundle exec rake release
128 git push upstream master
63129 ```
64130
65 This will automatically build the new gem, make a release commit and tag and then push the new gem to RubyGems. Don't worry about creating a GitHub release, @jekyllbot should take care of that.
131 where `upstream` references `git@github.com:jekyll/jekyll.git`.
66132
67 And then, you're done! :tada: Feel free to celebrate!
133 This will trigger a GitHub Actions workflow that will automatically build the new gem, tag the release commit, push the tag to GitHub and
134 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
135 release workflow publishes the new tag.
68136
69 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.
137 And then, if the workflow has completed successfully, you're done! :tada:
138 Feel free to celebrate!
139
140 If you have access to the [@jekyllrb](https://twitter.com/jekyllrb) Twitter account, you should tweet the release post from there. If not, just
141 ask another maintainer to do it or to give you access.
142
143 ### Build the docs
144
145 We package our documentation as a :gem: Gem for offline use.
146
147 This is done with the [**jekyll-docs**](https://github.com/jekyll/jekyll-docs#building) repository, and more detailed instructions are
148 provided there.
70149
71150 ## For non-core gems
72151
73 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:
152 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
153 this:
74154
75155 - Bump the gem version manually, usually in `lib/<plugin_name>/version.rb`
76156 - Adjust the history file
77 - Run `bundle exec rake release` or `script/release`, depending on which of the two exists
157 - Commit changes to default branch preferably with message `"Release :gem: v[CURRENT_VERSION]"`
158 - Push to remote repository
78159 - Rejoice
79160
80161 Be sure to ask your project's maintainers if you're unsure!
0 ---
1 title: Releasing off older stable branches
2 ---
3
4 Apart from having releases cut from the default `master` branch, Jekyll Core may occasionally cut releases containing security patches and
5 critical bug-fixes for older versions under maintenance. Such releases are cut from specially named branches, following the pattern
6 `[x].[y]-stable` where `[x]` denotes semver-major-version and `[y]`, the semver-minor-version. For example, the branch `3.9-stable` refers to
7 commits released as part of `jekyll-3.9.x` series.
8
9 Co-ordinating a release off a `*-stable` branch is complicated mainly because the default branch has to inevitably reflect the release as well.
10
11 ## Requirements
12
13 - The maintainer has to have **write-access** to both the concerned `*-stable` and `master` branches.
14 - The maintainer needs to complete the task using their **local CLI program** instead of dispatching via GitHub Web UI.
15 - The maintainer is abreast with the workflow to [release off `master`]({{ 'docs/maintaining/releasing-a-new-version/' | relative_url }}). The
16 procedure documented in the following section is an abridged adaptation of the workflow for `master`.
17 - A release post has been drafted and **is awaiting publish to `master`** via an approved pull request.
18 - Stable internet connection.
19
20 ## Trigger release workflow
21
22 1. Ensure that you've **checked out the concerned `*-stable` branch** and is up-to-date with its counterpart at `jekyll/jekyll` at GitHub.
23 2. Bump the `VERSION` string in `lib/jekyll/version.rb`.
24 3. Update the **History document** as documented [here]({{ 'docs/maintaining/releasing-a-new-version/#update-the-history-document' | relative_url }}).<br/>
25 (**IMPORTANT: Do not run `rake site:generate` on the stable branch though**).
26 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
27 temporary snippet at a future stage.
28 5. Commit changes to the version-file and History document with commit message `Release :gem: v[CURRENT_VERSION]`.
29 6. Push commit to upstream remote `jekyll/jekyll` at GitHub.
30
31 ## Publish release post
32
33 1. Ensure the `Release Gem` workflow has completed successfully.
34 2. Merge release-post pull request to `master`.
35
36 ## Update default branch to reflect release off the stable branch
37
38 1. Locally, check out `master` and ensure it is up-to-date with its remote counterpart at `jekyll/jekyll` at GitHub.
39 2. Update History document using the snippet in the temporary tab / window created earlier. The various sections in the History document are
40 primarily in reverse chronological order and secondarily scoped to the semver-major-version. For example, a release section for `v3.9.2`
41 will be listed above the section for `v3.9.1` but under release sections for v4.x.
42 The snippet stashed earlier has to be injected into the correct location manually.
43 3. Optionally, update `VERSION` string in `lib/jekyll/version.rb`. (*If existing version is lesser than latest version*).
44 4. Now **run `rake site:generate`** to update various meta files:
45 - docs/_config.yml
46 - docs/_docs/history.md
47 - docs/latest_version.txt
48 5. Commit changes to various meta files with commit message `Release :gem: v[CURRENT_VERSION]`.
49 6. Push commit to upstream remote.
50
51 ## Publish GitHub Release
52
53 Unlike releases cut off the `master` branch, our JekyllBot does not automatically create and publish a GitHub Release for tags created from
54 *non-default* branches. Therefore, the maintainer has to **manually create and publish** the concerned GitHub Release.
55 1. Choose the newly pushed tag.
56 2. Title is same as the name of the selected tag.
57 3. The release snippet stashed previously forms the body.
58 4. Delete the snippet's title (`## x.y.z / YYYY-MM-DD`) from the release body.
59 5. Publish.
60
61 Note: The GitHub Release may optionally be *drafted* prior to updating the default branch and then *published* immediately after pushing the
62 update commit to the default branch to streamline the procedure.
22 ---
33
44 **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.
5 {: .note .info }
5 {: .note .info}
66
77 ## Respond Kindly
88
22 ---
33
44 **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.
5 {: .note .info }
5 {: .note .info}
66
77 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:
88
22 ---
33
44 **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.
5 {: .note .info }
5 {: .note .info}
66
77 Before evaluating an issue, it is important to identify if it is a feature
88 request or a bug. For the Jekyll project the following definitions are used
5050
5151 ### Staleness and automatic closure
5252
53 @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 behaviour can be suppressed by setting the [`pinned` label](../maintaining/special-labels.md/#pinned).
53 @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).
0 ---
1 title: Markdown 101
2 permalink: /docs/markdown-101/
3 ---
4
5 # TO WRITE
00 ---
1 title: Blog migrations
1 title: Blog Migrations
22 permalink: /docs/migrations/
33 ---
44
00 ---
1 title: Creating pages
1 title: Pages
22 permalink: /docs/pages/
33 ---
44
5 In addition to [writing posts](../posts/), you might also want to add static pages (content that isn't date-based) to your Jekyll site. By taking advantage of the way Jekyll copies files and directories, this is easy to do.
5 Pages are the most basic building block for content. They're useful for standalone
6 content (content which is not date based or is not a group of content such as staff
7 members or recipes).
68
7 ## Homepage
8
9 Just about every web server configuration you come across will look for an HTML
10 file called `index.html` (by convention) in the site's root folder and display
11 that as the homepage. Unless the web server you’re using is configured to look
12 for some different filename as the default, this file will turn into the
13 homepage of your Jekyll-generated site.
14
15 <div class="note">
16 <h5>ProTip™: Use layouts on your homepage</h5>
17 <p>
18 Any HTML file on your site can use layouts and/or includes, even the
19 homepage. Common content, like headers and footers, make excellent
20 candidates for extraction into a layout.
21 </p>
22 </div>
23
24 ## Where additional pages live
25
26 Where you put HTML or [Markdown](https://daringfireball.net/projects/markdown/)
27 files for pages depends on how you want the pages to work. There are two main ways of creating pages:
28
29 - Place named HTML or [Markdown](https://daringfireball.net/projects/markdown/)
30 files for each page in your site's root folder.
31 - Place pages inside folders and subfolders named whatever you want.
32
33 Both methods work fine (and can be used in conjunction with each other),
34 with the only real difference being the resulting URLs. By default, pages retain the same folder structure in `_site` as they do in the source directory.
35
36 ### Named HTML files
37
38 The simplest way of adding a page is just to add an HTML file in the root
39 directory with a suitable name for the page you want to create. For a site with
9 The simplest way of adding a page is to add an HTML file in the root
10 directory with a suitable filename. You can also write a page in Markdown using
11 a `.md` extension which converts to HTML on build. For a site with
4012 a homepage, an about page, and a contact page, here’s what the root directory
4113 and associated URLs might look like:
4214
43 ```sh
15 ```
4416 .
45 |-- _config.yml
46 |-- _includes/
47 |-- _layouts/
48 |-- _posts/
49 |-- _site/
50 |-- about.html # => http://example.com/about.html
51 |-- index.html # => http://example.com/
52 |-- other.md # => http://example.com/other.html
17 ├── about.md # => http://example.com/about.html
18 ├── index.html # => http://example.com/
5319 └── contact.html # => http://example.com/contact.html
5420 ```
5521
56 If you have a lot of pages, you can organize those pages into subfolders. The same subfolders that are used to group your pages in our project's source will exist in the `_site` folder when your site builds.
22 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.
5723
58 ## Flattening pages from subfolders into the root directory
59
60 If you have pages organized into subfolders in your source folder and want to flatten them in the root folder on build, you must add the [permalink]({% link _docs/permalinks.md %}) property directly in your page's front matter like this:
61
62 ```yaml
63 ---
64 title: My page
65 permalink: mypageurl.html
66 ---
24 ```
25 .
26 ├── about.md # => http://example.com/about.html
27 ├── documentation # folder containing pages
28 │ └── doc1.md # => http://example.com/documentation/doc1.html
29 ├── design # folder containing pages
30 │ └── draft.md # => http://example.com/design/draft.html
6731 ```
6832
69 ### Named folders containing index HTML files
33 ## Changing the output URL
7034
71 If you don't want file extensions (`.html`) to appear in your page URLs (file extensions are the default), you can choose a [permalink style](../permalinks/#builtinpermalinkstyles) that has a trailing slash instead of a file extension.
35 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.
7236
73 Note if you want to view your site offline *without the Jekyll preview server*, your browser will need the file extension to display the page, and all assets will need to be relative links that function without the server baseurl.
37 ## Excerpts for pages
38
39 From Jekyll 4.1.1 onwards, one can *choose* to generate excerpts for their pages by setting `page_excerpts` to `true` in their
40 config file.
1313 <div class="note info">
1414 <h5>Pagination only works within HTML files</h5>
1515 <p>
16 Pagination does not work from within Markdown or Textile files from
16 Pagination does not work from within Markdown files from
1717 your Jekyll site. Pagination works when called from within the HTML
1818 file, named <code>index.html</code>, which optionally may reside in and
1919 produce pagination from within a subdirectory, via the
4141
4242 This will read in `blog/index.html`, send it each pagination page in Liquid as
4343 `paginator` and write the output to `blog/page:num/`, where `:num` is the
44 pagination page number, starting with `2`. If a site has 12 posts and specifies
45 `paginate: 5`, Jekyll will write `blog/index.html` with the first 5 posts, `blog/page2/index.html` with the next 5 posts
46 and `blog/page3/index.html` with the last 2 posts into the destination
47 directory.
44 pagination page number, starting with `2`. <br/>
45 If a site has 12 posts and specifies `paginate: 5`, Jekyll will write `blog/index.html`
46 with the first 5 posts, `blog/page2/index.html` with the next 5 posts and
47 `blog/page3/index.html` with the last 2 posts into the destination directory.
4848
4949 <div class="note warning">
5050 <h5>Don't set a permalink</h5>
5757 <div class="note info">
5858 <h5>Pagination for categories, tags and collections</h5>
5959 <p>
60 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.
61 <strong>This plugin is not supported by GitHub Pages</strong>.
60 The more recent <a href="https://github.com/sverrirs/jekyll-paginate-v2">
61 jekyll-paginate-v2</a> plugin supports more features. See the
62 <a href="https://github.com/sverrirs/jekyll-paginate-v2/tree/master/examples">
63 pagination examples</a> in the repository. <strong>This plugin is not
64 supported by GitHub Pages</strong>.
6265 </p>
6366 </div>
6467
6770 The pagination plugin exposes the `paginator` liquid object with the following
6871 attributes:
6972
70 <div class="mobile-side-scroller">
71 <table>
72 <thead>
73 <tr>
74 <th>Attribute</th>
75 <th>Description</th>
76 </tr>
77 </thead>
78 <tbody>
79 <tr>
80 <td><p><code>page</code></p></td>
81 <td><p>current page number</p></td>
82 </tr>
83 <tr>
84 <td><p><code>per_page</code></p></td>
85 <td><p>number of posts per page</p></td>
86 </tr>
87 <tr>
88 <td><p><code>posts</code></p></td>
89 <td><p>a list of posts for the current page</p></td>
90 </tr>
91 <tr>
92 <td><p><code>total_posts</code></p></td>
93 <td><p>total number of posts in the site</p></td>
94 </tr>
95 <tr>
96 <td><p><code>total_pages</code></p></td>
97 <td><p>number of pagination pages</p></td>
98 </tr>
99 <tr>
100 <td><p><code>previous_page</code></p></td>
101 <td>
102 <p>
103 page number of the previous pagination page,
104 or <code>nil</code> if no previous page exists
105 </p>
106 </td>
107 </tr>
108 <tr>
109 <td><p><code>previous_page_path</code></p></td>
110 <td>
111 <p>
112 path of previous pagination page,
113 or <code>nil</code> if no previous page exists
114 </p>
115 </td>
116 </tr>
117 <tr>
118 <td><p><code>next_page</code></p></td>
119 <td>
120 <p>
121 page number of the next pagination page,
122 or <code>nil</code> if no subsequent page exists
123 </p>
124 </td>
125 </tr>
126 <tr>
127 <td><p><code>next_page_path</code></p></td>
128 <td>
129 <p>
130 path of next pagination page,
131 or <code>nil</code> if no subsequent page exists
132 </p>
133 </td>
134 </tr>
135 </tbody>
136 </table>
137 </div>
73 {% include docs_variables_table.html scope=site.data.jekyll_variables.paginator %}
13874
13975 <div class="note info">
14076 <h5>Pagination does not support tags or categories</h5>
14177 <p>Pagination pages through every post in the <code>posts</code>
142 variable unless a post has <code>hidden: true</code> in its YAML Front Matter.
78 variable unless a post has <code>hidden: true</code> in its front matter.
14379 It does not currently allow paging over groups of posts linked
14480 by a common tag or category. It cannot include any collection of
14581 documents because it is restricted to posts.</p>
173109 <!-- Pagination links -->
174110 <div class="pagination">
175111 {% if paginator.previous_page %}
176 <a href="{{ paginator.previous_page_path }}" class="previous">Previous</a>
112 <a href="{{ paginator.previous_page_path }}" class="previous">
113 Previous
114 </a>
177115 {% else %}
178116 <span class="previous">Previous</span>
179117 {% endif %}
180 <span class="page_number ">Page: {{ paginator.page }} of {{ paginator.total_pages }}</span>
118 <span class="page_number ">
119 Page: {{ paginator.page }} of {{ paginator.total_pages }}
120 </span>
181121 {% if paginator.next_page %}
182122 <a href="{{ paginator.next_page_path }}" class="next">Next</a>
183123 {% else %}
204144 {% if paginator.total_pages > 1 %}
205145 <div class="pagination">
206146 {% if paginator.previous_page %}
207 <a href="{{ paginator.previous_page_path | prepend: site.baseurl | replace: '//', '/' }}">&laquo; Prev</a>
147 <a href="{{ paginator.previous_page_path | relative_url }}">&laquo; Prev</a>
208148 {% else %}
209149 <span>&laquo; Prev</span>
210150 {% endif %}
213153 {% if page == paginator.page %}
214154 <em>{{ page }}</em>
215155 {% elsif page == 1 %}
216 <a href="{{ paginator.previous_page_path | prepend: site.baseurl | replace: '//', '/' }}">{{ page }}</a>
156 <a href="{{ '/' | relative_url }}">{{ page }}</a>
217157 {% else %}
218 <a href="{{ site.paginate_path | prepend: site.baseurl | replace: '//', '/' | replace: ':num', page }}">{{ page }}</a>
158 <a href="{{ site.paginate_path | relative_url | replace: ':num', page }}">{{ page }}</a>
219159 {% endif %}
220160 {% endfor %}
221161
222162 {% if paginator.next_page %}
223 <a href="{{ paginator.next_page_path | prepend: site.baseurl | replace: '//', '/' }}">Next &raquo;</a>
163 <a href="{{ paginator.next_page_path | relative_url }}">Next &raquo;</a>
224164 {% else %}
225165 <span>Next &raquo;</span>
226166 {% endif %}
22 permalink: /docs/permalinks/
33 ---
44
5 Permalinks refer to the URLs (excluding the domain name or directory folder) for your pages, posts, or collections.
6 Jekyll supports a flexible way to build permalinks, allowing you to leverage various template variables or choose built-in permalink styles (such as `date`) that automatically use a template-variable pattern.
7
8 You construct permalinks by creating a template URL where dynamic elements are represented by colon-prefixed keywords. The default template permalink is `/:categories/:year/:month/:day/:title:output_ext`. Each of the colon-prefixed keywords is a template variable.
9
10 ## Where to configure permalinks
11
12 You can configure your site's permalinks through the [Configuration]({% link _docs/configuration.md %}) file or in the [Front Matter]({% link _docs/frontmatter.md %}) for each post, page, or collection.
13
14 Setting permalink styles in your configuration file applies the setting globally in your project. You configure permalinks in your `_config.yml` file like this:
5 Permalinks are the output path for your pages, posts, or collections. They
6 allow you to structure the directories of your source code different from the
7 directories in your output.
8
9 ## Front Matter
10
11 The simplest way to set a permalink is using front matter. You set the
12 `permalink` variable in front matter to the output path you'd like.
13
14 For example, you might have a page on your site located at
15 `/my_pages/about-me.html` and you want the output url to be `/about/`. In
16 front matter of the page you would set:
17
18 ```yaml
19 ---
20 permalink: /about/
21 ---
22 ```
23
24 ## Global
25
26 Setting a permalink in front matter for every page on your site is no fun.
27 Luckily, Jekyll lets you set the permalink structure globally in your `_config.yml`.
28
29 To set a global permalink, you use the `permalink` variable in `_config.yml`.
30 You can use placeholders to your desired output. For example:
1531
1632 ```yaml
1733 permalink: /:categories/:year/:month/:day/:title:output_ext
1834 ```
1935
20 If you don't specify any permalink setting, Jekyll uses the above pattern as the default.
21
22 The permalink can also be set using a built-in permalink style:
23
24 ```yaml
25 permalink: date
26 ```
27
28 `date` is the same as `:categories/:year/:month/:day/:title:output_ext`, the default. See [Built-in Permalink Styles](#builtinpermalinkstyles) below for more options.
29
30 Setting the permalink in your post, page, or collection's front matter overrides any global settings. Here's an example:
31
32 ```yaml
33 ---
34 title: My page title
35 permalink: /mypageurl/
36 ---
37 ```
38
39 Even if your configuration file specifies the `date` style, the URL for this page would be `http://somedomain.com/mypageurl/`.
40
41 When you use permalinks that omit the `.html` file extension (called "pretty URLs") Jekyll builds the file as index.html placed inside a folder with the page's name. For example:
42
43 ```
44 ├── mypageurl
45 │   └── index.html
46 ```
47
48 With a URL such as `/mypageurl/`, servers automatically load the index.html file inside the folder, so users can simply navigate to `http://somedomain.com/mypageurl/` to get to `mypageurl/index.html`.
49
50 ## Template variables for permalinks {#template-variables}
51
52 The following table lists the template variables available for permalinks. You can use these variables in the `permalink` property in your config file.
36 Note that pages and collections (excluding `posts` and `drafts`) don't have time
37 and categories (for pages, the above `:title` is equivalent to `:basename`), these
38 aspects of the permalink style are ignored for the output.
39
40 For example, a permalink style of
41 `/:categories/:year/:month/:day/:title:output_ext` for the `posts` collection becomes
42 `/:title.html` for pages and collections (excluding `posts` and `drafts`).
43
44 ### Placeholders
45
46 Here's the full list of placeholders available:
5347
5448 <div class="mobile-side-scroller">
5549 <table>
6660 </td>
6761 <td>
6862 <p>
69 Year from the post's filename. May be overridden via the document’s
70 <code>date</code> YAML front matter
63 Year from the post’s filename with four digits.
64 May be overridden via the document’s <code>date</code> front matter.
65 </p>
66 </td>
67 </tr>
68 <tr>
69 <td>
70 <p><code>short_year</code></p>
71 </td>
72 <td>
73 <p>
74 Year from the post’s filename without the century. (00..99)
75 May be overridden via the document’s <code>date</code> front matter.
7176 </p>
7277 </td>
7378 </tr>
7782 </td>
7883 <td>
7984 <p>
80 Month from the post's filename. May be overridden via the document’s
81 <code>date</code> YAML front matter
85 Month from the post’s filename. (01..12)
86 May be overridden via the document’s <code>date</code> front matter.
8287 </p>
8388 </td>
8489 </tr>
8893 </td>
8994 <td>
9095 <p>
91 Month without leading zeros from the post's filename. May be
92 overridden via the document’s <code>date</code> YAML front matter
93 </p>
96 Month without leading zeros from the post’s filename. May be
97 overridden via the document’s <code>date</code> front matter.
98 </p>
99 </td>
100 </tr>
101 <tr>
102 <td>
103 <p><code>short_month</code></p>
104 </td>
105 <td>
106 <p>Three-letter month abbreviation, e.g. “Jan”.</p>
107 </td>
108 </tr>
109 <tr>
110 <td>
111 <p><code>long_month</code></p>
112 <small>{% include docs_version_badge.html version="4.0" %}</small>
113 </td>
114 <td>
115 <p>Full month name, e.g. “January”.</p>
94116 </td>
95117 </tr>
96118 <tr>
99121 </td>
100122 <td>
101123 <p>
102 Day from the post's filename. May be overridden via the document’s
103 <code>date</code> YAML front matter
124 Day of the month from the post’s filename. (01..31)
125 May be overridden via the document’s <code>date</code> front matter.
104126 </p>
105127 </td>
106128 </tr>
110132 </td>
111133 <td>
112134 <p>
113 Day without leading zeros from the post's filename. May be overridden
114 via the document’s <code>date</code> YAML front matter
135 Day of the month without leading zeros from the post’s filename.
136 May be overridden via the document’s <code>date</code> front matter.
115137 </p>
116138 </td>
117139 </tr>
118140 <tr>
119141 <td>
120142 <p><code>y_day</code></p>
121 </td>_
122 <td>
123 <p>Day of the year from the post's filename, with leading zeros.</p>
124 </td>
125 </tr>
126 <tr>
127 <td>
128 <p><code>short_year</code></p>
129 </td>
130 <td>
131 <p>
132 Year without the century from the post's filename. May be overridden
133 via the document’s <code>date</code> YAML front matter
134 </p>
143 </td>
144 <td>
145 <p>Ordinal day of the year from the post’s filename, with leading zeros. (001..366)</p>
146 </td>
147 </tr>
148 <tr>
149 <td>
150 <p><code>w_year</code></p>
151 <small>{% include docs_version_badge.html version="4.0" %}</small>
152 </td>
153 <td>
154 <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>
155 </td>
156 </tr>
157 <tr>
158 <td>
159 <p><code>week</code></p>
160 <small>{% include docs_version_badge.html version="4.0" %}</small>
161 </td>
162 <td>
163 <p>Week number of the current year, starting with the first week having a majority of its days in January. (01..53)</p>
164 </td>
165 </tr>
166 <tr>
167 <td>
168 <p><code>w_day</code></p>
169 <small>{% include docs_version_badge.html version="4.0" %}</small>
170 </td>
171 <td>
172 <p>Day of the week, starting with Monday. (1..7)</p>
173 </td>
174 </tr>
175 <tr>
176 <td>
177 <p><code>short_day</code></p>
178 <small>{% include docs_version_badge.html version="4.0" %}</small>
179 </td>
180 <td>
181 <p>Three-letter weekday abbreviation, e.g. “Sun”.</p>
182 </td>
183 </tr>
184 <tr>
185 <td>
186 <p><code>long_day</code></p>
187 <small>{% include docs_version_badge.html version="4.0" %}</small>
188 </td>
189 <td>
190 <p>Weekday name, e.g. “Sunday”.</p>
135191 </td>
136192 </tr>
137193 <tr>
140196 </td>
141197 <td>
142198 <p>
143 Hour of the day, 24-hour clock, zero-padded from the post's
199 Hour of the day, 24-hour clock, zero-padded from the post’s
144200 <code>date</code> front matter. (00..23)
145201 </p>
146202 </td>
151207 </td>
152208 <td>
153209 <p>
154 Minute of the hour from the post's <code>date</code> front matter. (00..59)
210 Minute of the hour from the post’s <code>date</code> front matter. (00..59)
155211 </p>
156212 </td>
157213 </tr>
161217 </td>
162218 <td>
163219 <p>
164 Second of the minute from the post's <code>date</code> front matter. (00..59)
220 Second of the minute from the post’s <code>date</code> front matter. (00..59)
165221 </p>
166222 </td>
167223 </tr>
172228 <td>
173229 <p>
174230 Title from the document’s filename. May be overridden via
175 the document’s <code>slug</code> YAML front matter.
231 the document’s <code>slug</code> front matter. Preserves case from the source.
176232 </p>
177233 </td>
178234 </tr>
184240 <p>
185241 Slugified title from the document’s filename (any character
186242 except numbers and letters is replaced as hyphen). May be
187 overridden via the document’s <code>slug</code> YAML front matter.
243 overridden via the document’s <code>slug</code> front matter.
188244 </p>
189245 </td>
190246 </tr>
201257 </p>
202258 </td>
203259 </tr>
260 <tr>
261 <td>
262 <p><code>slugified_categories</code></p>
263 <small>{% include docs_version_badge.html version="4.1" %}</small>
264 </td>
265 <td>
266 <p>
267 The specified categories for this post but <em>slugified</em>. If a category is a
268 composite of multiple words, Jekyll will downcase all alphabets and replace any
269 non-alphanumeric character with a hyphen. (e.g. <code>"Work 2 Progress"</code>
270 will be converted into <code>"work-2-progress"</code>)
271 </p>
272 <p>
273 If a post has multiple categories, Jekyll will create a hierarchy
274 (e.g. <code>/work-2-progress/category2</code>).
275 Also Jekyll automatically parses out double slashes in the URLs,
276 so if no categories are present, it will ignore this.
277 </p>
278 </td>
279 </tr>
280 <tr>
281 <td>
282 <p><code>:output_ext</code></p>
283 </td>
284 <td>
285 <p>Extension of the output file. (Included by default and usually unnecessary.)</p>
286 </td>
287 </tr>
204288 </tbody>
205289 </table>
206290 </div>
207291
208 Note that all template variables relating to time or categories are available to posts only.
209
210 ## Built-in permalink styles {#builtinpermalinkstyles}
211
212 Although you can specify a custom permalink pattern using [template variables](#template-variables), Jekyll also provides the following built-in styles for convenience.
292 ### Built-in formats
293
294 For posts, Jekyll also provides the following built-in styles for convenience:
213295
214296 <div class="mobile-side-scroller">
215297 <table>
246328 </tr>
247329 <tr>
248330 <td>
331 <p><code>weekdate</code></p>
332 <small>{% include docs_version_badge.html version="4.0" %}</small>
333 </td>
334 <td>
335 <p>
336 <code>/:categories/:year/W:week/:short_day/:title:output_ext</code><br/>
337 <small>(<code>W</code> will be prefixed to the value of <code>:week</code>)</small>
338 </p>
339 </td>
340 </tr>
341 <tr>
342 <td>
249343 <p><code>none</code></p>
250344 </td>
251345 <td>
259353 Rather than typing `permalink: /:categories/:year/:month/:day/:title/`, you can just type `permalink: pretty`.
260354
261355 <div class="note info">
262 <h5>Specifying permalinks through the YAML Front Matter</h5>
263 <p>Built-in permalink styles are not recognized in YAML Front Matter. As a result, <code>permalink: pretty</code> will not work.</p>
356 <h5>Specifying permalinks through the front matter</h5>
357 <p>Built-in permalink styles are not recognized in front matter. As a result, <code>permalink: pretty</code> will not work.</p>
264358 </div>
265359
266 ## Permalink style examples with posts {#permalink-style-examples}
267
268 Here are a few examples to clarify how permalink styles get applied with posts.
269
270 Given a post named: `/2009-04-29-slap-chop.md`
360 ### Collections
361
362 For collections (including `posts` and `drafts`), you have the option to override
363 the global permalink in the collection configuration in `_config.yml`:
364
365 ```yaml
366 collections:
367 my_collection:
368 output: true
369 permalink: /:collection/:name
370 ```
371
372 Collections have the following placeholders available:
271373
272374 <div class="mobile-side-scroller">
273375 <table>
274376 <thead>
275377 <tr>
276 <th>URL Template</th>
277 <th>Resulting Permalink URL</th>
378 <th>Variable</th>
379 <th>Description</th>
278380 </tr>
279381 </thead>
280382 <tbody>
281383 <tr>
282384 <td>
283 <p>None specified, or <code>permalink: date</code></p>
284 </td>
285 <td>
286 <p><code>/2009/04/29/slap-chop.html</code></p>
287 </td>
288 </tr>
289 <tr>
290 <td>
291 <p><code>pretty</code></p>
292 </td>
293 <td>
294 <p><code>/2009/04/29/slap-chop/</code></p>
295 </td>
296 </tr>
297 <tr>
298 <td>
299 <p><code>/:month-:day-:year/:title:output_ext</code></p>
300 </td>
301 <td>
302 <p><code>/04-29-2009/slap-chop.html</code></p>
303 </td>
304 </tr>
305 <tr>
306 <td>
307 <p><code>/blog/:year/:month/:day/:title/</code></p>
308 </td>
309 <td>
310 <p><code>/blog/2009/04/29/slap-chop/</code></p>
311 </td>
312 </tr>
313 <tr>
314 <td>
315 <p><code>/:year/:month/:title</code></p>
316 <p>See <a href="#extensionless-permalinks">Extensionless permalinks with no trailing slashes</a> for details.</p>
317 </td>
318 <td>
319 <p><code>/2009/04/slap-chop</code></p>
385 <p><code>:collection</code></p>
386 </td>
387 <td>
388 <p>Label of the containing collection.</p>
389 </td>
390 </tr>
391 <tr>
392 <td>
393 <p><code>:path</code></p>
394 </td>
395 <td>
396 <p>
397 Path to the document relative to the collection's directory,
398 including base filename of the document.
399 </p>
400 </td>
401 </tr>
402 <tr>
403 <td>
404 <p><code>:name</code></p>
405 </td>
406 <td>
407 <p>The document's base filename, with every sequence of spaces
408 and non-alphanumeric characters replaced by a hyphen.</p>
409 </td>
410 </tr>
411 <tr>
412 <td>
413 <p><code>:title</code></p>
414 </td>
415 <td>
416 <p>
417 The <code>:title</code> template variable will take the
418 <code>slug</code> <a href="/docs/front-matter/">front matter</a>
419 variable value if any is present in the document; if none is
420 defined then <code>:title</code> will be equivalent to
421 <code>:name</code>, aka the slug generated from the filename.
422 Preserves case from the source.
423 </p>
424 </td>
425 </tr>
426 <tr>
427 <td>
428 <p><code>:output_ext</code></p>
429 </td>
430 <td>
431 <p>Extension of the output file. (Included by default and usually unnecessary.)</p>
320432 </td>
321433 </tr>
322434 </tbody>
323435 </table>
324436 </div>
325437
326 ## Permalink settings for pages and collections {#pages-and-collections}
327
328 The permalink setting in your configuration file specifies the permalink style used for posts, pages, and collections. However, because pages and collections don't have time or categories, these aspects of the permalink style are ignored with pages and collections.
329
330 For example:
331
332 * A permalink style of `/:categories/:year/:month/:day/:title:output_ext` for posts becomes `/:title.html` for pages and collections.
333 * A permalink style of `pretty` (or `/:categories/:year/:month/:day/:title/`), which omits the file extension and contains a trailing slash, will update page and collection permalinks to also omit the file extension and contain a trailing slash: `/:title/`.
334 * A permalink style of `date`, which contains a trailing file extension, will update page permalinks to also contain a trailing file extension: `/:title.html`. But no time or category information will be included.
335
336 ## Permalinks and default paths
337
338 The path to the post or page in the built site differs for posts, pages, and collections:
339
340 ### Posts
341
342 The subfolders into which you may have organized your posts inside the `_posts` directory will not be part of the permalink.
343
344 If you use a permalink style that omits the `.html` file extension, each post is rendered as an `index.html` file inside a folder with the post's name (for example, `categoryname/2016/12/01/mypostname/index.html`).
345
346438 ### Pages
347439
348 Unlike posts, pages by default mimic the source directory structure exactly. (The only exception is if your page has a `permalink` declared its front matter &mdash; in that case, the structure honors the permalink setting instead of the source folder structure.)
349
350 As with posts, if you use a permalink style that omits the `.html` file extension, each page is rendered as an `index.html` file inserted inside a folder with the page's name (for example, `mypage/index.html`).
351
352 ### Collections
353
354 By default, collections follow a similar structure in the `_site` folder as pages, except that the path is prefaced by the collection name. For example: `collectionname/mypage.html`. For permalink settings that omit the file extension, the path would be `collection_name/mypage/index.html`.
355
356 Collections have their own way of setting permalinks. Additionally, collections have unique template variables available (such as `path` and `output_ext`). See the [Configuring permalinks for collections](../collections/#permalinks) in Collections for more information.
357
358 ## Flattening pages in \_site on build
359
360 If you want to flatten your pages (pull them out of subfolders) in the `_site` directory when your site builds (similar to posts), add the `permalink` property to the front matter of each page, with no path specified:
361
362 ```yaml
363 ---
364 title: My page
365 permalink: mypageurl.html
366 ---
367 ```
368
369 ## Extensionless permalinks with no trailing slashes {#extensionless-permalinks}
370
371 Jekyll supports permalinks that contain neither a trailing slash nor a file extension, but this requires additional support from the web server to properly serve. When using these types of permalinks, output files written to disk will still have the proper file extension (typically `.html`), so the web server must be able to map requests without file extensions to these files.
372
373 Both [GitHub Pages](../github-pages/) and the Jekyll's built-in WEBrick server handle these requests properly without any additional work.
374
375 ### Apache
376
377 The Apache web server has extensive support for content negotiation and can handle extensionless URLs by setting the [multiviews](https://httpd.apache.org/docs/current/content-negotiation.html#multiviews) option in your `httpd.conf` or `.htaccess` file:
378
379 {% highlight apache %}
380 Options +MultiViews
381 {% endhighlight %}
382
383 ### Nginx
384
385 The [try_files](http://nginx.org/en/docs/http/ngx_http_core_module.html#try_files) directive allows you to specify a list of files to search for to process a request. The following configuration will instruct nginx to search for a file with an `.html` extension if an exact match for the requested URI is not found.
386
387 {% highlight nginx %}
388 try_files $uri $uri.html $uri/ =404;
389 {% endhighlight %}
390
391 ## Linking without regard to permalink styles
392
393 You can create links in your topics to other posts, pages, or collection items in a way that is valid no matter what permalink configuration you choose. By using the `link` tag, if you change your permalinks, your links won't break. See [Linking to pages](../templates#link) in Templates for more details.
440 For pages, you have to use front matter to override the global permalink,
441 and if you set a permalink via front matter defaults in `_config.yml`,
442 it will be ignored.
443
444 Pages have the following placeholders available:
445
446 <div class="mobile-side-scroller">
447 <table>
448 <thead>
449 <tr>
450 <th>Variable</th>
451 <th>Description</th>
452 </tr>
453 </thead>
454 <tbody>
455 <tr>
456 <td>
457 <p><code>:path</code></p>
458 </td>
459 <td>
460 <p>
461 Path to the page relative to the site's source directory, excluding
462 base filename of the page.
463 </p>
464 </td>
465 </tr>
466 <tr>
467 <td>
468 <p><code>:basename</code></p>
469 </td>
470 <td>
471 <p>The page's base filename</p>
472 </td>
473 </tr>
474 <tr>
475 <td>
476 <p><code>:output_ext</code></p>
477 </td>
478 <td>
479 <p>
480 Extension of the output file. (Included by default and usually
481 unnecessary.)
482 </p>
483 </td>
484 </tr>
485 </tbody>
486 </table>
487 </div>
0 ---
1 title: Commands
2 permalink: /docs/plugins/commands/
3 ---
4 As of version {% include docs_version_badge.html version="2.5.0"%}, Jekyll can be extended with plugins which provide
5 subcommands for the `jekyll` executable. This is possible by including the
6 relevant plugins in a `Gemfile` group called `:jekyll_plugins`:
7
8 ```ruby
9 group :jekyll_plugins do
10 gem "my_fancy_jekyll_plugin"
11 end
12 ```
13
14 Each `Command` must be a subclass of the `Jekyll::Command` class and must
15 contain one class method: `init_with_program`. An example:
16
17 ```ruby
18 class MyNewCommand < Jekyll::Command
19 class << self
20 def init_with_program(prog)
21 prog.command(:new) do |c|
22 c.syntax "new [options]"
23 c.description 'Create a new Jekyll site.'
24
25 c.option 'dest', '-d DEST', 'Where the site should go.'
26
27 c.action do |args, options|
28 Jekyll::Site.new_site_at(options['dest'])
29 end
30 end
31 end
32 end
33 end
34 ```
35
36 Commands should implement this single class method:
37
38 <div class="mobile-side-scroller">
39 <table>
40 <thead>
41 <tr>
42 <th>Method</th>
43 <th>Description</th>
44 </tr>
45 </thead>
46 <tbody>
47 <tr>
48 <td>
49 <p><code>init_with_program</code></p>
50 </td>
51 <td><p>
52 This method accepts one parameter, the
53 <code><a href="https://github.com/jekyll/mercenary#readme">Mercenary::Program</a></code>
54 instance, which is the Jekyll program itself. Upon the program,
55 commands may be created using the above syntax. For more details,
56 visit the Mercenary repository on GitHub.com.
57 </p></td>
58 </tr>
59 </tbody>
60 </table>
61 </div>
0 ---
1 title: Converters
2 permalink: /docs/plugins/converters/
3 ---
4
5 If you have a new markup language you’d like to use with your site, you can
6 include it by implementing your own converter. Both the Markdown and
7 [Textile](https://github.com/jekyll/jekyll-textile-converter) markup
8 languages are implemented using this method.
9
10 <div class="note info">
11 <h5>Remember your Front Matter</h5>
12 <p>
13 Jekyll will only convert files that have a YAML header at the top, even for
14 converters you add using a plugin.
15 </p>
16 </div>
17
18 Below is a converter that will take all posts ending in `.upcase` and process
19 them using the `UpcaseConverter`:
20
21 ```ruby
22 module Jekyll
23 class UpcaseConverter < Converter
24 safe true
25 priority :low
26
27 def matches(ext)
28 ext =~ /^\.upcase$/i
29 end
30
31 def output_ext(ext)
32 ".html"
33 end
34
35 def convert(content)
36 content.upcase
37 end
38 end
39 end
40 ```
41
42 Converters should implement at a minimum 3 methods:
43
44 <div class="mobile-side-scroller">
45 <table>
46 <thead>
47 <tr>
48 <th>Method</th>
49 <th>Description</th>
50 </tr>
51 </thead>
52 <tbody>
53 <tr>
54 <td>
55 <p><code>matches</code></p>
56 </td>
57 <td><p>
58 Does the given extension match this converter’s list of acceptable
59 extensions? Takes one argument: the file’s extension (including the
60 dot). Must return <code>true</code> if it matches, <code>false</code>
61 otherwise.
62 </p></td>
63 </tr>
64 <tr>
65 <td>
66 <p><code>output_ext</code></p>
67 </td>
68 <td><p>
69 The extension to be given to the output file (including the dot).
70 Usually this will be <code>".html"</code>.
71 </p></td>
72 </tr>
73 <tr>
74 <td>
75 <p><code>convert</code></p>
76 </td>
77 <td><p>
78 Logic to do the content conversion. Takes one argument: the raw content
79 of the file (without front matter). Must return a String.
80 </p></td>
81 </tr>
82 </tbody>
83 </table>
84 </div>
85
86 In our example, `UpcaseConverter#matches` checks if our filename extension is
87 `.upcase`, and will render using the converter if it is. It will call
88 `UpcaseConverter#convert` to process the content. In our simple converter we’re
89 simply uppercasing the entire content string. Finally, when it saves the page,
90 it will do so with a `.html` extension.
0 ---
1 title: Filters
2 permalink: /docs/plugins/filters/
3 ---
4
5 Filters are modules that export their methods to liquid.
6 All methods will have to take at least one parameter which represents the input
7 of the filter. The return value will be the output of the filter.
8
9 ```ruby
10 module Jekyll
11 module AssetFilter
12 def asset_url(input)
13 "http://www.example.com/#{input}?#{Time.now.to_i}"
14 end
15 end
16 end
17
18 Liquid::Template.register_filter(Jekyll::AssetFilter)
19 ```
20
21 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).
22
23 <div class="note">
24 <h5>ProTip™: Access the site object using Liquid</h5>
25 <p>
26 Jekyll lets you access the <code>site</code> object through the
27 <code>@context.registers</code> feature of Liquid at <code>@context.registers[:site]</code>. For example, you can
28 access the global configuration file <code>_config.yml</code> using
29 <code>@context.registers[:site].config</code>.
30 </p>
31 </div>
0 ---
1 title: Generators
2 permalink: /docs/plugins/generators/
3 ---
4
5 You can create a generator when you need Jekyll to create additional content based on your own rules.
6
7 A generator is a subclass of `Jekyll::Generator` that defines a `generate` method, which receives an instance of
8 [`Jekyll::Site`]({{ site.repository }}/blob/master/lib/jekyll/site.rb). The return value of `generate` is ignored.
9
10 Generators run after Jekyll has made an inventory of the existing content, and before the site is generated. Pages with
11 front matter are stored as instances of [`Jekyll::Page`]({{ site.repository }}/blob/master/lib/jekyll/page.rb) and are
12 available via `site.pages`. Static files become instances of
13 [`Jekyll::StaticFile`]({{ site.repository }}/blob/master/lib/jekyll/static_file.rb)
14 and are available via `site.static_files`. See [the Variables documentation page](/docs/variables/) and
15 [`Jekyll::Site`]({{ site.repository }}/blob/master/lib/jekyll/site.rb) for details.
16
17 In the following example, the generator will inject values computed at build time for template variables. The template
18 named `reading.html` has two undefined variables `ongoing` and `done` that will be defined or assigned a value when
19 the generator runs:
20
21 ```ruby
22 module Reading
23 class Generator < Jekyll::Generator
24 def generate(site)
25 book_data = site.data['books']
26 ongoing = book_data.select { |book| book['status'] == 'ongoing' }
27 done = book_data.select { |book| book['status'] == 'finished' }
28
29 # get template
30 reading = site.pages.find { |page| page.name == 'reading.html'}
31
32 # inject data into template
33 reading.data['ongoing'] = ongoing
34 reading.data['done'] = done
35 end
36 end
37 end
38 ```
39
40 The following example is a more complex generator that generates new pages.
41
42 In this example, the aim of the generator is to create a page for each category registered in the `site`. The pages are
43 created at runtime, so their contents, front matter and other attributes need to be designed by the plugin itself.
44 * The pages are intended to render a list of all documents under a given category. So the basename of the rendered file
45 would be better as `index.html`.
46 * Having the ability to configure the pages via [front matter defaults](/docs/configuration/front-matter-defaults/)
47 would be awesome! So assigning a particular `type` to these pages would be beneficial.
48
49 ```ruby
50 module SamplePlugin
51 class CategoryPageGenerator < Jekyll::Generator
52 safe true
53
54 def generate(site)
55 site.categories.each do |category, posts|
56 site.pages << CategoryPage.new(site, category, posts)
57 end
58 end
59 end
60
61 # Subclass of `Jekyll::Page` with custom method definitions.
62 class CategoryPage < Jekyll::Page
63 def initialize(site, category, posts)
64 @site = site # the current site instance.
65 @base = site.source # path to the source directory.
66 @dir = category # the directory the page will reside in.
67
68 # All pages have the same filename, so define attributes straight away.
69 @basename = 'index' # filename without the extension.
70 @ext = '.html' # the extension.
71 @name = 'index.html' # basically @basename + @ext.
72
73 # Initialize data hash with a key pointing to all posts under current category.
74 # This allows accessing the list in a template via `page.linked_docs`.
75 @data = {
76 'linked_docs' => posts
77 }
78
79 # Look up front matter defaults scoped to type `categories`, if given key
80 # doesn't exist in the `data` hash.
81 data.default_proc = proc do |_, key|
82 site.frontmatter_defaults.find(relative_path, :categories, key)
83 end
84 end
85
86 # Placeholders that are used in constructing page URL.
87 def url_placeholders
88 {
89 :category => @dir,
90 :basename => basename,
91 :output_ext => output_ext,
92 }
93 end
94 end
95 end
96 ```
97
98 The generated pages can now be set up to use a particular layout or output at a particular path in the destination
99 directory all via the config file using front matter defaults. For example:
100
101 ```yaml
102 # _config.yml
103
104 defaults:
105 - scope:
106 type: categories # select all category pages
107 values:
108 layout: category_page
109 permalink: categories/:category/
110 ```
111
112 ## Technical Aspects
113
114 Generators need to implement only one method:
115
116 <div class="mobile-side-scroller">
117 <table>
118 <thead>
119 <tr>
120 <th>Method</th>
121 <th>Description</th>
122 </tr>
123 </thead>
124 <tbody>
125 <tr>
126 <td>
127 <p><code>generate</code></p>
128 </td>
129 <td>
130 <p>Generates content as a side-effect.</p>
131 </td>
132 </tr>
133 </tbody>
134 </table>
135 </div>
136
137 If your generator is contained within a single file, it can be named whatever you want but it should have an `.rb`
138 extension. If your generator is split across multiple files, it should be packaged as a Rubygem to be published at
139 https://rubygems.org/. In this case, the name of the gem depends on the availability of the name at that site because
140 no two gems can have the same name.
141
142 By default, Jekyll looks for generators in the `_plugins` directory. However, you can change the default directory by
143 assigning the desired name to the key `plugins_dir` in the config file.
0 ---
1 title: Hooks
2 permalink: /docs/plugins/hooks/
3 ---
4
5 Using hooks, your plugin can exercise fine-grained control over various aspects of the build process. If your plugin defines any hooks, Jekyll
6 will call them at pre-defined points.
7
8 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,
9 and code to call whenever the hook is triggered. For example, if you want to execute some custom functionality every time Jekyll renders a
10 page, you could register a hook like this:
11
12 ```ruby
13 Jekyll::Hooks.register :pages, :post_render do |page|
14 # code to call after Jekyll renders a page
15 end
16 ```
17
18 *Note: The `:post_convert` events mentioned hereafter is a feature introduced in v4.2.0.*
19
20 Out of the box, Jekyll has pre-defined hook points for owners `:site`, `:pages`, `:documents` and `:clean`. Additionally, the hook points
21 defined for `:documents` can be utilized for individual collections only by invoking the collection type instead. i.e. `:posts` for documents
22 in collection `_posts` and `:movies` for documents in collection `_movies`. In all cases, Jekyll calls your hooks with the owner object as the
23 first callback parameter.
24
25 Every registered hook owner supports the following events &mdash; `:post_init`, `:pre_render`, `:post_convert`, `:post_render`, `:post_write`
26 &mdash; however, the `:site` owner is set up to *respond* to *special event names*. Refer to the subsequent section for details.
27
28 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
29 `:pre_render` events, the payload gives you full control over the variables that are available during rendering, with the `:site, :post_render`
30 event, the payload contains final values after rendering all the site (useful for sitemaps, feeds, etc).
31
32 ## Built-in Hook Owners and Events
33 The complete list of available hooks:
34
35 <div class="mobile-side-scroller">
36 <table id="builtin-hooks">
37 <thead>
38 <tr>
39 <th>Owner</th>
40 <th>Event</th>
41 <th>Triggered at</th>
42 </tr>
43 </thead>
44 <tbody>
45 <tr>
46 <td rowspan="6">
47 <p><code>:site</code></p>
48 <p>Encompasses the entire site</p>
49 </td>
50 <td>
51 <p><code>:after_init</code></p>
52 </td>
53 <td>
54 <p>Just after the site initializes. Good for modifying the configuration of the site. Triggered once per build / serve session</p>
55 </td>
56 </tr>
57 <tr>
58 <td>
59 <p><code>:after_reset</code></p>
60 </td>
61 <td>
62 <p>Just after the site resets during regeneration</p>
63 </td>
64 </tr>
65 <tr>
66 <td>
67 <p><code>:post_read</code></p>
68 </td>
69 <td>
70 <p>After all source files have been read and loaded from disk</p>
71 </td>
72 </tr>
73 <tr>
74 <td>
75 <p><code>:pre_render</code></p>
76 </td>
77 <td>
78 <p>Just before rendering the whole site</p>
79 </td>
80 </tr>
81 <tr>
82 <td>
83 <p><code>:post_render</code></p>
84 </td>
85 <td>
86 <p>After rendering the whole site, but before writing any files</p>
87 </td>
88 </tr>
89 <tr>
90 <td>
91 <p><code>:post_write</code></p>
92 </td>
93 <td>
94 <p>After writing all of the rendered files to disk</p>
95 </td>
96 </tr>
97 <tr>
98 <td rowspan="5">
99 <p><code>:pages</code></p>
100 <p>Allows fine-grained control over all pages in the site</p>
101 </td>
102 <td>
103 <p><code>:post_init</code></p>
104 </td>
105 <td>
106 <p>Whenever a page is initialized</p>
107 </td>
108 </tr>
109 <tr>
110 <td>
111 <p><code>:pre_render</code></p>
112 </td>
113 <td>
114 <p>Just before rendering a page</p>
115 </td>
116 </tr>
117 <tr>
118 <td>
119 <p><code>:post_convert</code></p>
120 </td>
121 <td>
122 <p>After converting the page content, but before rendering the page layout</p>
123 </td>
124 </tr>
125 <tr>
126 <td>
127 <p><code>:post_render</code></p>
128 </td>
129 <td>
130 <p>After rendering a page, but before writing it to disk</p>
131 </td>
132 </tr>
133 <tr>
134 <td>
135 <p><code>:post_write</code></p>
136 </td>
137 <td>
138 <p>After writing a page to disk</p>
139 </td>
140 </tr>
141 <tr>
142 <td rowspan="5">
143 <p><code>:documents</code></p>
144 <p>Allows fine-grained control over all documents in the site including posts and documents in user-defined collections</p>
145 </td>
146 <td>
147 <p><code>:post_init</code></p>
148 </td>
149 <td>
150 <p>Whenever any document is initialized</p>
151 </td>
152 </tr>
153 <tr>
154 <td>
155 <p><code>:pre_render</code></p>
156 </td>
157 <td>
158 <p>Just before rendering a document</p>
159 </td>
160 </tr>
161 <tr>
162 <td>
163 <p><code>:post_convert</code></p>
164 </td>
165 <td>
166 <p>
167 After converting the document content, but before rendering the document
168 layout
169 </p>
170 </td>
171 </tr>
172 <tr>
173 <td>
174 <p><code>:post_render</code></p>
175 </td>
176 <td>
177 <p>After rendering a document, but before writing it to disk</p>
178 </td>
179 </tr>
180 <tr>
181 <td>
182 <p><code>:post_write</code></p>
183 </td>
184 <td>
185 <p>After writing a document to disk</p>
186 </td>
187 </tr>
188 <tr>
189 <td rowspan="5">
190 <p><code>:posts</code></p>
191 <p>Allows fine-grained control over all posts in the site without affecting documents in user-defined collections</p>
192 </td>
193 <td>
194 <p><code>:post_init</code></p>
195 </td>
196 <td>
197 <p>Whenever a post is initialized</p>
198 </td>
199 </tr>
200 <tr>
201 <td>
202 <p><code>:pre_render</code></p>
203 </td>
204 <td>
205 <p>Just before rendering a post</p>
206 </td>
207 </tr>
208 <tr>
209 <td>
210 <p><code>:post_convert</code></p>
211 </td>
212 <td>
213 <p>After converting the post content, but before rendering the postlayout</p>
214 </td>
215 </tr>
216 <tr>
217 <td>
218 <p><code>:post_render</code></p>
219 </td>
220 <td>
221 <p>After rendering a post, but before writing it to disk</p>
222 </td>
223 </tr>
224 <tr>
225 <td>
226 <p><code>:post_write</code></p>
227 </td>
228 <td>
229 <p>After writing a post to disk</p>
230 </td>
231 </tr>
232 <tr>
233 <td>
234 <p><code>:clean</code></p>
235 <p>Fine-grained control on the list of obsolete files determined to be deleted during the site's cleanup phase.</p>
236 </td>
237 <td>
238 <p><code>:on_obsolete</code></p>
239 </td>
240 <td>
241 <p>During the cleanup of a site's destination before it is built</p>
242 </td>
243 </tr>
244 </tbody>
245 </table>
246 </div>
247
248 ## Hooks for custom Jekyll objects
249
250 You can also register and trigger hooks for Jekyll objects introduced by your plugin. All it takes is placing `trigger` calls under a suitable
251 `owner` name, at positions desired within your custom class and registering the `owner` by your plugin.
252
253 To illustrate, consider the following plugin that implements custom functionality for every custom `Excerpt` object initialized:
254
255 ```ruby
256 module Foobar
257 class HookedExcerpt < Jekyll::Excerpt
258 def initialize(doc)
259 super
260 trigger_hooks(:post_init)
261 end
262
263 def output
264 @output ||= trigger_hooks(:post_render, renderer.run)
265 end
266
267 def renderer
268 @renderer ||= Jekyll::Renderer.new(
269 doc.site, self, site.site_payload
270 )
271 end
272
273 def trigger_hooks(hook_name, *args)
274 Jekyll::Hooks.trigger :excerpts, hook_name, self, *args
275 end
276 end
277 end
278
279 Jekyll::Hooks.register :excerpts, :post_init do |excerpt|
280 Jekyll.logger.debug "Initialized:",
281 "Hooked Excerpt for #{excerpt.doc.inspect}"
282 end
283
284 Jekyll::Hooks.register :excerpts, :post_render do |excerpt, output|
285 return output unless excerpt.doc.type == :posts
286 Foobar.transform(output)
287 end
288 ```
0 ---
1 title: Plugins
2 permalink: /docs/plugins/installation/
3 ---
4
5 Jekyll has built-in support for using plugins to extend the core functionality.
6
7 Primarily, any file with extension `.rb` placed within a `_plugins` directory at the root of the site's `source`, will be automatically loaded
8 during a build session.
9
10 This behavior can be configured as follows:
11
12 - The `_plugins` directory may be changed either directly via the command-line or via the configuration file(s).
13 - Plugins in the `_plugins` directory (or its equivalent(s)) will not be loaded when Jekyll is running in `safe` mode.
14 - This route cannot be used to extend the Jekyll CLI.
15
16 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`.
17 Additionally, if you're building in `safe` mode, the gem needs to be listed under a top-level key named `whitelist`. For example:
18
19 ```yaml
20 plugins:
21 - jekyll-gist
22 - jekyll-coffeescript
23 - jekyll-seo-tag
24 - some-other-jekyll-plugin
25
26 # Enable safe mode
27 safe: true
28
29 # Whitelist plugins under safe mode.
30 # Note that `some-other-jekyll-plugin` is not listed here. Therefore,
31 # it will not be loaded under safe mode.
32 whitelist:
33 - jekyll-gist
34 - jekyll-coffeescript
35 - jekyll-seo-tag
36 ```
37
38 In the absence of a Gemfile, one must manually ensure that listed plugins have been installed prior to invoking Jekyll. For example, the
39 latest versions of gems in the above list may be installed to a system-wide location by running:
40
41 ```sh
42 gem install jekyll-gist jekyll-coffeescript jekyll-remote-theme some-other-jekyll-plugin
43 ```
44
45 ## Using a Gemfile
46
47 The maintenance of various gem dependencies may be greatly simplified by using a Gemfile (usually at the root of the site's source) in
48 conjunction with a Rubygem named `bundler`. The Gemfile however **should** list all the primary dependencies of your site, including Jekyll
49 itself, not just gem-based plugins of the site because Bundler narrows the scope of installed gems to just *runtime dependencies* resolved by
50 evaluating the Gemfile. For example:
51
52 ```ruby
53 source "https://rubygems.org"
54
55 # Use the latest version.
56 gem "jekyll"
57
58 # The theme of current site, locked to a certain version.
59 gem "minima", "2.4.1"
60
61 # Plugins of this site loaded during a build with proper
62 # site configuration.
63 gem "jekyll-gist"
64 gem "jekyll-coffeescript"
65 gem "jekyll-seo-tag", "~> 1.5"
66 gem "some-other-jekyll-plugin"
67
68 # A dependency of a custom-plugin inside `_plugins` directory.
69 gem "nokogiri", "~> 1.11"
70 ```
71
72 The gems listed in the Gemfile can be collectively installed by simply running `bundle install`.
73
74 ### The `:jekyll_plugins` Gemfile group
75 {: #the-jekyll_plugins-group}
76
77 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
78 the very beginning of any Jekyll process, irrespective of the `--safe` CLI flag or entries in the configuration file(s).
79
80 While this route allows one to enhance Jekyll's CLI with additional subcommands and options, or avoid having to list gems in the configuration
81 file, the downside is the necessity to be mindful of what gems are included in the group. For example:
82
83 ```ruby
84 source "https://rubygems.org"
85
86 # Use the latest version.
87 gem "jekyll"
88
89 # The theme of current site, locked to a certain version.
90 gem "minima", "2.4.1"
91
92 # Plugins of this site loaded only if configured correctly.
93 gem "jekyll-gist"
94 gem "jekyll-coffeescript"
95
96 # Gems loaded irrespective of site configuration.
97 group :jekyll_plugins do
98 gem "jekyll-cli-plus"
99 gem "jekyll-seo-tag", "~> 1.5"
100 gem "some-other-jekyll-plugin"
101 end
102 ```
103
104 <div class="note info">
105 <h5>Plugins on GitHub Pages</h5>
106 <p>
107 <a href="https://pages.github.com/">GitHub Pages</a> is powered by Jekyll. All GitHub Pages sites are generated using the
108 <code>--safe</code> option to disable plugins (with the exception of some
109 <a href="https://pages.github.com/versions">whitelisted plugins</a>) for security reasons. Unfortunately, this means your plugins won't
110 work if you’re deploying via GitHub Pages.<br><br>
111 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
112 GitHub repository instead of the Jekyll source files.
113 </p>
114 </div>
115
116 <div class="note">
117 <h5>
118 <code>_plugins</code>, <code>_config.yml</code> and <code>Gemfile</code> can be used simultaneously
119 </h5>
120 <p>
121 You may use any of the aforementioned plugin routes simultaneously in the same site if you so choose.
122 Use of one does not restrict the use of the others.
123 </p>
124 </div>
0 ---
1 title: Tags
2 permalink: /docs/plugins/tags/
3 ---
4
5 If you’d like to include custom liquid tags in your site, you can do so by
6 hooking into the tagging system. Built-in examples added by Jekyll include the
7 `highlight` and `include` tags. Below is an example of a custom liquid tag that
8 will output the time the page was rendered:
9
10 ```ruby
11 module Jekyll
12 class RenderTimeTag < Liquid::Tag
13
14 def initialize(tag_name, text, tokens)
15 super
16 @text = text
17 end
18
19 def render(context)
20 "#{@text} #{Time.now}"
21 end
22 end
23 end
24
25 Liquid::Template.register_tag('render_time', Jekyll::RenderTimeTag)
26 ```
27
28 At a minimum, liquid tags must implement:
29
30 <div class="mobile-side-scroller">
31 <table>
32 <thead>
33 <tr>
34 <th>Method</th>
35 <th>Description</th>
36 </tr>
37 </thead>
38 <tbody>
39 <tr>
40 <td>
41 <p><code>render</code></p>
42 </td>
43 <td>
44 <p>Outputs the content of the tag.</p>
45 </td>
46 </tr>
47 </tbody>
48 </table>
49 </div>
50
51 You must also register the custom tag with the Liquid template engine as
52 follows:
53
54 ```ruby
55 Liquid::Template.register_tag('render_time', Jekyll::RenderTimeTag)
56 ```
57
58 In the example above, we can place the following tag anywhere in one of our
59 pages:
60
61 {% raw %}
62 ```liquid
63 <p>{% render_time page rendered at: %}</p>
64 ```
65 {% endraw %}
66
67 And we would get something like this on the page:
68
69 ```html
70 <p>page rendered at: Tue June 22 23:38:47 –0500 2010</p>
71 ```
72
73 ## Tag Blocks
74
75 The `render_time` tag seen above can also be rewritten as a tag block by
76 inheriting the `Liquid::Block` class. Look at the example below:
77
78 ```ruby
79 module Jekyll
80 class RenderTimeTagBlock < Liquid::Block
81
82 def render(context)
83 text = super
84 "<p>#{text} #{Time.now}</p>"
85 end
86
87 end
88 end
89
90 Liquid::Template.register_tag('render_time', Jekyll::RenderTimeTagBlock)
91 ```
92
93 We can now use the tag block anywhere:
94
95 {% raw %}
96 ```liquid
97 {% render_time %}
98 page rendered at:
99 {% endrender_time %}
100 ```
101 {% endraw %}
102
103 And we would still get the same output as above on the page:
104
105 ```html
106 <p>page rendered at: Tue June 22 23:38:47 –0500 2010</p>
107 ```
108
109 {: .note .info}
110 In the above example, the tag block and the tag are both registered with
111 the name <code>render_time</code>, but to register a tag and a tag block using
112 the same name in the same project is not recommended as this may lead to
113 conflicts.
0 ---
1 title: Your first plugin
2 permalink: /docs/plugins/your-first-plugin/
3 ---
4
5 Plugins allow you to extend Jekyll's behavior to fit your needs. There are six
6 types of plugins in Jekyll.
7
8 ## Generators
9
10 [Generators](/docs/plugins/generators/) create content on your site.
11 For example:
12
13 * [jekyll-feed](https://github.com/jekyll/jekyll-feed) creates an Atom feed of
14 blog posts.
15 * [jekyll-archives](https://github.com/jekyll/jekyll-archives) creates archive
16 pages for blog categories and tags.
17 * [jekyll-sitemap](https://github.com/jekyll/jekyll-sitemap) creates a sitemap.
18
19 ## Converters
20
21 [Converters](/docs/plugins/converters/) change a markup language into another
22 format. For example:
23
24 * [jekyll-textile-converter](https://github.com/jekyll/jekyll-textile-converter)
25 converts textile to HTML.
26 * [jekyll-coffeescript](https://github.com/jekyll/jekyll-coffeescript) converts
27 Coffeescript to JavaScript.
28 * [jekyll-opal](https://github.com/jekyll/jekyll-opal) converts Ruby to
29 JavaScript.
30
31 ## Commands
32
33 [Commands](/docs/plugins/commands/) extend the `jekyll` executable with
34 subcommands. For example:
35
36 * [jekyll-compose](https://github.com/jekyll/jekyll-compose) adds subcommands
37 for creating a post, page or draft.
38
39 ## Tags
40
41 [Tags](/docs/plugins/tags/) create custom Liquid tags. For example:
42
43 * [jekyll-youtube](https://github.com/dommmel/jekyll-youtube) embeds a YouTube
44 video.
45 * [jekyll-asset-path-plugin](https://github.com/samrayner/jekyll-asset-path-plugin)
46 outputs a relative URL for assets.
47 * [jekyll-swfobject](https://github.com/sectore/jekyll-swfobject) embeds a SWF
48 object.
49
50 ## Filters
51
52 [Filters](/docs/plugins/filters/) create custom Liquid filters. For example:
53
54 * [jekyll-time-ago](https://github.com/markets/jekyll-timeago) - The distance
55 between two dates in words.
56 * [jekyll-toc](https://github.com/toshimaru/jekyll-toc) - Generates a table of
57 content.
58 * [jekyll-email-protect](https://github.com/vwochnik/jekyll-email-protect) -
59 Obfuscates emails to protect them from spam bots.
60
61 ## Hooks
62
63 [Hooks](/docs/plugins/hooks/) give fine-grained control to extend the build
64 process. For example:
65
66 * [jemoji](https://github.com/jekyll/jemoji) Display emojis :+1:
67 * [jekyll-mentions](https://github.com/jekyll/jekyll-mentions) turns mentions @jekyll into links
68 * [jekyll-spaceship](https://github.com/jeffreytse/jekyll-spaceship) - advanced example. Provides
69 powerful supports for table, mathjax, plantuml, video, etc.
70
71 ## Flags
72
73 There are two flags to be aware of when writing a plugin:
74
75 <div class="mobile-side-scroller">
76 <table>
77 <thead>
78 <tr>
79 <th>Flag</th>
80 <th>Description</th>
81 </tr>
82 </thead>
83 <tbody>
84 <tr>
85 <td>
86 <p><code>safe</code></p>
87 </td>
88 <td>
89 <p>
90 A boolean flag that informs Jekyll whether this plugin may be safely
91 executed in an environment where arbitrary code execution is not
92 allowed. This is used by GitHub Pages to determine which core plugins
93 may be used, and which are unsafe to run. If your plugin does not
94 allow for arbitrary code execution, set this to <code>true</code>.
95 GitHub Pages still won’t load your plugin, but if you submit it for
96 inclusion in core, it’s best for this to be correct!
97 </p>
98 </td>
99 </tr>
100 <tr>
101 <td>
102 <p><code>priority</code></p>
103 </td>
104 <td>
105 <p>
106 This flag determines what order the plugin is loaded in. Valid values
107 are: <code>:lowest</code>, <code>:low</code>, <code>:normal</code>,
108 <code>:high</code>, and <code>:highest</code>. Highest priority
109 matches are applied first, lowest priority are applied last.
110 </p>
111 </td>
112 </tr>
113 </tbody>
114 </table>
115 </div>
116
117 To use one of the example plugins above as an illustration, here is how you’d
118 specify these two flags:
119
120 ```ruby
121 module Jekyll
122 class UpcaseConverter < Converter
123 safe true
124 priority :low
125 ...
126 end
127 end
128 ```
129
130 ## Best Practices
131
132 The guides help you with the specifics of creating plugins. We also have some
133 recommended best practices to help structure your plugin.
134
135 We recommend using a [gem](/docs/ruby-101/#gems) for your plugin. This will
136 help you manage dependencies, keep separation from your site source code and
137 allow you to share functionality across multiple projects. For tips on creating
138 a gem take a look at the
139 [Ruby gems guide](https://guides.rubygems.org/make-your-own-gem/) or look
140 through the source code of an existing plugin such as
141 [jekyll-feed](https://github.com/jekyll/jekyll-feed).
66 content specific to your site. You can run custom code for your site without
77 having to modify the Jekyll source itself.
88
9 <div class="note info">
10 <h5>Plugins on GitHub Pages</h5>
11 <p>
12 <a href="https://pages.github.com/">GitHub Pages</a> is powered by Jekyll.
13 However, all Pages sites are generated using the <code>--safe</code> option
14 to disable custom plugins for security reasons. Unfortunately, this means
15 your plugins won’t work if you’re deploying to GitHub Pages.<br><br>
16 You can still use GitHub Pages to publish your site, but you’ll need to
17 convert the site locally and push the generated static files to your GitHub
18 repository instead of the Jekyll source files.
19 </p>
20 </div>
9 {: .note .info}
10 You can add specific plugins to the `whitelist` key in `_config.yml` to allow them to run in safe mode.
2111
22 ## Installing a plugin
23
24 You have 3 options for installing plugins:
25
26 1. In your site source root, make a `_plugins` directory. Place your plugins
27 here. Any file ending in `*.rb` inside this directory will be loaded before
28 Jekyll generates your site.
29
30 2. In your `_config.yml` file, add a new array with the key `plugins` (or `gems` for Jekyll < `3.5.0`) and the
31 values of the gem names of the plugins you'd like to use. An example:
32
33 ```yaml
34 # This will require each of these plugins automatically.
35 plugins:
36 - jekyll-gist
37 - jekyll-coffeescript
38 - jekyll-assets
39 - another-jekyll-plugin
40 ```
41
42 Then install your plugins using `gem install jekyll-gist jekyll-coffeescript jekyll-assets another-jekyll-plugin`
43
44 3. Add the relevant plugins to a Bundler group in your `Gemfile`. An
45 example:
46
47 ```ruby
48 group :jekyll_plugins do
49 gem "jekyll-gist"
50 gem "jekyll-coffeescript"
51 gem "jekyll-assets"
52 gem "another-jekyll-plugin"
53 end
54 ```
55
56 Now you need to install all plugins from your Bundler group by running single command `bundle install`.
57
58 <div class="note info">
59 <h5>
60 <code>_plugins</code>, <code>_config.yml</code> and <code>Gemfile</code>
61 can be used simultaneously
62 </h5>
63 <p>
64 You may use any of the aforementioned plugin options simultaneously in the
65 same site if you so choose. Use of one does not restrict the use of the
66 others.
67 </p>
68 </div>
69
70 ### The jekyll_plugins group
71
72 Jekyll gives this particular group of gems in your `Gemfile` a different
73 treatment. Any gem included in this group is loaded before Jekyll starts
74 processing the rest of your source directory.
75
76 A gem included here will be activated even if its not explicitly listed under
77 the `plugins:` key in your site's config file.
78
79 <div class="note warning">
80 <p>
81 Gems included in the <code>:jekyll-plugins</code> group are activated
82 regardless of the <code>--safe</code> mode setting. Be aware of what
83 gems are included under this group!
84 </p>
85 </div>
86
87
88 In general, plugins you make will fall broadly into one of five categories:
89
90 1. [Generators](#generators)
91 2. [Converters](#converters)
92 3. [Commands](#commands)
93 4. [Tags](#tags)
94 5. [Hooks](#hooks)
95
96 See the bottom of the page for a [list of available plugins](#available-plugins).
97
98 For further information on how to develop your own plugins, check out the [Liquid documentation](https://github.com/Shopify/liquid/wiki/Liquid-for-Programmers) as well.
99
100 If you never developed a Jekyll plugin [check this useful wrap-up](https://ayastreb.me/writing-a-jekyll-plugin/) by @ayastreb to get started.
101
102 ## Generators
103
104 You can create a generator when you need Jekyll to create additional content
105 based on your own rules.
106
107 A generator is a subclass of `Jekyll::Generator` that defines a `generate`
108 method, which receives an instance of
109 [`Jekyll::Site`]({{ site.repository }}/blob/master/lib/jekyll/site.rb). The
110 return value of `generate` is ignored.
111
112 Generators run after Jekyll has made an inventory of the existing content, and
113 before the site is generated. Pages with YAML Front Matters are stored as
114 instances of
115 [`Jekyll::Page`]({{ site.repository }}/blob/master/lib/jekyll/page.rb)
116 and are available via `site.pages`. Static files become instances of
117 [`Jekyll::StaticFile`]({{ site.repository }}/blob/master/lib/jekyll/static_file.rb)
118 and are available via `site.static_files`. See
119 [the Variables documentation page](/docs/variables/) and
120 [`Jekyll::Site`]({{ site.repository }}/blob/master/lib/jekyll/site.rb)
121 for more details.
122
123 For instance, a generator can inject values computed at build time for template
124 variables. In the following example the template `reading.html` has two
125 variables `ongoing` and `done` that we fill in the generator:
126
127 ```ruby
128 module Reading
129 class Generator < Jekyll::Generator
130 def generate(site)
131 ongoing, done = Book.all.partition(&:ongoing?)
132
133 reading = site.pages.detect {|page| page.name == 'reading.html'}
134 reading.data['ongoing'] = ongoing
135 reading.data['done'] = done
136 end
137 end
138 end
139 ```
140
141 This is a more complex generator that generates new pages:
142
143 ```ruby
144 module Jekyll
145 class CategoryPage < Page
146 def initialize(site, base, dir, category)
147 @site = site
148 @base = base
149 @dir = dir
150 @name = 'index.html'
151
152 self.process(@name)
153 self.read_yaml(File.join(base, '_layouts'), 'category_index.html')
154 self.data['category'] = category
155
156 category_title_prefix = site.config['category_title_prefix'] || 'Category: '
157 self.data['title'] = "#{category_title_prefix}#{category}"
158 end
159 end
160
161 class CategoryPageGenerator < Generator
162 safe true
163
164 def generate(site)
165 if site.layouts.key? 'category_index'
166 dir = site.config['category_dir'] || 'categories'
167 site.categories.each_key do |category|
168 site.pages << CategoryPage.new(site, site.source, File.join(dir, category), category)
169 end
170 end
171 end
172 end
173 end
174 ```
175
176 In this example, our generator will create a series of files under the
177 `categories` directory for each category, listing the posts in each category
178 using the `category_index.html` layout.
179
180 Generators are only required to implement one method:
181
182 <div class="mobile-side-scroller">
183 <table>
184 <thead>
185 <tr>
186 <th>Method</th>
187 <th>Description</th>
188 </tr>
189 </thead>
190 <tbody>
191 <tr>
192 <td>
193 <p><code>generate</code></p>
194 </td>
195 <td>
196 <p>Generates content as a side-effect.</p>
197 </td>
198 </tr>
199 </tbody>
200 </table>
201 </div>
202
203 ## Converters
204
205 If you have a new markup language you’d like to use with your site, you can
206 include it by implementing your own converter. Both the Markdown and
207 [Textile](https://github.com/jekyll/jekyll-textile-converter) markup
208 languages are implemented using this method.
209
210 <div class="note info">
211 <h5>Remember your YAML Front Matter</h5>
212 <p>
213 Jekyll will only convert files that have a YAML header at the top, even for
214 converters you add using a plugin.
215 </p>
216 </div>
217
218 Below is a converter that will take all posts ending in `.upcase` and process
219 them using the `UpcaseConverter`:
220
221 ```ruby
222 module Jekyll
223 class UpcaseConverter < Converter
224 safe true
225 priority :low
226
227 def matches(ext)
228 ext =~ /^\.upcase$/i
229 end
230
231 def output_ext(ext)
232 ".html"
233 end
234
235 def convert(content)
236 content.upcase
237 end
238 end
239 end
240 ```
241
242 Converters should implement at a minimum 3 methods:
243
244 <div class="mobile-side-scroller">
245 <table>
246 <thead>
247 <tr>
248 <th>Method</th>
249 <th>Description</th>
250 </tr>
251 </thead>
252 <tbody>
253 <tr>
254 <td>
255 <p><code>matches</code></p>
256 </td>
257 <td><p>
258 Does the given extension match this converter’s list of acceptable
259 extensions? Takes one argument: the file’s extension (including the
260 dot). Must return <code>true</code> if it matches, <code>false</code>
261 otherwise.
262 </p></td>
263 </tr>
264 <tr>
265 <td>
266 <p><code>output_ext</code></p>
267 </td>
268 <td><p>
269 The extension to be given to the output file (including the dot).
270 Usually this will be <code>".html"</code>.
271 </p></td>
272 </tr>
273 <tr>
274 <td>
275 <p><code>convert</code></p>
276 </td>
277 <td><p>
278 Logic to do the content conversion. Takes one argument: the raw content
279 of the file (without YAML Front Matter). Must return a String.
280 </p></td>
281 </tr>
282 </tbody>
283 </table>
284 </div>
285
286 In our example, `UpcaseConverter#matches` checks if our filename extension is
287 `.upcase`, and will render using the converter if it is. It will call
288 `UpcaseConverter#convert` to process the content. In our simple converter we’re
289 simply uppercasing the entire content string. Finally, when it saves the page,
290 it will do so with a `.html` extension.
291
292 ## Commands
293
294 As of version 2.5.0, Jekyll can be extended with plugins which provide
295 subcommands for the `jekyll` executable. This is possible by including the
296 relevant plugins in a `Gemfile` group called `:jekyll_plugins`:
297
298 ```ruby
299 group :jekyll_plugins do
300 gem "my_fancy_jekyll_plugin"
301 end
302 ```
303
304 Each `Command` must be a subclass of the `Jekyll::Command` class and must
305 contain one class method: `init_with_program`. An example:
306
307 ```ruby
308 class MyNewCommand < Jekyll::Command
309 class << self
310 def init_with_program(prog)
311 prog.command(:new) do |c|
312 c.syntax "new [options]"
313 c.description 'Create a new Jekyll site.'
314
315 c.option 'dest', '-d DEST', 'Where the site should go.'
316
317 c.action do |args, options|
318 Jekyll::Site.new_site_at(options['dest'])
319 end
320 end
321 end
322 end
323 end
324 ```
325
326 Commands should implement this single class method:
327
328 <div class="mobile-side-scroller">
329 <table>
330 <thead>
331 <tr>
332 <th>Method</th>
333 <th>Description</th>
334 </tr>
335 </thead>
336 <tbody>
337 <tr>
338 <td>
339 <p><code>init_with_program</code></p>
340 </td>
341 <td><p>
342 This method accepts one parameter, the
343 <code><a href="https://github.com/jekyll/mercenary#readme">Mercenary::Program</a></code>
344 instance, which is the Jekyll program itself. Upon the program,
345 commands may be created using the above syntax. For more details,
346 visit the Mercenary repository on GitHub.com.
347 </p></td>
348 </tr>
349 </tbody>
350 </table>
351 </div>
352
353 ## Tags
354
355 If you’d like to include custom liquid tags in your site, you can do so by
356 hooking into the tagging system. Built-in examples added by Jekyll include the
357 `highlight` and `include` tags. Below is an example of a custom liquid tag that
358 will output the time the page was rendered:
359
360 ```ruby
361 module Jekyll
362 class RenderTimeTag < Liquid::Tag
363
364 def initialize(tag_name, text, tokens)
365 super
366 @text = text
367 end
368
369 def render(context)
370 "#{@text} #{Time.now}"
371 end
372 end
373 end
374
375 Liquid::Template.register_tag('render_time', Jekyll::RenderTimeTag)
376 ```
377
378 At a minimum, liquid tags must implement:
379
380 <div class="mobile-side-scroller">
381 <table>
382 <thead>
383 <tr>
384 <th>Method</th>
385 <th>Description</th>
386 </tr>
387 </thead>
388 <tbody>
389 <tr>
390 <td>
391 <p><code>render</code></p>
392 </td>
393 <td>
394 <p>Outputs the content of the tag.</p>
395 </td>
396 </tr>
397 </tbody>
398 </table>
399 </div>
400
401 You must also register the custom tag with the Liquid template engine as
402 follows:
403
404 ```ruby
405 Liquid::Template.register_tag('render_time', Jekyll::RenderTimeTag)
406 ```
407
408 In the example above, we can place the following tag anywhere in one of our
409 pages:
410
411 {% raw %}
412 ```ruby
413 <p>{% render_time page rendered at: %}</p>
414 ```
415 {% endraw %}
416
417 And we would get something like this on the page:
418
419 ```html
420 <p>page rendered at: Tue June 22 23:38:47 –0500 2010</p>
421 ```
422
423 ### Liquid filters
424
425 You can add your own filters to the Liquid template system much like you can
426 add tags above. Filters are simply modules that export their methods to liquid.
427 All methods will have to take at least one parameter which represents the input
428 of the filter. The return value will be the output of the filter.
429
430 ```ruby
431 module Jekyll
432 module AssetFilter
433 def asset_url(input)
434 "http://www.example.com/#{input}?#{Time.now.to_i}"
435 end
436 end
437 end
438
439 Liquid::Template.register_filter(Jekyll::AssetFilter)
440 ```
441
442 <div class="note">
443 <h5>ProTip™: Access the site object using Liquid</h5>
444 <p>
445 Jekyll lets you access the <code>site</code> object through the
446 <code>context.registers</code> feature of Liquid at <code>context.registers[:site]</code>. For example, you can
447 access the global configuration file <code>_config.yml</code> using
448 <code>context.registers[:site].config</code>.
449 </p>
450 </div>
451
452 ### Flags
453
454 There are two flags to be aware of when writing a plugin:
455
456 <div class="mobile-side-scroller">
457 <table>
458 <thead>
459 <tr>
460 <th>Flag</th>
461 <th>Description</th>
462 </tr>
463 </thead>
464 <tbody>
465 <tr>
466 <td>
467 <p><code>safe</code></p>
468 </td>
469 <td>
470 <p>
471 A boolean flag that informs Jekyll whether this plugin may be safely
472 executed in an environment where arbitrary code execution is not
473 allowed. This is used by GitHub Pages to determine which core plugins
474 may be used, and which are unsafe to run. If your plugin does not
475 allow for arbitrary code execution, set this to <code>true</code>.
476 GitHub Pages still won’t load your plugin, but if you submit it for
477 inclusion in core, it’s best for this to be correct!
478 </p>
479 </td>
480 </tr>
481 <tr>
482 <td>
483 <p><code>priority</code></p>
484 </td>
485 <td>
486 <p>
487 This flag determines what order the plugin is loaded in. Valid values
488 are: <code>:lowest</code>, <code>:low</code>, <code>:normal</code>,
489 <code>:high</code>, and <code>:highest</code>. Highest priority
490 matches are applied first, lowest priority are applied last.
491 </p>
492 </td>
493 </tr>
494 </tbody>
495 </table>
496 </div>
497
498 To use one of the example plugins above as an illustration, here is how you’d
499 specify these two flags:
500
501 ```ruby
502 module Jekyll
503 class UpcaseConverter < Converter
504 safe true
505 priority :low
506 ...
507 end
508 end
509 ```
510
511 ## Hooks
512
513 Using hooks, your plugin can exercise fine-grained control over various aspects
514 of the build process. If your plugin defines any hooks, Jekyll will call them
515 at pre-defined points.
516
517 Hooks are registered to a container and an event name. To register one, you
518 call Jekyll::Hooks.register, and pass the container, event name, and code to
519 call whenever the hook is triggered. For example, if you want to execute some
520 custom functionality every time Jekyll renders a post, you could register a
521 hook like this:
522
523 ```ruby
524 Jekyll::Hooks.register :posts, :post_render do |post|
525 # code to call after Jekyll renders a post
526 end
527 ```
528
529 Jekyll provides hooks for <code>:site</code>, <code>:pages</code>,
530 <code>:posts</code>, and <code>:documents</code>. In all cases, Jekyll calls
531 your hooks with the container object as the first callback parameter. However,
532 all `:pre_render` hooks and the`:site, :post_render` hook will also provide a
533 payload hash as a second parameter. In the case of `:pre_render`, the payload
534 gives you full control over the variables that are available while rendering.
535 In the case of `:site, :post_render`, the payload contains final values after
536 rendering all the site (useful for sitemaps, feeds, etc).
537
538 The complete list of available hooks is below:
539
540 <div class="mobile-side-scroller">
541 <table>
542 <thead>
543 <tr>
544 <th>Container</th>
545 <th>Event</th>
546 <th>Called</th>
547 </tr>
548 </thead>
549 <tbody>
550 <tr>
551 <td>
552 <p><code>:site</code></p>
553 </td>
554 <td>
555 <p><code>:after_init</code></p>
556 </td>
557 <td>
558 <p>Just after the site initializes, but before setup & render. Good
559 for modifying the configuration of the site.</p>
560 </td>
561 </tr>
562 <tr>
563 <td>
564 <p><code>:site</code></p>
565 </td>
566 <td>
567 <p><code>:after_reset</code></p>
568 </td>
569 <td>
570 <p>Just after site reset</p>
571 </td>
572 </tr>
573 <tr>
574 <td>
575 <p><code>:site</code></p>
576 </td>
577 <td>
578 <p><code>:post_read</code></p>
579 </td>
580 <td>
581 <p>After site data has been read and loaded from disk</p>
582 </td>
583 </tr>
584 <tr>
585 <td>
586 <p><code>:site</code></p>
587 </td>
588 <td>
589 <p><code>:pre_render</code></p>
590 </td>
591 <td>
592 <p>Just before rendering the whole site</p>
593 </td>
594 </tr>
595 <tr>
596 <td>
597 <p><code>:site</code></p>
598 </td>
599 <td>
600 <p><code>:post_render</code></p>
601 </td>
602 <td>
603 <p>After rendering the whole site, but before writing any files</p>
604 </td>
605 </tr>
606 <tr>
607 <td>
608 <p><code>:site</code></p>
609 </td>
610 <td>
611 <p><code>:post_write</code></p>
612 </td>
613 <td>
614 <p>After writing the whole site to disk</p>
615 </td>
616 </tr>
617 <tr>
618 <td>
619 <p><code>:pages</code></p>
620 </td>
621 <td>
622 <p><code>:post_init</code></p>
623 </td>
624 <td>
625 <p>Whenever a page is initialized</p>
626 </td>
627 </tr>
628 <tr>
629 <td>
630 <p><code>:pages</code></p>
631 </td>
632 <td>
633 <p><code>:pre_render</code></p>
634 </td>
635 <td>
636 <p>Just before rendering a page</p>
637 </td>
638 </tr>
639 <tr>
640 <td>
641 <p><code>:pages</code></p>
642 </td>
643 <td>
644 <p><code>:post_render</code></p>
645 </td>
646 <td>
647 <p>After rendering a page, but before writing it to disk</p>
648 </td>
649 </tr>
650 <tr>
651 <td>
652 <p><code>:pages</code></p>
653 </td>
654 <td>
655 <p><code>:post_write</code></p>
656 </td>
657 <td>
658 <p>After writing a page to disk</p>
659 </td>
660 </tr>
661 <tr>
662 <td>
663 <p><code>:posts</code></p>
664 </td>
665 <td>
666 <p><code>:post_init</code></p>
667 </td>
668 <td>
669 <p>Whenever a post is initialized</p>
670 </td>
671 </tr>
672 <tr>
673 <td>
674 <p><code>:posts</code></p>
675 </td>
676 <td>
677 <p><code>:pre_render</code></p>
678 </td>
679 <td>
680 <p>Just before rendering a post</p>
681 </td>
682 </tr>
683 <tr>
684 <td>
685 <p><code>:posts</code></p>
686 </td>
687 <td>
688 <p><code>:post_render</code></p>
689 </td>
690 <td>
691 <p>After rendering a post, but before writing it to disk</p>
692 </td>
693 </tr>
694 <tr>
695 <td>
696 <p><code>:posts</code></p>
697 </td>
698 <td>
699 <p><code>:post_write</code></p>
700 </td>
701 <td>
702 <p>After writing a post to disk</p>
703 </td>
704 </tr>
705 <tr>
706 <td>
707 <p><code>:documents</code></p>
708 </td>
709 <td>
710 <p><code>:post_init</code></p>
711 </td>
712 <td>
713 <p>Whenever a document is initialized</p>
714 </td>
715 </tr>
716 <tr>
717 <td>
718 <p><code>:documents</code></p>
719 </td>
720 <td>
721 <p><code>:pre_render</code></p>
722 </td>
723 <td>
724 <p>Just before rendering a document</p>
725 </td>
726 </tr>
727 <tr>
728 <td>
729 <p><code>:documents</code></p>
730 </td>
731 <td>
732 <p><code>:post_render</code></p>
733 </td>
734 <td>
735 <p>After rendering a document, but before writing it to disk</p>
736 </td>
737 </tr>
738 <tr>
739 <td>
740 <p><code>:documents</code></p>
741 </td>
742 <td>
743 <p><code>:post_write</code></p>
744 </td>
745 <td>
746 <p>After writing a document to disk</p>
747 </td>
748 </tr>
749 </tbody>
750 </table>
751 </div>
752
753 ## Available Plugins
754
755 You can find a few useful plugins at the following locations:
756
757 #### Generators
758
759 - [Sitemap.xml Generator by Michael Levin](https://github.com/kinnetica/jekyll-plugins): Generates a sitemap.xml file by traversing all of the available posts and pages.
760 - [Full-text search by Pascal Widdershoven](https://github.com/PascalW/jekyll_indextank): Adds full-text search to your Jekyll site with a plugin and a bit of JavaScript.
761 - [AliasGenerator by Thomas Mango](https://github.com/tsmango/jekyll_alias_generator): Generates redirect pages for posts when an alias is specified in the YAML Front Matter.
762 - [Pageless Redirect Generator by Nick Quinlan](https://github.com/nquinlan/jekyll-pageless-redirects): Generates redirects based on files in the Jekyll root, with support for htaccess style redirects.
763 - [RssGenerator by Assaf Gelber](https://github.com/agelber/jekyll-rss): Automatically creates an RSS 2.0 feed from your posts.
764 - [Monthly archive generator by Shigeya Suzuki](https://github.com/shigeya/jekyll-monthly-archive-plugin): Generator and template which renders monthly archive like MovableType style, based on the work by Ilkka Laukkanen and others above.
765 - [Category archive generator by Shigeya Suzuki](https://github.com/shigeya/jekyll-category-archive-plugin): Generator and template which renders category archive like MovableType style, based on Monthly archive generator.
766 - [Emoji for Jekyll](https://github.com/yihangho/emoji-for-jekyll): Seamlessly enable emoji for all posts and pages.
767 - [Compass integration for Jekyll](https://github.com/mscharley/jekyll-compass): Easily integrate Compass and Sass with your Jekyll website.
768 - [Pages Directory by Ben Baker-Smith](https://github.com/bbakersmith/jekyll-pages-directory): Defines a `_pages` directory for page files which routes its output relative to the project root.
769 - [Page Collections by Jeff Kolesky](https://github.com/jeffkole/jekyll-page-collections): Generates collections of pages with functionality that resembles posts.
770 - [Windows 8.1 Live Tile Generation by Matt Sheehan](https://github.com/sheehamj13/jekyll-live-tiles): Generates Internet Explorer 11 config.xml file and Tile Templates for pinning your site to Windows 8.1.
771 - [Typescript Generator by Matt Sheehan](https://github.com/sheehamj13/jekyll_ts): Generate Javascript on build from your Typescript.
772 - [Jekyll::AutolinkEmail by Ivan Tse](https://github.com/ivantsepp/jekyll-autolink_email): Autolink your emails.
773 - [Jekyll::GitMetadata by Ivan Tse](https://github.com/ivantsepp/jekyll-git_metadata): Expose Git metadata for your templates.
774 - [Jekyll Auto Image by Merlos](https://github.com/merlos/jekyll-auto-image): Gets the first image of a post. Useful to list your posts with images or to add [twitter cards](https://dev.twitter.com/cards/overview) to your site.
775 - [Jekyll Portfolio Generator by Shannon Babincsak](https://github.com/codeinpink/jekyll-portfolio-generator): Generates project pages and computes related projects out of project data files.
776 - [Jekyll-Umlauts by Arne Gockeln](https://github.com/webchef/jekyll-umlauts): This generator replaces all german umlauts (äöüß) case sensitive with html.
777 - [Jekyll Flickr Plugin](https://github.com/lawmurray/indii-jekyll-flickr) by [Lawrence Murray](http://www.indii.org): Generates posts for photos uploaded to a Flickr photostream.
778 - [Jekyll::Paginate::Category](https://github.com/midnightSuyama/jekyll-paginate-category): Pagination Generator for Jekyll Category.
779 - [AMP-Jekyll by Juuso Mikkonen](https://github.com/juusaw/amp-jekyll): Generate [Accelerated Mobile Pages](https://www.ampproject.org) of Jekyll posts.
780 - [Jekyll Art Gallery plugin](https://github.com/alexivkin/Jekyll-Art-Gallery-Plugin): An advanced art/photo gallery generation plugin for creating galleries from a set of image folders. Supports image tagging, thumbnails, sorting, image rotation, post-processing (remove EXIF, add watermark), multiple collections and much more.
781 - [jekyll-ga](https://github.com/developmentseed/jekyll-ga): A Jekyll plugin that downloads Google Analytics data and adds it to posts. Useful for making a site that lists "most popular" content. [Read the introduction](https://developmentseed.org/blog/google-analytics-jekyll-plugin/) post on the developmentSEED blog.
782 - [jekyll-multi-paginate](https://github.com/fadhilnapis/jekyll-multi-paginate): Simple Jekyll paginator for multiple page. Ease you to make pagination on multiple page especially like multiple language.
783 - [jekyll-category-pages](https://github.com/field-theory/jekyll-category-pages): Easy-to-use category index pages with and without pagination. Supports non-URL-safe category keywords and has extensive documentation and test coverage.
784 - [Tweetsert](https://github.com/ibrado/jekyll-tweetsert): Imports tweets (Twitter statuses) as new posts. Features multiple timeline support, hashtag import, filtering, automatic category and/or tags, optional retweets and replies.
785 - [Stickyposts](https://github.com/ibrado/jekyll-stickyposts): Moves or copies (pins) posts marked `sticky: true` to the top of the list. Perfect for keeping important announcements on the home page, or giving collections a descriptive entry. Paginator friendly.
786 - [Jekyll::Paginate::Content](https://github.com/ibrado/jekyll-paginate-content): Content paginator in the style of jekyll-paginator-v2 that splits pages, posts, and collection entries into several pages. Specify a separator or use HTML &lt;h1&gt; etc. headers. Automatic splitting, single-page view, pager/trail, self-adjusting links, multipage TOC, SEO support.
787 - [Premonition](https://github.com/amedia/premonition): Adds block-styled side content to your page. For example summary, notes, hints or warning boxes.
788 - [jekyll-fontello](https://github.com/ericcornelissen/jekyll-fontello): A Jekyll plugin that automatically downloads your webfont from Fontello.
789
790 #### Converters
791
792 - [Pug plugin by Doug Beney](http://jekyll-pug.dougie.io): Use the popular Pug (previously Jade) templating language in Jekyll. Complete with caching, includes support, and much more.
793 - [Textile converter](https://github.com/jekyll/jekyll-textile-converter): Convert `.textile` files into HTML. Also includes the `textilize` Liquid filter.
794 - [Slim plugin](https://github.com/slim-template/jekyll-slim): Slim converter and includes for Jekyll with support for Liquid tags.
795 - [Markdown References by Olov Lassus](https://github.com/olov/jekyll-references): Keep all your markdown reference-style link definitions in one \_references.md file.
796 - [ReStructuredText Converter](https://github.com/xdissent/jekyll-rst): Converts ReST documents to HTML with Pygments syntax highlighting.
797 - [Jekyll-pandoc-multiple-formats](https://github.com/fauno/jekyll-pandoc-multiple-formats) by [edsl](https://github.com/edsl): Use pandoc to generate your site in multiple formats. Supports pandoc’s markdown extensions.
798 - [Customized Kramdown Converter](https://github.com/mvdbos/kramdown-with-pygments): Enable Pygments syntax highlighting for Kramdown-parsed fenced code blocks.
799 - [Bigfootnotes Plugin](https://github.com/TheFox/jekyll-bigfootnotes): Enables big footnotes for Kramdown.
800 - [AsciiDoc Plugin](https://github.com/asciidoctor/jekyll-asciidoc): AsciiDoc convertor for Jekyll using [Asciidoctor](http://asciidoctor.org/).
801 - [Lazy Tweet Embedding](https://github.com/takuti/jekyll-lazy-tweet-embedding): Automatically convert tweet urls into twitter cards.
802 - [jekyll-commonmark](https://github.com/pathawks/jekyll-commonmark): Markdown converter that uses [libcmark](https://github.com/jgm/CommonMark), the reference parser for CommonMark.
803
804 #### Filters
805
806 - [Truncate HTML](https://github.com/MattHall/truncatehtml) by [Matt Hall](https://codebeef.com/): A Jekyll filter that truncates HTML while preserving markup structure.
807 - [Domain Name Filter by Lawrence Woodman](https://github.com/LawrenceWoodman/domain_name-liquid_filter): Filters the input text so that just the domain name is left.
808 - [Smilify](https://github.com/SaswatPadhi/jekyll_smilify) by [SaswatPadhi](https://github.com/SaswatPadhi): Convert text emoticons in your content to themeable smiley pics.
809 - [Jekyll-timeago](https://github.com/markets/jekyll-timeago): Converts a time value to the time ago in words.
810 - [pluralize](https://github.com/bdesham/pluralize): Easily combine a number and a word into a grammatically-correct amount like “1 minute” or “2 minute**s**”.
811 - [reading_time](https://github.com/bdesham/reading_time): Count words and estimate reading time for a piece of text, ignoring HTML elements that are unlikely to contain running text.
812 - [Table of Content Generator](https://github.com/dafi/jekyll-toc-generator): Generate the HTML code containing a table of content (TOC), the TOC can be customized in many way, for example you can decide which pages can be without TOC.
813 - [jekyll-toc](https://github.com/toshimaru/jekyll-toc): A liquid filter plugin for Jekyll which generates a table of contents.
814 - [jekyll-humanize](https://github.com/23maverick23/jekyll-humanize): This is a port of the Django app humanize which adds a "human touch" to data. Each method represents a Fluid type filter that can be used in your Jekyll site templates. Given that Jekyll produces static sites, some of the original methods do not make logical sense to port (e.g. naturaltime).
815 - [Jekyll-Ordinal](https://github.com/PatrickC8t/Jekyll-Ordinal): Jekyll liquid filter to output a date ordinal such as "st", "nd", "rd", or "th".
816 - [Deprecated articles keeper](https://github.com/kzykbys/JekyllPlugins) by [Kazuya Kobayashi](http://blog.kazuya.co/): A simple Jekyll filter which monitor how old an article is.
817 - [Jekyll-jalali](https://github.com/mehdisadeghi/jekyll-jalali) by [Mehdi Sadeghi](http://mehdix.ir): A simple Gregorian to Jalali date converter filter.
818 - [Jekyll Thumbnail Filter](https://github.com/matallo/jekyll-thumbnail-filter): Related posts thumbnail filter.
819 - [liquid-md5](https://github.com/pathawks/liquid-md5): Returns an MD5 hash. Helpful for generating Gravatars in templates.
820 - [jekyll-roman](https://github.com/paulrobertlloyd/jekyll-roman): A liquid filter for Jekyll that converts numbers into Roman numerals.
821 - [jekyll-typogrify](https://github.com/myles/jekyll-typogrify): A Jekyll plugin that brings the functions of [typogruby](http://avdgaag.github.io/typogruby/).
822 - [Jekyll Email Protect](https://github.com/vwochnik/jekyll-email-protect): Email protection liquid filter for Jekyll
823 - [Jekyll Uglify Filter](https://github.com/mattg/jekyll-uglify-filter): A Liquid filter that runs your JavaScript through UglifyJS.
824 - [match_regex](https://github.com/sparanoid/match_regex): A Liquid filter to perform regex match.
825 - [replace_regex](https://github.com/sparanoid/replace_regex): A Liquid filter to perform regex replace.
826 - [Jekyll Money](https://rubygems.org/gems/jekyll-money): A Jekyll plugin for dealing with money. Because we all have to at some point.
827 - [jekyll-random](https://github.com/codecalm/jekyll-random) by [codecalm](https://nodecalm.net): A Jekyll plugin that generates pseudo-random data. Very useful when you want to generate a large amount of random data.
828
829 #### Tags
830
831 You can find a few useful plugins at the following locations:
832
833 - [Jekyll-gist](https://github.com/jekyll/jekyll-gist): Use the `gist` tag to easily embed a GitHub Gist onto your site. This works with public or secret gists.
834 - [Asset Path Tag](https://github.com/samrayner/jekyll-asset-path-plugin) by [Sam Rayner](http://www.samrayner.com/): Allows organisation of assets into subdirectories by outputting a path for a given file relative to the current post or page.
835 - [Delicious Plugin by Christian Hellsten](https://github.com/christianhellsten/jekyll-plugins): Fetches and renders bookmarks from delicious.com.
836 - [Embed.ly client by Robert Böhnke](https://github.com/robb/jekyll-embedly-client): Autogenerate embeds from URLs using oEmbed.
837 - [FlickrSetTag by Thomas Mango](https://github.com/tsmango/jekyll_flickr_set_tag): Generates image galleries from Flickr sets.
838 - [Tweet Tag by Scott W. Bradley](https://github.com/scottwb/jekyll-tweet-tag): Liquid tag for [Embedded Tweets](https://dev.twitter.com/docs/embedded-tweets) using Twitter’s shortcodes.
839 - [Jekyll Twitter Plugin](https://github.com/rob-murray/jekyll-twitter-plugin): A Liquid tag plugin that renders Tweets from Twitter API. Currently supports the [oEmbed](https://dev.twitter.com/rest/reference/get/statuses/oembed) API.
840 - [Jekyll-contentblocks](https://github.com/rustygeldmacher/jekyll-contentblocks): Lets you use Rails-like content_for tags in your templates, for passing content from your posts up to your layouts.
841 - [Jekyll-beastiepress](https://github.com/okeeblow/jekyll-beastiepress): FreeBSD utility tags for Jekyll sites.
842 - [Bibjekyll](https://github.com/pablooliveira/bibjekyll): Render BibTeX-formatted bibliographies/citations included in posts and pages using bibtex2html.
843 - [Jekyll-citation](https://github.com/archome/jekyll-citation): Render BibTeX-formatted bibliographies/citations included in posts and pages (pure Ruby).
844 - [Jekyll Dribbble Set Tag](https://github.com/ericdfields/Jekyll-Dribbble-Set-Tag): Builds Dribbble image galleries from any user.
845 - [JekyllGalleryTag](https://github.com/redwallhp/JekyllGalleryTag) by [redwallhp](https://github.com/redwallhp): Generates thumbnails from a directory of images and displays them in a grid.
846 - [Jekyll-swfobject](https://github.com/sectore/jekyll-swfobject): Liquid plugin for embedding Adobe Flash files (.swf) using [SWFObject](https://github.com/swfobject/swfobject).
847 - [Jekyll Picture Tag](https://github.com/robwierzbowski/jekyll-picture-tag): Easy responsive images for Jekyll. Based on the proposed [`<picture>`](https://html.spec.whatwg.org/multipage/embedded-content.html#the-picture-element) element, polyfilled with Scott Jehl’s [Picturefill](https://github.com/scottjehl/picturefill).
848 - [Jekyll Image Tag](https://github.com/robwierzbowski/jekyll-image-tag): Better images for Jekyll. Save image presets, generate resized images, and add classes, alt text, and other attributes.
849 - [Jekyll Responsive Image](https://github.com/wildlyinaccurate/jekyll-responsive-image): Responsive images for Jekyll. Automatically resizes images, supports all responsive methods (`<picture>`, `srcset`, Imager.js, etc), super-flexible configuration.
850 - [Ditaa Tag](https://github.com/matze/jekyll-ditaa) by [matze](https://github.com/matze): Renders ASCII diagram art into PNG images and inserts a figure tag.
851 - [Jekyll Suggested Tweet](https://github.com/davidensinger/jekyll-suggested-tweet) by [David Ensinger](https://github.com/davidensinger/): A Liquid tag for Jekyll that allows for the embedding of suggested tweets via Twitter’s Web Intents API.
852 - [Jekyll Date Chart](https://github.com/GSI/jekyll_date_chart) by [GSI](https://github.com/GSI): Block that renders date line charts based on textile-formatted tables.
853 - [Jekyll Image Encode](https://github.com/GSI/jekyll_image_encode) by [GSI](https://github.com/GSI): Tag that renders base64 codes of images fetched from the web.
854 - [Jekyll Quick Man](https://github.com/GSI/jekyll_quick_man) by [GSI](https://github.com/GSI): Tag that renders pretty links to man page sources on the internet.
855 - [Image Set/Gallery Tag](https://github.com/callmeed/jekyll-image-set) by [callmeed](https://github.com/callmeed): Renders HTML for an image gallery from a folder in your Jekyll site. Just pass it a folder name and class/tag options.
856 - [jekyll_figure](https://github.com/lmullen/jekyll_figure): Generate figures and captions with links to the figure in a variety of formats
857 - [Jekyll GitHub Sample Tag](https://github.com/bwillis/jekyll-github-sample): A liquid tag to include a sample of a github repo file in your Jekyll site.
858 - [Jekyll Project Version Tag](https://github.com/rob-murray/jekyll-version-plugin): A Liquid tag plugin that renders a version identifier for your Jekyll site sourced from the git repository containing your code.
859 - [Piwigo Gallery](https://github.com/AlessandroLorenzi/piwigo_gallery) by [Alessandro Lorenzi](http://blog.alorenzi.eu/): Jekyll plugin to generate thumbnails from a Piwigo gallery and display them with a Liquid tag
860 - [mathml.rb](https://github.com/tmthrgd/jekyll-plugins) by Tom Thorogood: A plugin to convert TeX mathematics into MathML for display.
861 - [webmention_io.rb](https://github.com/aarongustafson/jekyll-webmention_io) by [Aaron Gustafson](http://aaron-gustafson.com/): A plugin to enable [webmention](https://indieweb.org/webmention) integration using [Webmention.io](https://webmention.io/). Includes an optional JavaScript for updating webmentions automatically between publishes and, if available, in realtime using WebSockets.
862 - [Jekyll 500px Embed](https://github.com/lkorth/jekyll-500px-embed) by Luke Korth. A Liquid tag plugin that embeds [500px](https://500px.com/) photos.
863 - [inline\_highlight](https://github.com/bdesham/inline_highlight): A tag for inline syntax highlighting.
864 - [jekyll-mermaid](https://github.com/jasonbellamy/jekyll-mermaid): Simplify the creation of mermaid diagrams and flowcharts in your posts and pages.
865 - [twa](https://github.com/Ezmyrelda/twa): Twemoji Awesome plugin for Jekyll. Liquid tag allowing you to use twitter emoji in your jekyll pages.
866 - [Fetch remote file content](https://github.com/dimitri-koenig/jekyll-plugins) by [Dimitri König](https://www.dimitrikoenig.net/): Using `remote_file_content` tag you can fetch the content of a remote file and include it as if you would put the content right into your markdown file yourself. Very useful for including code from github repo's to always have a current repo version.
867 - [jekyll-asciinema](https://github.com/mnuessler/jekyll-asciinema): A tag for embedding asciicasts recorded with [asciinema](https://asciinema.org) in your Jekyll pages.
868 - [Jekyll-Youtube](https://github.com/dommmel/jekyll-youtube) A Liquid tag that embeds Youtube videos. The default emded markup is responsive but you can also specify your own by using an include/partial.
869 - [Jekyll Flickr Plugin](https://github.com/lawmurray/indii-jekyll-flickr) by [Lawrence Murray](http://www.indii.org): Embeds Flickr photosets (albums) as a gallery of thumbnails, with lightbox links to larger images.
870 - [jekyll-figure](https://github.com/paulrobertlloyd/jekyll-figure): A liquid tag for Jekyll that generates `<figure>` elements.
871 - [Jekyll Video Embed](https://github.com/eug/jekyll-video-embed): It provides several tags to easily embed videos (e.g. Youtube, Vimeo, UStream and Ted Talks)
872 - [jekyll-i18n_tags](https://github.com/KrzysiekJ/jekyll-i18n_tags): Translate your templates.
873 - [Jekyll Ideal Image Slider](https://github.com/jekylltools/jekyll-ideal-image-slider): Liquid tag plugin to create image sliders using [Ideal Image Slider](https://github.com/gilbitron/Ideal-Image-Slider).
874 - [Jekyll Tags List Plugin](https://github.com/crispgm/jekyll-tags-list-plugin): A Liquid tag plugin that creates tags list in specific order.
875 - [Jekyll Maps](https://github.com/ayastreb/jekyll-maps) by [Anatoliy Yastreb](https://github.com/ayastreb): A Jekyll plugin to easily embed maps with filterable locations.
876 - [Jekyll Cloudinary](https://nhoizey.github.io/jekyll-cloudinary/) by [Nicolas Hoizey](https://nicolas-hoizey.com/): a Jekyll plugin adding a Liquid tag to ease the use of Cloudinary for responsive images in your Markdown/Kramdown posts.
877 - [jekyll-include-absolute-plugin](https://github.com/tnhu/jekyll-include-absolute-plugin) by [Tan Nhu](https://github.com/tnhu): A Jekyll plugin to include a file from its path relative to Jekyll's source folder.
878 - [Jekyll Download Tag](https://github.com/mattg/jekyll-download-tag): A Liquid tag that acts like `include`, but for external resources.
879 - [Jekyll Brand Social Wall](https://github.com/MediaComem/jekyll-brand-social-wall): A jekyll plugin to generate a social wall with your favorite social networks
880 - [Jekyll If File Exists](https://github.com/k-funk/jekyll-if-file-exists): A Jekyll Plugin that checks if a file exists with an if/else block.
881 - [BibSonomy](https://github.com/rjoberon/bibsonomy-jekyll): Jekyll
882 plugin to generate publication lists from [BibSonomy](https://www.bibsonomy.org/).
883 - [github-cards](https://github.com/edward-shen/github-cards): Creates styleable Github cards for your Github projects.
884 - [disqus-for-jekyll](https://github.com/kacperduras/disqus-for-jekyll): A Jekyll plugin to view the comments powered by Disqus.
885 - [jekyll-html](https://github.com/kacperduras/jekyll-html): A Jekyll plugin to use HTML tags in Jekyll pages, posts and collections.
886 - [jekyll-onebox](https://github.com/rriemann/jekyll-onebox): Liquid tag for displaying HTML previews (embeds) for links to popular domains. Plugin is based on [Onebox](https://github.com/discourse/onebox) that powers link previews in [Discourse](http://github.com/discourse/discourse) forums.
887 - [jekyll-w2m](https://github.com/kacperduras/jekyll-w2m): A Jekyll plugin to liberate content from Microsoft Word documents (powered by [word-to-markdown](https://github.com/benbalter/word-to-markdown)).
888
889 #### Collections
890
891 - [Jekyll Plugins by Recursive Design](https://github.com/recurser/jekyll-plugins): Plugins to generate Project pages from GitHub readmes, a Category page, and a Sitemap generator.
892 - [Company website and blog plugins](https://github.com/flatterline/jekyll-plugins) by Flatterline, a Ruby on Rails development company: Portfolio/project page generator, team/individual page generator, an author bio liquid tag for use on posts, and a few other smaller plugins.
893 - [Jekyll plugins by Aucor](https://github.com/aucor/jekyll-plugins): Plugins for trimming unwanted newlines/whitespace and sorting pages by weight attribute.
894
895 #### Other
896
897 - [Analytics for Jekyll](https://github.com/hendrikschneider/jekyll-analytics) by Hendrik Schneider: An effortless way to add various trackers like Google Analytics, Matomo (formerly Piwik), mPulse, etc. to your site.
898 - [ditaa-ditaa](https://github.com/tmthrgd/ditaa-ditaa) by Tom Thorogood: a drastic revision of jekyll-ditaa that renders diagrams drawn using ASCII art into PNG images.
899 - [Pygments Cache Path by Raimonds Simanovskis](https://github.com/rsim/blog.rayapps.com/blob/master/_plugins/pygments_cache_patch.rb): Plugin to cache syntax-highlighted code from Pygments.
900 - [Related Posts by Lawrence Woodman](https://github.com/LawrenceWoodman/related_posts-jekyll_plugin): Overrides `site.related_posts` to use categories to assess relationship.
901 - [jekyll-tagging-related_posts](https://github.com/toshimaru/jekyll-tagging-related_posts): Jekyll related_posts function based on tags (works on Jekyll3).
902 - [Jekyll-localization](https://github.com/blackwinter/jekyll-localization): Jekyll plugin that adds localization features to the rendering engine.
903 - [Jekyll-rendering](https://github.com/blackwinter/jekyll-rendering): Jekyll plugin to provide alternative rendering engines.
904 - [Jekyll-pagination](https://github.com/blackwinter/jekyll-pagination): Jekyll plugin to extend the pagination generator.
905 - [Jekyll-tagging](https://github.com/pattex/jekyll-tagging): Jekyll plugin to automatically generate a tag cloud and tag pages.
906 - [Jekyll-scholar](https://github.com/inukshuk/jekyll-scholar): Jekyll extensions for the blogging scholar.
907 - [Jekyll-assets](http://jekyll.github.io/jekyll-assets/) by [ixti](https://github.com/ixti): Rails-alike assets pipeline (write assets in CoffeeScript, Sass, LESS etc; specify dependencies for automatic bundling using simple declarative comments in assets; minify and compress; use JST templates; cache bust; and many-many more).
908 - [JAPR](https://github.com/kitsched/japr): Jekyll Asset Pipeline Reborn - Powerful asset pipeline for Jekyll that collects, converts and compresses JavaScript and CSS assets.
909 - [Jekyll-minibundle](https://github.com/tkareine/jekyll-minibundle): Asset bundling and cache busting using external minification tool of your choice. No gem dependencies.
910 - [Singlepage-jekyll](https://github.com/JCB-K/singlepage-jekyll) by [JCB-K](https://github.com/JCB-K): Turns Jekyll into a dynamic one-page website.
911 - [generator-jekyllrb](https://github.com/robwierzbowski/generator-jekyllrb): A generator that wraps Jekyll in [Yeoman](http://yeoman.io/), a tool collection and workflow for building modern web apps.
912 - [grunt-jekyll](https://github.com/dannygarcia/grunt-jekyll): A straightforward [Grunt](http://gruntjs.com/) plugin for Jekyll.
913 - [jekyll-postfiles](https://github.com/indirect/jekyll-postfiles): Add `_postfiles` directory and {% raw %}`{{ postfile }}`{% endraw %} tag so the files a post refers to will always be right there inside your repo.
914 - [A layout that compresses HTML](http://jch.penibelst.de/): GitHub Pages compatible, configurable way to compress HTML files on site build.
915 - [Jekyll CO₂](https://github.com/wdenton/jekyll-co2): Generates HTML showing the monthly change in atmospheric CO₂ at the Mauna Loa observatory in Hawaii.
916 - [remote-include](http://www.northfieldx.co.uk/remote-include/): Includes files using remote URLs
917 - [jekyll-minifier](https://github.com/digitalsparky/jekyll-minifier): Minifies HTML, XML, CSS, and Javascript both inline and as separate files utilising yui-compressor and htmlcompressor.
918 - [Jekyll views router](https://bitbucket.org/nyufac/jekyll-views-router): Simple router between generator plugins and templates.
919 - [Jekyll Language Plugin](https://github.com/vwochnik/jekyll-language-plugin): Jekyll 3.0-compatible multi-language plugin for posts, pages and includes.
920 - [Jekyll Deploy](https://github.com/vwochnik/jekyll-deploy): Adds a `deploy` sub-command to Jekyll.
921 - [Official Contentful Jekyll Plugin](https://github.com/contentful/jekyll-contentful-data-import): Adds a `contentful` sub-command to Jekyll to import data from Contentful.
922 - [jekyll-paspagon](https://github.com/KrzysiekJ/jekyll-paspagon): Sell your posts in various formats for cryptocurrencies.
923 - [Hawkins](https://github.com/awood/hawkins): Adds a `liveserve` sub-command to Jekyll that incorporates [LiveReload](http://livereload.com/) into your pages while you preview them. No more hitting the refresh button in your browser!
924 - [Jekyll Autoprefixer](https://github.com/vwochnik/jekyll-autoprefixer): Autoprefixer integration for Jekyll
925 - [Jekyll-breadcrumbs](https://github.com/git-no/jekyll-breadcrumbs): Creates breadcrumbs for Jekyll 3.x, includes features like SEO optimization, optional breadcrumb item translation and more.
926 - [generator-jekyllized](https://github.com/sondr3/generator-jekyllized): A Yeoman generator for rapidly developing sites with Gulp. Live reload your site, automatically minify and optimize your assets and much more.
927 - [Jekyll-Spotify](https://github.com/MertcanGokgoz/Jekyll-Spotify): Easily output Spotify Embed Player for jekyll
928 - [jekyll-menus](https://github.com/forestryio/jekyll-menus): Hugo style menus for your Jekyll site... recursive menus included.
929 - [jekyll-data](https://github.com/ashmaroli/jekyll-data): Read data files within Jekyll Theme Gems.
930 - [jekyll-pinboard](https://github.com/snaptortoise/jekyll-pinboard-plugin): Access your Pinboard bookmarks within your Jekyll theme.
931 - [jekyll-migrate-permalink](https://github.com/mpchadwick/jekyll-migrate-permalink): Adds a `migrate-permalink` sub-command to help deal with side effects of changing your permalink.
932 - [Jekyll-Post](https://github.com/robcrocombe/jekyll-post): A CLI tool to easily draft, edit, and publish Jekyll posts.
933 - [jekyll-numbered-headings](https://github.com/muratayusuke/jekyll-numbered-headings): Adds ordered number to headings.
934 - [jekyll-pre-commit](https://github.com/mpchadwick/jekyll-pre-commit): A framework for running checks against your posts using a git pre-commit hook before you publish them.
935 - [jekyll-pwa-plugin](https://github.com/lavas-project/jekyll-pwa): A plugin provides PWA support for Jekyll. It generates a service worker in Jekyll build process and makes precache and runtime cache available in the runtime with Google Workbox.
936 - [jekyll-algolia](https://community.algolia.com/jekyll-algolia/): Add fast and relevant search to your Jekyll site through the Algolia API.
937
938 <div class="note info">
939 <h5>Submit your gem plugins</h5>
940 <p>
941 You're encouraged to add your Jekyll gem plugins to this list, <a href="../contributing/">read the contributing page</a> to find
942 out how to make that happen.
943 </p>
944 </div>
12 * [Installation]({{ '/docs/plugins/installation/' | relative_url }}) - How to install plugins
13 * [Your first plugin]({{ '/docs/plugins/your-first-plugin/' | relative_url }}) - How to write plugins
14 * [Generators]({{ '/docs/plugins/generators/' | relative_url }}) - Create additional content on your site
15 * [Converters]({{ '/docs/plugins/converters/' | relative_url }}) - Change a markup language into another format
16 * [Commands]({{ '/docs/plugins/commands/' | relative_url }}) - Extend the `jekyll` executable with subcommands
17 * [Tags]({{ '/docs/plugins/tags/' | relative_url }}) - Create custom Liquid tags
18 * [Filters]({{ '/docs/plugins/filters/' | relative_url }}) - Create custom Liquid filters
19 * [Hooks]({{ '/docs/plugins/hooks/' | relative_url }}) - Fine-grained control to extend the build process
00 ---
1 title: Writing posts
1 title: Posts
22 permalink: /docs/posts/
3 ---
4
5 One of Jekyll’s best aspects is that it is “blog aware”. What does this mean,
6 exactly? Well, simply put, it means that blogging is baked into Jekyll’s
7 functionality. If you write articles and publish them online, you can publish
8 and maintain a blog simply by managing a folder of text-files on your computer.
9 Compared to the hassle of configuring and maintaining databases and web-based
10 CMS systems, this will be a welcome change!
3 redirect_from:
4 - /docs/drafts/
5 ---
6
7 Blogging is baked into Jekyll. You write blog posts as text files and Jekyll
8 provides everything you need to turn it into a blog.
119
1210 ## The Posts Folder
1311
14 As explained on the [directory structure](../structure/) page, the `_posts`
15 folder is where your blog posts will live. These files are generally
16 [Markdown](https://daringfireball.net/projects/markdown/) or HTML, but can
17 be other formats with the proper converter installed.
18 All posts must have [YAML Front Matter](../frontmatter/), and they will be
19 converted from their source format into an HTML page that is part of your
20 static site.
21
22 ### Creating Post Files
23
24 To create a new post, all you need to do is create a file in the `_posts`
25 directory. How you name files in this folder is important. Jekyll requires blog
26 post files to be named according to the following format:
12 The `_posts` folder is where your blog posts live. You typically write posts
13 in [Markdown](https://daringfireball.net/projects/markdown/), HTML is
14 also supported.
15
16 ## Creating Posts
17
18 To create a post, add a file to your `_posts` directory with the following
19 format:
2720
2821 ```
2922 YEAR-MONTH-DAY-title.MARKUP
3831 2012-09-12-how-to-write-a-blog.md
3932 ```
4033
34 All blog post files must begin with [front matter](/docs/front-matter/) which is
35 typically used to set a [layout](/docs/layouts/) or other meta data. For a simple
36 example this can just be empty:
37
38 ```markdown
39 ---
40 layout: post
41 title: "Welcome to Jekyll!"
42 ---
43
44 # Welcome
45
46 **Hello world**, this is my first Jekyll blog post.
47
48 I hope you like it!
49 ```
50
4151 <div class="note">
4252 <h5>ProTip™: Link to other posts</h5>
4353 <p>
44 Use the <a href="../templates/#linking-to-posts"><code>post_url</code></a>
54 Use the <a href="/docs/liquid/tags/#linking-to-posts"><code>post_url</code></a>
4555 tag to link to other posts without having to worry about the URLs
4656 breaking when the site permalink style changes.
4757 </p>
4858 </div>
49
50 ### Content Formats
51
52 All blog post files must begin with [YAML Front Matter](../frontmatter/). After
53 that, it's simply a matter of deciding which format you prefer. Jekyll supports
54 [Markdown](https://daringfireball.net/projects/markdown/) out of the box,
55 and has [myriad extensions for other formats as well](/docs/plugins/#converters-1),
56 including the popular [Textile](http://redcloth.org/textile) format. These
57 formats each have their own way of marking up different types of content
58 within a post, so you should familiarize yourself with these formats and
59 decide which one best suits your needs.
6059
6160 <div class="note info">
6261 <h5>Be aware of character sets</h5>
7271
7372 ## Including images and resources
7473
75 Chances are, at some point, you'll want to include images, downloads, or other
76 digital assets along with your text content. While the syntax for linking to
77 these resources differs between Markdown and Textile, the problem of working
78 out where to store these files in your site is something everyone will face.
79
80 There are a number of ways to include digital assets in Jekyll.
81 One common solution is to create a folder in the root of the project directory
82 called something like `assets`, into which any images, files
83 or other resources are placed. Then, from within any post, they can be linked
84 to using the site’s root as the path for the asset to include. Again, this will
85 depend on the way your site’s (sub)domain and path are configured, but here are
86 some examples in Markdown of how you could do this using the `absolute_url`
87 filter in a post.
74 At some point, you'll want to include images, downloads, or other
75 digital assets along with your text content. One common solution is to create
76 a folder in the root of the project directory called something like `assets`,
77 into which any images, files or other resources are placed. Then, from within
78 any post, they can be linked to using the site’s root as the path for the asset
79 to include. The best way to do this depends on the way your site’s (sub)domain
80 and path are configured, but here are some simple examples in Markdown:
8881
8982 Including an image asset in a post:
9083
84 ```markdown
85 ... which is shown in the screenshot below:
86 ![My helpful screenshot](/assets/screenshot.jpg)
87 ```
88
89 Linking to a PDF for readers to download:
90
91 ```markdown
92 ... you can [get the PDF](/assets/mydoc.pdf) directly.
93 ```
94
95 ## Displaying an index of posts
96
97 Creating an index of posts on another page should be easy thanks to
98 [Liquid](https://shopify.github.io/liquid/) and its tags. Here’s a
99 simple example of how to create a list of links to your blog posts:
100
91101 {% raw %}
92 ```markdown
93 ... which is shown in the screenshot below:
94 ![My helpful screenshot]({{ "/assets/screenshot.jpg" | absolute_url }})
95 ```
96 {% endraw %}
97
98 Linking to a PDF for readers to download:
99
100 {% raw %}
101 ```markdown
102 ... you can [get the PDF]({{ "/assets/mydoc.pdf" | absolute_url }}) directly.
103 ```
104 {% endraw %}
105
106 <div class="info">
107
108 </div>
109
110 ## A typical post
111
112 Jekyll can handle many different iterations of the idea you might associate with a "post," however a standard blog style post, including a Title, Layout, Publishing Date, and Categories might look like this:
113
114 ```markdown
115 ---
116 layout: post
117 title: "Welcome to Jekyll!"
118 date: 2015-11-17 16:16:01 -0600
119 categories: jekyll update
120 ---
121
122 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.
123
124 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.
125 ```
126
127 Everything in between the first and second `---` are part of the YAML Front Matter, and everything after the second `---` will be rendered with Markdown and show up as "Content".
128
129 ## Displaying an index of posts
130
131 It’s all well and good to have posts in a folder, but a blog is no use unless
132 you have a list of posts somewhere. Creating an index of posts on another page
133 (or in a [template](../templates/)) is easy, thanks to the [Liquid template
134 language](https://docs.shopify.com/themes/liquid/basics) and its tags. Here’s a
135 basic example of how to create a list of links to your blog posts:
136
137 {% raw %}
138 ```html
102 ```liquid
139103 <ul>
140104 {% for post in site.posts %}
141105 <li>
146110 ```
147111 {% endraw %}
148112
149 Of course, you have full control over how (and where) you display your posts,
113 You have full control over how (and where) you display your posts,
150114 and how you structure your site. You should read more about [how templates
151 work](../templates/) with Jekyll if you want to know more.
115 work](/docs/templates/) with Jekyll if you want to know more.
152116
153117 Note that the `post` variable only exists inside the `for` loop above. If
154118 you wish to access the currently-rendering page/posts's variables (the
155119 variables of the post/page that has the `for` loop in it), use the `page`
156120 variable instead.
157121
158 ## Displaying post categories or tags
159
160 Hey, that's pretty neat, but what about showing just some of your posts that are
161 related to each other? For that you can use any of the [variables definable in
162 Front Matter](https://jekyllrb.com/docs/frontmatter/). In the "typical post"
163 section you can see how to define categories. Simply add the categories to your
164 Front Matter as a [yaml
165 list](https://en.wikipedia.org/wiki/YAML#Basic_components).
166
167 Now that your posts have a category or multiple categories, you can make a page
168 or a template displaying just the posts in those categories you specify. Here's
169 a basic example of how to create a list of posts from a specific category.
170
171 First, in the `_layouts` directory create a new file called `category.html` - in
172 that file put (at least) the following:
122 ## Tags and Categories
123
124 Jekyll has first class support for *tags* and *categories* in blog posts.
125
126 ### Tags
127
128 Tags for a post are defined in the post's front matter using either the key
129 `tag` for a single entry or `tags` for multiple entries. <br/> Since Jekyll
130 expects multiple items mapped to the key `tags`, it will automatically *split*
131 a string entry if it contains whitespace. For example, while front matter
132 `tag: classic hollywood` will be processed into a singular entity
133 `"classic hollywood"`, front matter `tags: classic hollywood` will be processed
134 into an array of entries `["classic", "hollywood"]`.
135
136 Irrespective of the front matter key chosen, Jekyll stores the metadata mapped
137 to the plural key which is exposed to Liquid templates.
138
139 All tags registered in the current site are exposed to Liquid templates via
140 `site.tags`. Iterating over `site.tags` on a page will yield another array with
141 two items, where the first item is the name of the tag and the second item being
142 *an array of posts* with that tag.
173143
174144 {% raw %}
175145 ```liquid
176 ---
177 layout: page
178 ---
179
180 {% for post in site.categories[page.category] %}
181 <a href="{{ post.url | absolute_url }}">
182 {{ post.title }}
183 </a>
146 {% for tag in site.tags %}
147 <h3>{{ tag[0] }}</h3>
148 <ul>
149 {% for post in tag[1] %}
150 <li><a href="{{ post.url }}">{{ post.title }}</a></li>
151 {% endfor %}
152 </ul>
184153 {% endfor %}
185154 ```
186155 {% endraw %}
187156
188 Next, in the root directory of your Jekyll install, create a new directory
189 called `category` and then create a file for each category you want to list. For
190 example, if you have a category `blog` then create a file in the new directory
191 called `blog.html` with at least
192
193 ```yaml
194 ---
195 layout: category
196 title: Blog
197 category: blog
198 ---
199 ```
200
201 In this case, the listing pages will be accessible at `{baseurl}/category/blog.html`
202
203 Although categories and tags are very similar, they are used to group posts,
204 there are a few differences between them. Categories and sub-categories create
205 hierarchies that, by default, are reflected in the directory structure of your
206 site. A post with the following header
207 ```yaml
208 ---
209 layout: post
210 title: A Trip
211 category: [ blog, travel ]
212 ---
213 ```
214 will be accessible at `{baseurl}/blog/travel/year/month/day/A-Trip.html`. On
215 the other hand, a tagged post
216 ```yaml
217 ---
218 layout: post
219 title: A Trip
220 tags: [ blog, travel ]
221 ---
222 ```
223 will be saved as `{baseurl}/year/month/day/A-Trip.html`. It is up to you to
224 create `{baseurl}/tag/blog.html` and `{baseurl}/tag/travel.html` the same way as
225 described above for categories.
226
227 While this example is done with tags and categories, you can easily extend your
228 lists to filter by any other variable created with extensions.
157
158 ### Categories
159
160 Categories of a post work similar to the tags above:
161 * They can be defined via the front matter using keys `category` or
162 `categories` (that follow the same logic as for tags)
163 * All categories registered in the site are exposed to Liquid templates via
164 `site.categories` which can be iterated over (similar to the loop for tags
165 above.)
166
167 *The similarity between categories and tags however, ends there.*
168
169 Unlike tags, categories for posts can also be defined by a post's file path.
170 Any directory above `_posts` will be read-in as a category. For example,
171 if a post is at path `movies/horror/_posts/2019-05-21-bride-of-chucky.markdown`,
172 then `movies` and `horror` are automatically registered as categories for that
173 post.
174
175 When the post also has front matter defining categories, they just get added to
176 the existing list if not present already.
177
178 The hallmark difference between categories and tags is that categories of a post
179 may be incorporated into [the generated URL](/docs/permalinks/#global) for the
180 post, while tags cannot be.
181
182 Therefore, depending on whether front matter has `category: classic hollywood`,
183 or `categories: classic hollywood`, the example post above would have the URL as
184 either
185 `movies/horror/classic%20hollywood/2019/05/21/bride-of-chucky.html` or
186 `movies/horror/classic/hollywood/2019/05/21/bride-of-chucky.html` respectively.
187
229188
230189 ## Post excerpts
231190
232 Each post automatically takes the first block of text, from the beginning of
233 the content to the first occurrence of `excerpt_separator`, and sets it in the
234 post's data hash.
235 Take the above example of an index of posts. Perhaps you want to include
236 a little hint about the post's content by adding the first paragraph of each of
237 your posts:
191 You can access a snippet of a posts's content by using `excerpt` variable on a
192 post. By default this is the first paragraph of content in the post, however it
193 can be customized by setting a `excerpt_separator` variable in front matter or
194 `_config.yml`.
195
196 ```markdown
197 ---
198 excerpt_separator: <!--more-->
199 ---
200
201 Excerpt with multiple paragraphs
202
203 Here's another paragraph in the excerpt.
204 <!--more-->
205 Out-of-excerpt
206 ```
207
208 Here's an example of outputting a list of blog posts with an excerpt:
238209
239210 {% raw %}
240211 ```liquid
249220 ```
250221 {% endraw %}
251222
252 Because Jekyll grabs the first paragraph you will not need to wrap the excerpt
253 in `p` tags, which is already done for you. These tags can be removed with the
254 following if you'd prefer:
255
256 {% raw %}
257 ```liquid
258 {{ post.excerpt | remove: '<p>' | remove: '</p>' }}
259 ```
260 {% endraw %}
261
262 If you don't like the automatically-generated post excerpt, it can be
263 explicitly overridden by adding an `excerpt` value to your post's YAML
264 Front Matter. Alternatively, you can choose to define a custom
265 `excerpt_separator` in the post's YAML front matter:
266
267 ```yaml
268 ---
269 excerpt_separator: <!--more-->
270 ---
271
272 Excerpt
273 <!--more-->
274 Out-of-excerpt
275 ```
276
277 You can also set the `excerpt_separator` globally in your `_config.yml`
278 configuration file.
279
280 Completely disable excerpts by setting your `excerpt_separator` to `""`.
281
282 Also, as with any output generated by Liquid tags, you can pass the
283 `| strip_html` filter to remove any html tags in the output. This is
284 particularly helpful if you wish to output a post excerpt as a
285 `meta="description"` tag within the post `head`, or anywhere else having
286 html tags along with the content is not desirable.
287
288 ## Highlighting code snippets
289
290 Jekyll also has built-in support for syntax highlighting of code snippets using
291 either Pygments or Rouge, and including a code snippet in any post is easy.
292 Just use the dedicated Liquid tag as follows:
293
294 {% raw %}
295 ```liquid
296 {% highlight ruby %}
297 def show
298 @widget = Widget(params[:id])
299 respond_to do |format|
300 format.html # show.html.erb
301 format.json { render json: @widget }
302 end
303 end
304 {% endhighlight %}
305 ```
306 {% endraw %}
307
308 And the output will look like this:
309
310 ```ruby
311 def show
312 @widget = Widget(params[:id])
313 respond_to do |format|
314 format.html # show.html.erb
315 format.json { render json: @widget }
316 end
317 end
318 ```
319
320 <div class="note">
321 <h5>ProTip™: Show line numbers</h5>
322 <p>
323 You can make code snippets include line-numbers by adding the word
324 <code>linenos</code> to the end of the opening highlight tag like this:
325 <code>{% raw %}{% highlight ruby linenos %}{% endraw %}</code>.
326 </p>
327 </div>
328
329 These basics should be enough to get you started writing your first posts. When
330 you’re ready to dig into what else is possible, you might be interested in
331 doing things like [customizing post permalinks](../permalinks/) or
332 using [custom variables](../variables/) in your posts and elsewhere on your
333 site.
223 ## Drafts
224
225 Drafts are posts without a date in the filename. They're posts you're still
226 working on and don't want to publish yet. To get up and running with drafts,
227 create a `_drafts` folder in your site's root and create your first draft:
228
229 ```
230 .
231 ├── _drafts
232 │ └── a-draft-post.md
233 ...
234 ```
235
236 To preview your site with drafts, run `jekyll serve` or `jekyll build`
237 with the `--drafts` switch. Each will be assigned the value modification time
238 of the draft file for its date, and thus you will see currently edited drafts
239 as the latest posts.
+0
-51
docs/_docs/quickstart.md less more
0 ---
1 title: Quick-start guide
2 permalink: /docs/quickstart/
3 ---
4
5
6 If you already have a full [Ruby](https://www.ruby-lang.org/en/downloads/) development environment with all headers and [RubyGems](https://rubygems.org/pages/download) installed (see Jekyll's [requirements](/docs/installation/#requirements)), you can create a new Jekyll site by doing the following:
7
8 ```sh
9 # Install Jekyll and Bundler gems through RubyGems
10 gem install jekyll bundler
11
12 # Create a new Jekyll site at ./myblog
13 jekyll new myblog
14
15 # Change into your new directory
16 cd myblog
17
18 # Build the site on the preview server
19 bundle exec jekyll serve
20
21 # Now browse to http://localhost:4000
22 ```
23
24 If you encounter any unexpected errors during the above, please refer to the [troubleshooting](/docs/troubleshooting/#configuration-problems) page or the already-mentioned [requirements](/docs/installation/#requirements) page, as you might be missing development headers or other prerequisites.
25
26 ## About Bundler
27
28 `gem install jekyll bundler` installs the [jekyll](https://rubygems.org/gems/jekyll/) and [bundler](https://rubygems.org/gems/bundler) gems through [RubyGems](https://rubygems.org/). You need only to install the gems one time &mdash; not every time you create a new Jekyll project. Here are some additional details:
29
30 * `bundler` is a gem that manages other Ruby gems. It makes sure your gems and gem versions are compatible, and that you have all necessary dependencies each gem requires.
31 * The `Gemfile` and `Gemfile.lock` files inform Bundler about the gem requirements in your site. If your site doesn't have these Gemfiles, you can omit `bundle exec` and just run `jekyll serve`.
32
33 * When you run `bundle exec jekyll serve`, Bundler uses the gems and versions as specified in `Gemfile.lock` to ensure your Jekyll site builds with no compatibility or dependency conflicts.
34
35 ## Options for creating a new site with Jekyll
36
37 `jekyll new <PATH>` installs a new Jekyll site at the path specified (relative to current directory). In this case, Jekyll will be installed in a directory called `myblog`. Here are some additional details:
38
39 * To install the Jekyll site into the directory you're currently in, run `jekyll new .` If the existing directory isn't empty, you can pass the `--force` option with `jekyll new . --force`.
40 * `jekyll new` automatically initiates `bundle install` to install the dependencies required. (If you don't want Bundler to install the gems, use `jekyll new myblog --skip-bundle`.)
41 * By default, the Jekyll site installed by `jekyll new` uses a gem-based theme called [Minima](https://github.com/jekyll/minima). With [gem-based themes](../themes), some of the directories and files are stored in the theme-gem, hidden from your immediate view.
42 * We recommend setting up Jekyll with a gem-based theme but if you want to start with a blank slate, use `jekyll new myblog --blank`
43 * To learn about other parameters you can include with `jekyll new`, type `jekyll new --help`.
44
45 When in doubt, use the <code>help</code> command to remind you of all available options and usage, it also works with the <code>new</code>, <code>build</code> and <code>serve</code> subcommands, e.g. <code>jekyll help new</code> or <code>jekyll help build</code>.
46 {: .note .info }
47
48 ## Next steps
49
50 Building a Jekyll site with the default theme is just the first step. The real magic happens when you start creating blog posts, using the front matter to control templates and layouts, and taking advantage of all the awesome configuration options Jekyll makes available.
0 ---
1 ---
2
3 For any Jekyll site, a *build session* consists of discrete phases in the following order --- *setting up plugins,
4 reading source files, running generators, rendering templates*, and finally *writing files to disk*.
5
6 While the phases above are self-explanatory, the one phase that warrants dissection is *the rendering phase*.
7
8 The rendering phase is further divisible into three optional stages. Every file rendered, passes through one or more of
9 these stages as determined by the file's content string, front matter and extension. The stages are akin to an assembly
10 line, with the *output* from a stage being the *input* for the succeeding stage:
11 - **Interpreting Liquid expressions in the file**<br/>
12 This stage evaluates Liquid expressions in the current file. By default, the interpretation is *shallow* --- in that
13 any Liquid expression in resulting output is not further interpreted. Moreover, any Liquid expression in the file's
14 front matter is left untouched.
15 - **Unleashing the converters**<br/>
16 This stage invokes the converter mapped to the current file's extension and converts the input string. This is when
17 Markdown gets converted into HTML and Sass / Scss into CSS or CoffeeScript into JavaScript, etc, etc. Since this stage
18 is determined by the file's extension, Markdown or Sass inside a `.html` file will remain untouched.
19 - **Populating the layouts**<br/>
20 By this stage, *the source file* is considered rendered and it will not be revisited. However, based on the file's
21 extension and consequently based on the front matter, it is determined whether to take the *output* string from
22 the preceding stage and place into layouts or not. Whereas output from Sass files or CoffeeScript files are *never*
23 placed into a layout, regular text output can go either ways based on whether a layout has been assigned via the front
24 matter.<br/><br/>
25 Placement into layouts work similar to how Russian dolls encase the smaller ones within itself or how an oyster
26 generates a pearl --- the converted output from the preceding stage forms the core and layout(s) are successively
27 *rendered* separately onto the core.
+0
-62
docs/_docs/resources.md less more
0 ---
1 title: Resources
2 permalink: /docs/resources/
3 ---
4
5 Jekyll's growing use is producing a wide variety of tutorials, frameworks, extensions, examples, and other resources that can be very helpful. Below is a collection of links to some of the most popular Jekyll resources.
6
7 ## Editors
8
9 - [jekyll-atom](https://atom.io/packages/jekyll): A collection of snippets and tools for Jekyll in Atom
10 - [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.
11 - [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).
12 - [vim-jekyll](https://github.com/parkr/vim-jekyll): A vim plugin to generate new posts and run `jekyll build` all without leaving vim.
13 - [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.
14
15 ## Useful Guides
16
17 - [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.
18 - [Jekyll Cheatsheet](https://learn.cloudcannon.com/jekyll-cheat-sheet/) is a single-page resource for Jekyll filters, variables, and the like.
19 - ["Creating and Hosting a Personal Site on GitHub"](http://jmcglone.com/guides/github-pages/)
20 - ['Build A Blog With Jekyll And GitHub Pages' on Smashing Magazine](https://www.smashingmagazine.com/2014/08/01/build-blog-jekyll-github-pages/)
21 - Publishing to GitHub Pages? [Check out our documentation page for just that purpose](/docs/github-pages/).
22 - [Blogging with Git, Emacs and Jekyll](https://metajack.im/2009/01/23/blogging-with-git-emacs-and-jekyll/)
23 - [Tips for working with GitHub Pages Integration](https://gist.github.com/jedschneider/2890453)
24
25 ## Integrations
26
27 - Use a saas service as a backend for forms (contact forms, hiring forms, etc.)
28
29 - [Formspree (open source)](https://formspree.io/)
30 - [FormKeep](https://formkeep.com/guides/contact-form-jekyll?utm_source=github&utm_medium=jekyll-docs&utm_campaign=contact-form-jekyll)
31 - [Simple Form](https://getsimpleform.com/)
32 - [Formingo](https://www.formingo.co/guides/jekyll?utm_source=github&utm_medium=jekyll-docs&utm_campaign=Jekyll%20Documentation)
33 - [Formester](http://www.formester.com)
34 - [Talkyard](https://www.talkyard.io/blog-comments): Embedded comments for Jekyll and others (free and open source, or hosted serverless)
35 - [Staticman](https://staticman.net): Add user-generated content to a Jekyll site (free and open source)
36 - [Snipcart](https://snipcart.com/blog/static-site-e-commerce-part-2-integrating-snipcart-with-jekyll): Add a shopping cart to a Jekyll site
37 - [Contentful](https://www.contentful.com/ecosystem/jekyll/): use Jekyll together with the API-driven Contentful CMS.
38 - [Algolia](https://blog.algolia.com/instant-search-blog-documentation-jekyll-plugin/): Add a powerful instant search to your Jekyll site
39
40 ## Other commentary
41
42 - [How I'm using Jekyll in 2016](https://mademistakes.com/articles/using-jekyll-2016/)
43
44 - [Talkyard comments instructions for Jekyll](https://jekyll-demo.talkyard.io/2018/01/09/installation-instructions.html)
45
46 - [Static Comments with Jekyll & Staticman](https://mademistakes.com/articles/improving-jekyll-static-comments/)
47
48 - [Adding Ajax pagination to Jekyll](https://eduardoboucas.com/blog/2014/11/05/adding-ajax-pagination-to-jekyll.html)
49
50 - ['My Jekyll Fork', by Mike West](https://mikewest.org/2009/11/my-jekyll-fork)
51
52 > "Jekyll is a well-architected throwback to a time before WordPress, when men were men, and HTML was static. I like the ideas it espouses, and have made a few improvements to it's core. Here, I'll point out some highlights of my fork in the hopes that they see usage beyond this site."
53
54 - ['About this Website', by Carter Allen](http://cartera.me/2010/08/12/about-this-website/)
55
56 > "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."
57
58 - [Generating a Tag Cloud in Jekyll](http://www.justkez.com/generating-a-tag-cloud-in-jekyll/) – A guide to implementing a tag cloud and per-tag content pages using Jekyll.
59
60 - 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://wiki.github.com/rfelix/jekyll_ext/extensions) that can be reused and shared.
61 - [Using your Rails layouts in Jekyll](http://numbers.brighterplanet.com/2010/08/09/sharing-rails-views-with-jekyll)
0 ---
1 title: Ruby 101
2 permalink: /docs/ruby-101/
3 ---
4
5 Jekyll is written in Ruby. If you're new to Ruby, this page helps you learn some of the terminology.
6
7 ## Gems
8
9 Gems are code you can include in Ruby projects. Gems package specific functionality. You can share gems across multiple projects or with other people.
10 Gems can perform actions like:
11
12 * Converting a Ruby object to JSON
13 * Pagination
14 * Interacting with APIs such as GitHub
15
16 Jekyll is a gem. Many Jekyll [plugins]({{ '/docs/plugins/' | relative_url }}) are also gems, including
17 [jekyll-feed](https://github.com/jekyll/jekyll-feed),
18 [jekyll-seo-tag](https://github.com/jekyll/jekyll-seo-tag) and
19 [jekyll-archives](https://github.com/jekyll/jekyll-archives).
20
21 ## Gemfile
22
23 A `Gemfile` is a list of gems used by your site. Every Jekyll site has a Gemfile in the main folder.
24
25 For a simple Jekyll site it might look something like this:
26
27 ```ruby
28 source "https://rubygems.org"
29
30 gem "jekyll"
31
32 group :jekyll_plugins do
33 gem "jekyll-feed"
34 gem "jekyll-seo-tag"
35 end
36 ```
37
38 ## Bundler
39
40 [Bundler](https://rubygems.org/gems/bundler) is a gem that installs all gems in your `Gemfile`.
41
42 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.
43
44 Install Bundler using `gem install bundler`. You only need to install it once, not every time you create a new Jekyll project.
45
46 To install gems in your Gemfile using Bundler, run the following in the directory that has the Gemfile:
47
48 ```
49 bundle install
50 bundle exec jekyll serve
51 ```
52
53 To bypass Bundler if you aren't using a Gemfile, run `jekyll serve`.
54
55 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.
0 ---
1 title: Security Policy
2 permalink: "/docs/security/"
3 note: This file is autogenerated. Edit /.github/SECURITY.markdown instead.
4 ---
5
6 ## Supported Versions
7
8 Security updates are applied to the latest MINOR version of Jekyll, and the version used by GitHub Pages, v3.9.x.
9
10 | Version | Supported |
11 | ------- | ------------------ |
12 | 4.2.x | :white_check_mark: |
13 | 3.9.x | :white_check_mark: |
14 | < 3.9.x | :x: |
15
16 ## Reporting a Vulnerability
17
18 Please report vulnerabilities by sending an email to security@jekyllrb.com with the following information:
19
20 1. A description of the vulnerability
21 2. Reproduction steps and/or a sample site (share a private repo to the [Jekyll Security Team](docs/pages/team.md))
22 3. Your contact information
23
24 The Jekyll security team will respond to your submission and notify you whether it has been confirmed by the team.
25 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
26 been closed.
27
28 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
29 use it. If we use your patch, we will attribute authorship to you either as the commit author, or as a co-author.
30
31 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
32 credit in our release post.
33
34 Once the patched version has been released, we will no longer request you to maintain confidentiality and you may choose to share details on
35 how you found the vulnerability with the community.
+0
-22
docs/_docs/sites.md less more
0 ---
1 title: Sites using Jekyll
2 permalink: /docs/sites/
3 ---
4
5 It’s interesting to see what designs and features others have come up
6 with. Below are some Jekyll-powered blogs which were hand-picked for
7 learning purposes.
8
9 - [Tom Preston-Werner](http://tom.preston-werner.com/)
10 ([source](https://github.com/mojombo/mojombo.github.io))
11 - [GitHub Official Teaching Materials](https://services.github.com/training/)
12 ([source](https://github.com/github/training-kit))
13 - [Rasmus Andersson](https://rsms.me/)
14 ([source](https://github.com/rsms/rsms.github.com))
15 - [MvvmCross](https://mvvmcross.github.io/MvvmCross/)
16 ([source](https://github.com/MvvmCross/MvvmCross/tree/master/docs))
17
18 If you would like to explore more examples, you can find a list of sites
19 and their sources on the ["Sites" page in the Jekyll wiki][jekyll-sites].
20
21 [jekyll-sites]: {{ site.repository }}/wiki/Sites
11 title: Static Files
22 permalink: /docs/static-files/
33 ---
4
5 In addition to renderable and convertible content, we also have **static
6 files**.
7
8 A static file is a file that does not contain any YAML front matter. These
4 A static file is a file that does not contain any front matter. These
95 include images, PDFs, and other un-rendered content.
106
117 They're accessible in Liquid via `site.static_files` and contain the
6561 </table>
6662 </div>
6763
68 Note that in the above table, `file` can be anything. It's simply an arbitrarily set variable used in your own logic (such as in a for loop). It isn't a global site or page variable.
64 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.
6965
7066 ## Add front matter to static files
7167
72 Although you can't directly add front matter values to static files, you can set front matter values through the [defaults property](../configuration/#front-matter-defaults) in your configuration file. When Jekyll builds the site, it will use the front matter values you set.
68 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.
7369
7470 Here's an example:
7571
0 ---
1 layout: step
2 title: Setup
3 menu_name: Step by Step Tutorial
4 position: 1
5 ---
6 Welcome to Jekyll's step-by-step tutorial. This tutorial takes
7 you from having some front-end web development experience to building your
8 first Jekyll site from scratch without relying on the default gem-based theme.
9
10 ## Installation
11
12 Jekyll is a Ruby gem. First, install Ruby on your machine.
13 Go to [Installation]({{ '/docs/installation/' | relative_url }}) and follow the
14 instructions for your operating system.
15
16 With Ruby installed, install Jekyll from the terminal:
17
18 ```sh
19 gem install jekyll bundler
20 ```
21
22 Create a new `Gemfile` to list your project's dependencies:
23
24 ```sh
25 bundle init
26 ```
27
28 Edit the `Gemfile` in a text editor and add jekyll as a dependency:
29
30 ```ruby
31 gem "jekyll"
32 ```
33
34 Run `bundle` to install jekyll for your project.
35
36 You can now prefix all jekyll commands listed in this tutorial with `bundle exec`
37 to make sure you use the jekyll version defined in your `Gemfile`.
38
39 ## Create a site
40
41 It's time to create a site! Create a new directory for your site and name
42 it whatever you want. Through the rest of this tutorial we'll refer to this
43 directory as **root**.
44
45 You can also initialize a Git repository here.
46
47 One of the great things about Jekyll is there's no database. All content and
48 site structure are files that a Git repository can version. Using a repository
49 is optional but is recommended. You can learn more
50 about using Git by reading the
51 [Git Handbook](https://guides.github.com/introduction/git-handbook/).
52
53 Let's add your first file. Create `index.html` in **root** with the following
54 content:
55
56 ```html
57 <!DOCTYPE html>
58 <html>
59 <head>
60 <meta charset="utf-8">
61 <title>Home</title>
62 </head>
63 <body>
64 <h1>Hello World!</h1>
65 </body>
66 </html>
67 ```
68
69 ## Build
70
71 Since Jekyll is a static site generator, it has to build the site
72 before we can view it. Run either of the following commands to build your site:
73
74 * `jekyll build` - Builds the site and outputs a static site to a directory
75 called `_site`.
76 * `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.
77
78 {: .note .info}
79 When you're developing a site, use `jekyll serve`. To force the browser to refresh with every change, use `jekyll serve --livereload`.
80 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,
81 as described in the [serve command options]({{ '/docs/configuration/options/#serve-command-options' | relative_url }}).
82
83 {: .note .warning}
84 The version of the site that `jekyll serve` builds in `_site` is not suited for deployment. Links and asset URLs in sites created
85 with `jekyll serve` will use `https://localhost:4000` or the value set with command-line configuration, instead of the values set
86 in [your site's configuration file]({{ '/docs/configuration/' | relative_url }}). To learn about how to build your site when it's
87 ready for deployment, read the [Deployment]({{ '/docs/step-by-step/10-deployment/' | relative_url }}) section of this tutorial.
88
89
90 Run `jekyll serve` and go to
91 <a href="http://localhost:4000" target="_blank" data-proofer-ignore>http://localhost:4000</a> in
92 your browser. You should see "Hello World!".
93
94 At this point, you might be thinking, "So what?". The only thing that happened was that Jekyll copied an
95 HTML file from one place to another.
96
97 Patience, young grasshopper, there's
98 still much to learn!
99
100 Next. you'll learn about Liquid and templating.
0 ---
1 layout: step
2 title: Liquid
3 position: 2
4 ---
5 Liquid is where Jekyll starts to get more interesting. It is a templating
6 language which has three main components:
7 * [objects](#objects)
8 * [tags](#tags)
9 * [filters](#filters)
10
11 ## Objects
12
13 Objects tell Liquid to output predefined [variables](../../variables/) as content on a page. Use double curly braces for objects: {% raw %}`{{`{% endraw %} and {% raw %}`}}`{% endraw %}.
14
15 For example, {% raw %}`{{ page.title }}`{% endraw %} displays the `page.title` variable.
16
17 ## Tags
18
19 Tags define the logic and control flow for templates. Use curly
20 braces and percent signs for tags: {% raw %}`{%`{% endraw %} and
21 {% raw %}`%}`{% endraw %}.
22
23 For example:
24
25 {% raw %}
26 ```liquid
27 {% if page.show_sidebar %}
28 <div class="sidebar">
29 sidebar content
30 </div>
31 {% endif %}
32 ```
33 {% endraw %}
34
35 This displays the sidebar if the value of the `show_sidebar` page variable is true.
36
37 Learn more about the tags available in Jekyll [here](/docs/liquid/tags/).
38
39 ## Filters
40
41 Filters change the output of a Liquid object. They are used within an output
42 and are separated by a `|`.
43
44 For example:
45
46 {% raw %}
47 ```liquid
48 {{ "hi" | capitalize }}
49 ```
50 {% endraw %}
51
52 This displays `Hi` instead of `hi`.
53
54 [Learn more about the filters](/docs/liquid/filters/) available.
55
56 ## Use Liquid
57
58 Now, use Liquid to make your `Hello World!` text from [Setup](../01-setup/) lowercase:
59
60 {% raw %}
61 ```liquid
62 ...
63 <h1>{{ "Hello World!" | downcase }}</h1>
64 ...
65 ```
66 {% endraw %}
67
68 To make Jekyll process your changes, add [front matter](../03-front-matter/) to the top of the page:
69
70 ```yaml
71 ---
72 # front matter tells Jekyll to process Liquid
73 ---
74 ```
75
76 Your HTML document should look like this:
77
78 {% raw %}
79 ```html
80 ---
81 ---
82
83 <!DOCTYPE html>
84 <html>
85 <head>
86 <meta charset="utf-8">
87 <title>Home</title>
88 </head>
89 <body>
90 <h1>{{ "Hello World!" | downcase }}</h1>
91 </body>
92 </html>
93 ```
94 {% endraw %}
95
96 When you reload your browser, you should see `hello world!`.
97
98 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.
99
100 Next, you'll learn more about frontmatter.
0 ---
1 layout: step
2 title: Front Matter
3 position: 3
4 ---
5 Front matter is a snippet of [YAML](http://yaml.org/) placed between two
6 triple-dashed lines at the start of a file.
7
8 You can use front matter to set variables for the page:
9
10 ```yaml
11 ---
12 my_number: 5
13 ---
14 ```
15
16 You can call front matter variables in Liquid using the `page` variable. For
17 example, to output the value of the `my_number` variable above:
18
19 {% raw %}
20 ```liquid
21 {{ page.my_number }}
22 ```
23 {% endraw %}
24
25 ## Use front matter
26
27 Change the `<title>` on your site to use front matter:
28
29 {% raw %}
30 ```liquid
31 ---
32 title: Home
33 ---
34 <!doctype html>
35 <html>
36 <head>
37 <meta charset="utf-8">
38 <title>{{ page.title }}</title>
39 </head>
40 <body>
41 <h1>{{ "Hello World!" | downcase }}</h1>
42 </body>
43 </html>
44 ```
45 {% endraw %}
46
47 {: .note .info }
48 You _must_ include front matter on the page for Jekyll to process any Liquid tags on it.
49
50 To make Jekyll process a page without defining variables in the front matter, use:
51
52 ```yaml
53 ---
54 ---
55 ```
56
57 Next, you'll learn more about layouts and why your pages use more source code than plain HTML.
0 ---
1 layout: step
2 title: Layouts
3 position: 4
4 ---
5 Jekyll supports [Markdown](https://daringfireball.net/projects/markdown/syntax)
6 in addition to HTML when building pages. Markdown is a great choice for pages with a simple
7 content structure (just paragraphs, headings and images), as it's less verbose
8 than raw HTML.
9
10 Create a new Markdown file named `about.md` in your site's root folder.
11
12 You could copy the contents of `index` and modify it for the About page. However,
13 this creates duplicate code that has to be customized for each new page you add
14 to your site.
15
16 For example, adding a new stylesheet to your site would involve adding the link
17 to the stylesheet to the `<head>` of each page. For sites with many pages, this
18 is a waste of time.
19
20 ## Creating a layout
21
22 Layouts are templates that can be used by any page in your site and wrap around page content.
23 They are stored in a directory called `_layouts`.
24
25 Create the `_layouts` directory in your site's root folder and create a new `default.html` file with the following content:
26
27 {% raw %}
28 ```liquid
29 <!doctype html>
30 <html>
31 <head>
32 <meta charset="utf-8">
33 <title>{{ page.title }}</title>
34 </head>
35 <body>
36 {{ content }}
37 </body>
38 </html>
39 ```
40 {% endraw %}
41
42 This HTML is almost identical to `index.html` except there's
43 no front matter and the content of the page is replaced by a `content`
44 variable.
45
46 `content` is a special variable that returns the rendered
47 content of the page on which it's called.
48
49 ## Use layouts
50
51 To make `index.html` use your new layout, set the `layout` variable in the front
52 matter. The file should look like this:
53
54 {% raw %}
55 ```liquid
56 ---
57 layout: default
58 title: Home
59 ---
60 <h1>{{ "Hello World!" | downcase }}</h1>
61 ```
62 {% endraw %}
63
64 When you reload the site, the output remains the same.
65
66 Since the layout wraps around the content on the page, you can call front matter like `page`
67 in the layout file. When you apply the layout to a page, it uses the front matter on that page.
68
69 ## Build the About page
70
71 Add the following to `about.md` to use your new layout in the About page:
72
73 ```markdown
74 ---
75 layout: default
76 title: About
77 ---
78 # About page
79
80 This page tells you a little bit about me.
81 ```
82
83 Open <a href="http://localhost:4000/about.html" target="_blank" data-proofer-ignore>http://localhost:4000/about.html</a>
84 in your browser and view your new page.
85
86 Congratulations, you now have a two page website!
87
88 Next, you'll learn about navigating from page to page in your site.
0 ---
1 layout: step
2 title: Includes
3 position: 5
4 ---
5 The site is coming together; however, there's no way to navigate between
6 pages. Let's fix that.
7
8 Navigation should be on every page so adding it to your layout is the correct
9 place to do this. Instead of adding it directly to the layout, let's use this
10 as an opportunity to learn about includes.
11
12 ## Include tag
13
14 The `include` tag allows you to include content from another file stored
15 in an `_includes` folder. Includes are useful for having a single source for
16 source code that repeats around the site or for improving the readability.
17
18 Navigation source code can get complex, so sometimes it's nice to move it into an
19 include.
20
21 ## Include usage
22
23 Create a file for the navigation at `_includes/navigation.html` with the
24 following content:
25
26 ```
27 <nav>
28 <a href="/">Home</a>
29 <a href="/about.html">About</a>
30 </nav>
31 ```
32
33 Try using the include tag to add the navigation to `_layouts/default.html`:
34
35 {% raw %}
36 ```liquid
37 <!doctype html>
38 <html>
39 <head>
40 <meta charset="utf-8">
41 <title>{{ page.title }}</title>
42 </head>
43 <body>
44 {% include navigation.html %}
45 {{ content }}
46 </body>
47 </html>
48 ```
49 {% endraw %}
50
51 Open <a href="http://localhost:4000" target="_blank" data-proofer-ignore>http://localhost:4000</a>
52 in your browser and try switching between the pages.
53
54 ## Current page highlighting
55
56 Let's take this a step further and highlight the current page in the navigation.
57
58 `_includes/navigation.html` needs to know the URL of the page it's inserted into
59 so it can add styling. Jekyll has useful [variables](/docs/variables/) available,
60 one of which is `page.url`.
61
62 Using `page.url` you can check if each link is the current page and color it red
63 if true:
64
65 {% raw %}
66 ```liquid
67 <nav>
68 <a href="/" {% if page.url == "/" %}style="color: red;"{% endif %}>
69 Home
70 </a>
71 <a href="/about.html" {% if page.url == "/about.html" %}style="color: red;"{% endif %}>
72 About
73 </a>
74 </nav>
75 ```
76 {% endraw %}
77
78 Take a look at <a href="http://localhost:4000" target="_blank" data-proofer-ignore>http://localhost:4000</a>
79 and see your red link for the current page.
80
81 There's still a lot of repetition here if you wanted to add a new item to the
82 navigation or change the highlight color. In the next step we'll address this.
0 ---
1 layout: step
2 title: Data Files
3 position: 6
4 ---
5 Jekyll supports loading data from YAML, JSON, and CSV files located in a `_data`
6 directory. Data files are a great way to separate content from source code to
7 make the site easier to maintain.
8
9 In this step you'll store the contents of the navigation in a data file
10 and then iterate over it in the navigation include.
11
12 ## Data file usage
13
14 [YAML](http://yaml.org/) is a format that's common in the Ruby ecosystem. You'll
15 use it to store an array of navigation items each with a name and link.
16
17 Create a data file for the navigation at `_data/navigation.yml` with the
18 following:
19
20 ```yaml
21 - name: Home
22 link: /
23 - name: About
24 link: /about.html
25 ```
26
27 Jekyll makes this data file available to you at `site.data.navigation`. Instead
28 of outputting each link in `_includes/navigation.html`, now you can iterate over
29 the data file instead:
30
31 {% raw %}
32 ```liquid
33 <nav>
34 {% for item in site.data.navigation %}
35 <a href="{{ item.link }}" {% if page.url == item.link %}style="color: red;"{% endif %}>
36 {{ item.name }}
37 </a>
38 {% endfor %}
39 </nav>
40 ```
41 {% endraw %}
42
43 The output will be exactly the same. The difference is you’ve made it easier to
44 add new navigation items and change the HTML structure.
45
46 What good is a site without CSS, JS and images? Let’s look at how to handle
47 assets in Jekyll.
0 ---
1 layout: step
2 title: Assets
3 position: 7
4 ---
5 Using CSS, JS, images and other assets is straightforward with Jekyll. Place
6 them in your site folder and they’ll copy across to the built site.
7
8 Jekyll sites often use this structure to keep assets organized:
9
10 ```
11 .
12 ├── assets
13 │ ├── css
14 │ ├── images
15 │ └── js
16 ...
17 ```
18 So, from your assets folder, create folders called css, images and js.
19 Additionally, directly under the root create another folder called '_sass', which you will need shortly.
20
21 ## Sass
22
23 Inlining the styles used in `_includes/navigation.html`(adding or configuring within the same file) is not a best practice.
24 Instead, let's style the current page by defining our first class in a new css file instead.
25
26 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:
27
28 {% raw %}
29 ```liquid
30 <nav>
31 {% for item in site.data.navigation %}
32 <a href="{{ item.link }}" {% if page.url == item.link %}class="current"{% endif %}>{{ item.name }}</a>
33 {% endfor %}
34 </nav>
35 ```
36 {% endraw %}
37
38 You could use a standard CSS file for styling, we're going to take it a step
39 further by using [Sass](https://sass-lang.com/). Sass is a fantastic extension
40 to CSS baked right into Jekyll.
41
42 First create a Sass file at `assets/css/styles.scss` with the following content:
43
44 ```sass
45 ---
46 ---
47 @import "main";
48 ```
49
50 The empty front matter at the top tells Jekyll it needs to process the file. The
51 `@import "main"` tells Sass to look for a file called `main.scss` in the sass
52 directory (`_sass/`) by default which you already created directly under the root folder of your website).
53
54 At this stage you'll just have a main css file. For larger projects, this is a
55 great way to keep your CSS organized.
56
57 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:
58
59 ```sass
60 .current {
61 color: green;
62 }
63 ```
64
65 You'll need to reference the stylesheet in your layout.
66
67 Open `_layouts/default.html` and add the stylesheet to the `<head>`:
68
69 {% raw %}
70 ```liquid
71 <!doctype html>
72 <html>
73 <head>
74 <meta charset="utf-8">
75 <title>{{ page.title }}</title>
76 <link rel="stylesheet" href="/assets/css/styles.css">
77 </head>
78 <body>
79 {% include navigation.html %}
80 {{ content }}
81 </body>
82 </html>
83 ```
84 {% endraw %}
85
86 The `styles.css` referenced here is generated by Jekyll from the `styles.scss` you created earlier in `assets/css/`.
87
88 Load up <a href="http://localhost:4000" target="_blank" data-proofer-ignore>http://localhost:4000</a>
89 and check that the active link in the navigation is green.
90
91 Next we're looking at one of Jekyll's most popular features, blogging.
0 ---
1 layout: step
2 title: Blogging
3 position: 8
4 ---
5 You might be wondering how you can have a blog without a database. In true
6 Jekyll style, blogging is powered by text files only.
7
8 ## Posts
9
10 Blog posts live in a folder called `_posts`. The filename for posts have a
11 special format: the publish date, then a title, followed by an extension.
12
13 Create your first post at `_posts/2018-08-20-bananas.md` with the
14 following content:
15
16 ```markdown
17 ---
18 layout: post
19 author: jill
20 ---
21 A banana is an edible fruit – botanically a berry – produced by several kinds
22 of large herbaceous flowering plants in the genus Musa.
23
24 In some countries, bananas used for cooking may be called "plantains",
25 distinguishing them from dessert bananas. The fruit is variable in size, color,
26 and firmness, but is usually elongated and curved, with soft flesh rich in
27 starch covered with a rind, which may be green, yellow, red, purple, or brown
28 when ripe.
29 ```
30
31 This is like the `about.md` you created before except it has an author and
32 a different layout. `author` is a custom variable, it's not required and could
33 have been named something like `creator`.
34
35 ## Layout
36
37 The `post` layout doesn't exist so you'll need to create it at
38 `_layouts/post.html` with the following content:
39
40 {% raw %}
41 ```liquid
42 ---
43 layout: default
44 ---
45 <h1>{{ page.title }}</h1>
46 <p>{{ page.date | date_to_string }} - {{ page.author }}</p>
47
48 {{ content }}
49 ```
50 {% endraw %}
51
52 This is an example of layout inheritance. The post layout outputs the title,
53 date, author and content body which is wrapped by the default layout.
54
55 Also note the `date_to_string` filter, this formats a date into a nicer format.
56
57 ## List posts
58
59 There's currently no way to navigate to the blog post. Typically a blog has a
60 page which lists all the posts, let's do that next.
61
62 Jekyll makes posts available at `site.posts`.
63
64 Create `blog.html` in your root (`/blog.html`) with the following content:
65
66 {% raw %}
67 ```liquid
68 ---
69 layout: default
70 title: Blog
71 ---
72 <h1>Latest Posts</h1>
73
74 <ul>
75 {% for post in site.posts %}
76 <li>
77 <h2><a href="{{ post.url }}">{{ post.title }}</a></h2>
78 {{ post.excerpt }}
79 </li>
80 {% endfor %}
81 </ul>
82 ```
83 {% endraw %}
84
85 There's a few things to note with this code:
86
87 * `post.url` is automatically set by Jekyll to the output path of the post
88 * `post.title` is pulled from the post filename and can be overridden by
89 setting `title` in front matter
90 * `post.excerpt` is the first paragraph of content by default
91
92 You also need a way to navigate to this page through the main navigation. Open
93 `_data/navigation.yml` and add an entry for the blog page:
94
95 ```yaml
96 - name: Home
97 link: /
98 - name: About
99 link: /about.html
100 - name: Blog
101 link: /blog.html
102 ```
103
104 ## More posts
105
106 A blog isn't very exciting with a single post. Add a few more:
107
108 `_posts/2018-08-21-apples.md`:
109
110 ```markdown
111 ---
112 layout: post
113 author: jill
114 ---
115 An apple is a sweet, edible fruit produced by an apple tree.
116
117 Apple trees are cultivated worldwide, and are the most widely grown species in
118 the genus Malus. The tree originated in Central Asia, where its wild ancestor,
119 Malus sieversii, is still found today. Apples have been grown for thousands of
120 years in Asia and Europe, and were brought to North America by European
121 colonists.
122 ```
123
124 `_posts/2018-08-22-kiwifruit.md`:
125
126 ```markdown
127 ---
128 layout: post
129 author: ted
130 ---
131 Kiwifruit (often abbreviated as kiwi), or Chinese gooseberry is the edible
132 berry of several species of woody vines in the genus Actinidia.
133
134 The most common cultivar group of kiwifruit is oval, about the size of a large
135 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
136 diameter). It has a fibrous, dull greenish-brown skin and bright green or
137 golden flesh with rows of tiny, black, edible seeds. The fruit has a soft
138 texture, with a sweet and unique flavor.
139 ```
140
141 Open <a href="http://localhost:4000" target="_blank" data-proofer-ignore>http://localhost:4000</a>
142 and have a look through your blog posts.
143
144 Next we'll focus on creating a page for each post author.
0 ---
1 layout: step
2 title: Collections
3 position: 9
4 ---
5 Let's look at fleshing out authors so each author has their own page with a
6 blurb and the posts they've published.
7
8 To do this you'll use collections. Collections are similar to posts except the
9 content doesn't have to be grouped by date.
10
11 ## Configuration
12
13 To set up a collection you need to tell Jekyll about it. Jekyll configuration
14 happens in a file called `_config.yml` (by default).
15
16 Create `_config.yml` in the root with the following:
17
18 ```yaml
19 collections:
20 authors:
21 ```
22
23 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.
24
25 ## Add authors
26
27 Documents (the items in a collection) live in a folder in the root of the site
28 named `_*collection_name*`. In this case, `_authors`.
29
30 Create a document for each author:
31
32 `_authors/jill.md`:
33
34 ```markdown
35 ---
36 short_name: jill
37 name: Jill Smith
38 position: Chief Editor
39 ---
40 Jill is an avid fruit grower based in the south of France.
41 ```
42
43 `_authors/ted.md`:
44
45 ```markdown
46 ---
47 short_name: ted
48 name: Ted Doe
49 position: Writer
50 ---
51 Ted has been eating fruit since he was baby.
52 ```
53
54 ## Staff page
55
56 Let's add a page which lists all the authors on the site. Jekyll makes the
57 collection available at `site.authors`.
58
59 Create `staff.html` and iterate over `site.authors` to output all the staff:
60
61 {% raw %}
62 ```liquid
63 ---
64 layout: default
65 title: Staff
66 ---
67 <h1>Staff</h1>
68
69 <ul>
70 {% for author in site.authors %}
71 <li>
72 <h2>{{ author.name }}</h2>
73 <h3>{{ author.position }}</h3>
74 <p>{{ author.content | markdownify }}</p>
75 </li>
76 {% endfor %}
77 </ul>
78 ```
79 {% endraw %}
80
81 Since the content is markdown, you need to run it through the
82 `markdownify` filter. This happens automatically when outputting using
83 {% raw %}`{{ content }}`{% endraw %} in a layout.
84
85 You also need a way to navigate to this page through the main navigation. Open
86 `_data/navigation.yml` and add an entry for the staff page:
87
88 ```yaml
89 - name: Home
90 link: /
91 - name: About
92 link: /about.html
93 - name: Blog
94 link: /blog.html
95 - name: Staff
96 link: /staff.html
97 ```
98
99 ## Output a page
100
101 By default, collections do not output a page for documents. In this case we
102 want each author to have their own page so let's tweak the collection
103 configuration.
104
105 Open `_config.yml` and add `output: true` to the author collection
106 configuration:
107
108 ```yaml
109 collections:
110 authors:
111 output: true
112 ```
113
114 Restart the jekyll server once more for the configuration changes to take effect.
115
116 You can link to the output page using `author.url`.
117
118 Add the link to the `staff.html` page:
119
120 {% raw %}
121 ```liquid
122 ---
123 layout: default
124 title: Staff
125 ---
126 <h1>Staff</h1>
127
128 <ul>
129 {% for author in site.authors %}
130 <li>
131 <h2><a href="{{ author.url }}">{{ author.name }}</a></h2>
132 <h3>{{ author.position }}</h3>
133 <p>{{ author.content | markdownify }}</p>
134 </li>
135 {% endfor %}
136 </ul>
137 ```
138 {% endraw %}
139
140 Just like posts you'll need to create a layout for authors.
141
142 Create `_layouts/author.html` with the following content:
143
144 {% raw %}
145 ```liquid
146 ---
147 layout: default
148 ---
149 <h1>{{ page.name }}</h1>
150 <h2>{{ page.position }}</h2>
151
152 {{ content }}
153 ```
154 {% endraw %}
155
156 ## Front matter defaults
157
158 Now you need to configure the author documents to use the `author` layout. You
159 could do this in the front matter like we have previously but that's getting
160 repetitive.
161
162 What you really want is all posts to automatically have the post
163 layout, authors to have author and everything else to use the default.
164
165 You can achieve this by using [front matter defaults](/docs/configuration/front-matter-defaults/)
166 in `_config.yml`. You set a scope of what the default applies to, then the
167 default front matter you'd like.
168
169 Add defaults for layouts to your `_config.yml`,
170
171 ```yaml
172 collections:
173 authors:
174 output: true
175
176 defaults:
177 - scope:
178 path: ""
179 type: "authors"
180 values:
181 layout: "author"
182 - scope:
183 path: ""
184 type: "posts"
185 values:
186 layout: "post"
187 - scope:
188 path: ""
189 values:
190 layout: "default"
191 ```
192
193 Now you can remove layout from the front matter of all pages and posts. Note
194 that any time you update `_config.yml` you'll need to restart Jekyll for the
195 changes to take effect.
196
197 ## List author's posts
198
199 Let's list the posts an author has published on their page. To do
200 this you need to match the author `short_name` to the post `author`. You
201 use this to filter the posts by author.
202
203 Iterate over this filtered list in `_layouts/author.html` to output the
204 author's posts:
205
206 {% raw %}
207 ```liquid
208 ---
209 layout: default
210 ---
211 <h1>{{ page.name }}</h1>
212 <h2>{{ page.position }}</h2>
213
214 {{ content }}
215
216 <h2>Posts</h2>
217 <ul>
218 {% assign filtered_posts = site.posts | where: 'author', page.short_name %}
219 {% for post in filtered_posts %}
220 <li><a href="{{ post.url }}">{{ post.title }}</a></li>
221 {% endfor %}
222 </ul>
223 ```
224 {% endraw %}
225
226 ## Link to authors page
227
228 The posts have a reference to the author so let's link it to the author's page.
229 You can do this using a similar filtering technique in `_layouts/post.html`:
230
231 {% raw %}
232 ```liquid
233 ---
234 layout: default
235 ---
236 <h1>{{ page.title }}</h1>
237
238 <p>
239 {{ page.date | date_to_string }}
240 {% assign author = site.authors | where: 'short_name', page.author | first %}
241 {% if author %}
242 - <a href="{{ author.url }}">{{ author.name }}</a>
243 {% endif %}
244 </p>
245
246 {{ content }}
247 ```
248 {% endraw %}
249
250 Open up <a href="http://localhost:4000" target="_blank" data-proofer-ignore>http://localhost:4000</a> and
251 have a look at the staff page and the author links on posts to check everything
252 is linked together correctly.
253
254 In the next and final step of this tutorial, we'll add polish to the site and
255 get it ready for a production deployment.
0 ---
1 layout: step
2 title: Deployment
3 position: 10
4 ---
5 In this final step we'll get the site ready for production.
6
7 ## Gemfile
8
9 It's good practice to have a [Gemfile](/docs/ruby-101/#gemfile) for your site.
10 This ensures the version of Jekyll and other gems remains consistent across
11 different environments.
12
13 Create a `Gemfile` in the root.
14 The file should be called 'Gemfile' and should *not* have any extension.
15 You can create a Gemfile with Bundler and then add the `jekyll` gem:
16
17 ```sh
18 bundle init
19 bundle add jekyll
20 ```
21
22 Your file should look something like:
23
24 ```ruby
25 # frozen_string_literal: true
26 source "https://rubygems.org"
27
28 gem "jekyll"
29 ```
30
31 Bundler installs the gems and creates a `Gemfile.lock` which locks the current
32 gem versions for a future `bundle install`. If you ever want to update your gem
33 versions you can run `bundle update`.
34
35 When using a `Gemfile`, you'll run commands like `jekyll serve` with
36 `bundle exec` prefixed. So the full command is:
37
38 ```sh
39 bundle exec jekyll serve
40 ```
41
42 This restricts your Ruby environment to only use gems set in your `Gemfile`.
43
44 ## Plugins
45
46 Jekyll plugins allow you to create custom generated content specific to your
47 site. There's many [plugins](/docs/plugins/) available or you can even
48 write your own.
49
50 There are three official plugins which are useful on almost any Jekyll site:
51
52 * [jekyll-sitemap](https://github.com/jekyll/jekyll-sitemap) - Creates a sitemap
53 file to help search engines index content
54 * [jekyll-feed](https://github.com/jekyll/jekyll-feed) - Creates an RSS feed for
55 your posts
56 * [jekyll-seo-tag](https://github.com/jekyll/jekyll-seo-tag) - Adds meta tags to help
57 with SEO
58
59 To use these first you need to add them to your `Gemfile`. If you put them
60 in a `jekyll_plugins` group they'll automatically be required into Jekyll:
61
62 ```ruby
63 source 'https://rubygems.org'
64
65 gem 'jekyll'
66
67 group :jekyll_plugins do
68 gem 'jekyll-sitemap'
69 gem 'jekyll-feed'
70 gem 'jekyll-seo-tag'
71 end
72 ```
73
74 Then add these lines to your `_config.yml`:
75
76 ```yaml
77 plugins:
78 - jekyll-feed
79 - jekyll-sitemap
80 - jekyll-seo-tag
81 ```
82
83 Now install them by running a `bundle update`.
84
85 `jekyll-sitemap` doesn't need any setup, it will create your sitemap on build.
86
87 For `jekyll-feed` and `jekyll-seo-tag` you need to add tags to
88 `_layouts/default.html`:
89
90 {% raw %}
91 ```liquid
92 <!doctype html>
93 <html>
94 <head>
95 <meta charset="utf-8">
96 <title>{{ page.title }}</title>
97 <link rel="stylesheet" href="/assets/css/styles.css">
98 {% feed_meta %}
99 {% seo %}
100 </head>
101 <body>
102 {% include navigation.html %}
103 {{ content }}
104 </body>
105 </html>
106 ```
107 {% endraw %}
108
109 Restart your Jekyll server and check these tags are added to the `<head>`.
110
111 ## Environments
112
113 Sometimes you might want to output something in production but not
114 in development. Analytics scripts are the most common example of this.
115
116 To do this you can use [environments](/docs/configuration/environments/). You
117 can set the environment by using the `JEKYLL_ENV` environment variable when
118 running a command. For example:
119
120 ```sh
121 JEKYLL_ENV=production bundle exec jekyll build
122 ```
123
124 By default `JEKYLL_ENV` is development. The `JEKYLL_ENV` is available to you
125 in liquid using `jekyll.environment`. So to only output the analytics script
126 on production you would do the following:
127
128 {% raw %}
129 ```liquid
130 {% if jekyll.environment == "production" %}
131 <script src="my-analytics-script.js"></script>
132 {% endif %}
133 ```
134 {% endraw %}
135
136 ## Deployment
137
138 The final step is to get the site onto a production server. The most basic way
139 to do this is to run a production build:
140
141 ```sh
142 JEKYLL_ENV=production bundle exec jekyll build
143 ```
144
145 And then copy the contents of `_site` to your server.
146
147 <div class="note warning">
148 <h5>Destination folders are cleaned on site builds</h5>
149 <p>
150 The contents of <code>_site</code> are automatically cleaned, by default, when
151 the site is built. Files or folders that are not created by your site's build
152 process will be removed.
153 </p>
154 <p>
155 Some files could be retained by specifying them within the <code>keep_files</code>
156 configuration directive. Other files could be retained by keeping them in your
157 assets directory.
158 </p>
159 </div>
160
161 A better way is to automate this process using a [CI](/docs/deployment/automated/)
162 or [3rd party](/docs/deployment/third-party/).
163
164 ## Wrap up
165
166 That brings us to the end of this step-by-step tutorial and the beginning of
167 your Jekyll journey!
168
169 * Come say hi to the [community forums](https://talk.jekyllrb.com)
170 * Help us make Jekyll better by [contributing](/docs/contributing/)
171 * Keep building Jekyll sites!
00 ---
1 title: Directory structure
1 title: Directory Structure
22 permalink: /docs/structure/
33 ---
4
5 Jekyll is, at its core, a text transformation engine. The concept behind the
6 system is this: you give it text written in your favorite markup language, be
7 that Markdown, Textile, or just plain HTML, and it churns that through a layout
8 or a series of layout files. Throughout that process you can tweak how you want
9 the site URLs to look, what data gets displayed in the layout, and more. This
10 is all done through editing text files; the static web site is the final
11 product.
12
134 A basic Jekyll site usually looks something like this:
145
15 ```sh
6 ```
167 .
178 ├── _config.yml
189 ├── _data
19 | └── members.yml
10 │ └── members.yml
2011 ├── _drafts
21 | ├── begin-with-the-crazy-ideas.md
22 | └── on-simplicity-in-technology.md
12 │ ├── begin-with-the-crazy-ideas.md
13 │ └── on-simplicity-in-technology.md
2314 ├── _includes
24 | ├── footer.html
25 | └── header.html
15 │ ├── footer.html
16 │ └── header.html
2617 ├── _layouts
27 | ├── default.html
28 | └── post.html
18 │ ├── default.html
19 │ └── post.html
2920 ├── _posts
30 | ├── 2007-10-29-why-every-programmer-should-play-nethack.md
31 | └── 2009-04-26-barcamp-boston-4-roundup.md
21 │ ├── 2007-10-29-why-every-programmer-should-play-nethack.md
22 │ └── 2009-04-26-barcamp-boston-4-roundup.md
3223 ├── _sass
33 | ├── _base.scss
34 | └── _layout.scss
24 │ ├── _base.scss
25 │ └── _layout.scss
3526 ├── _site
27 ├── .jekyll-cache
28 │ └── Jekyll
29 │ └── Cache
30 │ └── [...]
3631 ├── .jekyll-metadata
37 └── index.html # can also be an 'index.md' with valid YAML Frontmatter
32 └── index.html # can also be an 'index.md' with valid front matter
3833 ```
3934
40 <div class="note info">
35 <div class="note">
4136 <h5>Directory structure of Jekyll sites using gem-based themes</h5>
4237 <p>
43 Starting <strong>Jekyll 3.2</strong>, a new Jekyll project bootstrapped with <code>jekyll new</code> uses <a href="../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.
38 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.
4439 </p>
4540 <br />
4641 <p>
47 <a href="https://github.com/jekyll/minima">minima</a> is the current default theme, and <code>bundle show minima</code> will show you where minima theme's files are stored on your computer.
42 <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.
4843 </p>
4944 </div>
5045
6560 </td>
6661 <td>
6762 <p>
68 Stores <a href="../configuration/">configuration</a> data. Many of
63 Stores <a href="/docs/configuration/">configuration</a> data. Many of
6964 these options can be specified from the command line executable but
7065 it’s easier to specify them here so you don’t have to remember them.
7166 </p>
7873 <td>
7974 <p>
8075 Drafts are unpublished posts. The format of these files is without a
81 date: <code>title.MARKUP</code>. Learn how to <a href="../drafts/">
76 date: <code>title.MARKUP</code>. Learn how to <a href="/docs/posts/#drafts">
8277 work with drafts</a>.
8378 </p>
8479 </td>
105100 <p>
106101 These are the templates that wrap posts. Layouts are chosen on a
107102 post-by-post basis in the
108 <a href="../frontmatter/">YAML Front Matter</a>,
103 <a href="/docs/front-matter/">front matter</a>,
109104 which is described in the next section. The liquid tag
110105 <code>{% raw %}{{ content }}{% endraw %}</code>
111106 is used to inject content into the web page.
121116 Your dynamic content, so to speak. The naming convention of these
122117 files is important, and must follow the format:
123118 <code>YEAR-MONTH-DAY-title.MARKUP</code>.
124 The <a href="../permalinks/">permalinks</a> can be customized for
119 The <a href="/docs/permalinks/">permalinks</a> can be customized for
125120 each post, but the date and markup language are determined solely by
126121 the file name.
127122 </p>
135130 <p>
136131 Well-formatted site data should be placed here. The Jekyll engine
137132 will autoload all data files (using either the <code>.yml</code>,
138 <code>.yaml</code>, <code>.json</code> or <code>.csv</code>
139 formats and extensions) in this directory, and they will be
140 accessible via `site.data`. If there's a file
133 <code>.yaml</code>, <code>.json</code>, <code>.csv</code> or
134 <code>.tsv</code> formats and extensions) in this directory,
135 and they will be accessible via `site.data`. If there's a file
141136 <code>members.yml</code> under the directory, then you can access
142137 contents of the file through <code>site.data.members</code>.
143138 </p>
152147 These are sass partials that can be imported into your <code>main.scss</code>
153148 which will then be processed into a single stylesheet
154149 <code>main.css</code> that defines the styles to be used by your site.
150 Learn <a href="{{ '/docs/assets/' | relative_url }}">how to work with assets</a>.
155151 </p>
156152 </td>
157153 </tr>
169165 </tr>
170166 <tr>
171167 <td>
168 <p><code>.jekyll-cache</code></p>
169 </td>
170 <td>
171 <p>
172 Keeps a copy of the generated pages and markup (e.g.: markdown) for
173 faster serving. Created when using e.g.: <code>jekyll serve</code>.
174 Can be disabled with
175 <a href="/docs/configuration/options/">an option and/or flag</a>.
176 This directory will not be included in the generated site. It’s
177 probably a good idea to add this to your <code>.gitignore</code>
178 file.
179 </p>
180 </td>
181 </tr>
182 <tr>
183 <td>
172184 <p><code>.jekyll-metadata</code></p>
173185 </td>
174186 <td>
175187 <p>
176188 This helps Jekyll keep track of which files have not been modified
177189 since the site was last built, and which files will need to be
178 regenerated on the next build. This file will not be included in the
179 generated site. It’s probably a good idea to add this to your
180 <code>.gitignore</code> file.
190 regenerated on the next build. Only created when using
191 <a href="/docs/configuration/incremental-regeneration/">
192 incremental regeneration</a> (e.g.: with <code>jekyll serve -I</code>).
193 This file will not be included in the generated site. It’s probably
194 a good idea to add this to your <code>.gitignore</code> file.
181195 </p>
182196 </td>
183197 </tr>
184198 <tr>
185199 <td>
186200 <p><code>index.html</code> or <code>index.md</code> and other HTML,
187 Markdown, Textile files</p>
188 </td>
189 <td>
190 <p>
191 Provided that the file has a <a href="../frontmatter/">YAML Front
192 Matter</a> section, it will be transformed by Jekyll. The same will
201 Markdown files</p>
202 </td>
203 <td>
204 <p>
205 Provided that the file has a <a href="/docs/front-matter/">front
206 matter</a> section, it will be transformed by Jekyll. The same will
193207 happen for any <code>.html</code>, <code>.markdown</code>,
194208 <code>.md</code>, or <code>.textile</code> file in your site’s root
195209 directory or directories not listed above.
202216 </td>
203217 <td>
204218 <p>
205 Every other directory and file except for those listed above—such as
206 <code>css</code> and <code>images</code> folders,
219 Except for the special cases listed above, every other directory and
220 file—such as <code>css</code> and <code>images</code> folders,
207221 <code>favicon.ico</code> files, and so forth—will be copied verbatim
208 to the generated site. There are plenty of <a href="../sites/">sites
222 to the generated site. There are plenty of <a href="/showcase/">sites
209223 already using Jekyll</a> if you’re curious to see how they’re laid
210224 out.
211225 </p>
214228 </tbody>
215229 </table>
216230 </div>
231
232 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:
233
234 ```yaml
235 include:
236 - _pages
237 - .htaccess
238 ```
+0
-615
docs/_docs/templates.md less more
0 ---
1 title: Templates
2 permalink: /docs/templates/
3 ---
4
5 Jekyll uses the [Liquid](https://shopify.github.io/liquid/) templating language to
6 process templates. All of the standard Liquid [tags](https://shopify.github.io/liquid/tags/control-flow/) and
7 [filters](https://shopify.github.io/liquid/filters/abs/) are
8 supported. To make common tasks easier, Jekyll even adds a few handy filters
9 and tags of its own, all of which you can find on this page. Jekyll even lets
10 you come up with your own tags via plugins.
11
12 ## Filters
13
14 <div class="mobile-side-scroller">
15 <table>
16 <thead>
17 <tr>
18 <th>Description</th>
19 <th><span class="filter">Filter</span> and <span class="output">Output</span></th>
20 </tr>
21 </thead>
22 <tbody>
23 <tr>
24 <td>
25 <p class="name"><strong>Relative URL</strong></p>
26 <p>Prepend the <code>baseurl</code> value to the input. Useful if your site is hosted at a subpath rather than the root of the domain.</p>
27 </td>
28 <td class="align-center">
29 <p>
30 <code class="filter">{% raw %}{{ "/assets/style.css" | relative_url }}{% endraw %}</code>
31 </p>
32 <p>
33 <code class="output">/my-baseurl/assets/style.css</code>
34 </p>
35 </td>
36 </tr>
37 <tr>
38 <td>
39 <p class="name"><strong>Absolute URL</strong></p>
40 <p>Prepend the <code>url</code> and <code>baseurl</code> value to the input.</p>
41 </td>
42 <td class="align-center">
43 <p>
44 <code class="filter">{% raw %}{{ "/assets/style.css" | absolute_url }}{% endraw %}</code>
45 </p>
46 <p>
47 <code class="output">http://example.com/my-baseurl/assets/style.css</code>
48 </p>
49 </td>
50 </tr>
51 <tr>
52 <td>
53 <p class="name"><strong>Date to XML Schema</strong></p>
54 <p>Convert a Date into XML Schema (ISO 8601) format.</p>
55 </td>
56 <td class="align-center">
57 <p>
58 <code class="filter">{% raw %}{{ site.time | date_to_xmlschema }}{% endraw %}</code>
59 </p>
60 <p>
61 <code class="output">2008-11-07T13:07:54-08:00</code>
62 </p>
63 </td>
64 </tr>
65 <tr>
66 <td>
67 <p class="name"><strong>Date to RFC-822 Format</strong></p>
68 <p>Convert a Date into the RFC-822 format used for RSS feeds.</p>
69 </td>
70 <td class="align-center">
71 <p>
72 <code class="filter">{% raw %}{{ site.time | date_to_rfc822 }}{% endraw %}</code>
73 </p>
74 <p>
75 <code class="output">Mon, 07 Nov 2008 13:07:54 -0800</code>
76 </p>
77 </td>
78 </tr>
79 <tr>
80 <td>
81 <p class="name"><strong>Date to String</strong></p>
82 <p>Convert a date to short format.</p>
83 </td>
84 <td class="align-center">
85 <p>
86 <code class="filter">{% raw %}{{ site.time | date_to_string }}{% endraw %}</code>
87 </p>
88 <p>
89 <code class="output">07 Nov 2008</code>
90 </p>
91 </td>
92 </tr>
93 <tr>
94 <td>
95 <p class="name"><strong>Date to String in ordinal US style</strong></p>
96 <p>Format a date to ordinal, US, short format.</p>
97 </td>
98 <td class="align-center">
99 <p>
100 <code class="filter">{% raw %}{{ site.time | date_to_string: "ordinal", "US" }}{% endraw %}</code>
101 </p>
102 <p>
103 <code class="output">Nov 7th, 2008</code>
104 </p>
105 </td>
106 </tr>
107 <tr>
108 <td>
109 <p class="name"><strong>Date to Long String</strong></p>
110 <p>Format a date to long format.</p>
111 </td>
112 <td class="align-center">
113 <p>
114 <code class="filter">{% raw %}{{ site.time | date_to_long_string }}{% endraw %}</code>
115 </p>
116 <p>
117 <code class="output">07 November 2008</code>
118 </p>
119 </td>
120 </tr>
121 <tr>
122 <td>
123 <p class="name"><strong>Date to Long String in ordinal UK style</strong></p>
124 <p>Format a date to ordinal, UK, long format.</p>
125 </td>
126 <td class="align-center">
127 <p>
128 <code class="filter">{% raw %}{{ site.time | date_to_long_string: "ordinal" }}{% endraw %}</code>
129 </p>
130 <p>
131 <code class="output">7th November 2008</code>
132 </p>
133 </td>
134 </tr>
135 <tr>
136 <td>
137 <p class="name"><strong>Where</strong></p>
138 <p>Select all the objects in an array where the key has the given value.</p>
139 </td>
140 <td class="align-center">
141 <p>
142 <code class="filter">{% raw %}{{ site.members | where:"graduation_year","2014" }}{% endraw %}</code>
143 </p>
144 </td>
145 </tr>
146 <tr>
147 <td>
148 <p class="name"><strong>Where Expression</strong></p>
149 <p>Select all the objects in an array where the expression is true. Jekyll v3.2.0 & later.</p>
150 </td>
151 <td class="align-center">
152 <p>
153 <code class="filter">{% raw %}{{ site.members | where_exp:"item",
154 "item.graduation_year == 2014" }}{% endraw %}</code>
155 <code class="filter">{% raw %}{{ site.members | where_exp:"item",
156 "item.graduation_year < 2014" }}{% endraw %}</code>
157 <code class="filter">{% raw %}{{ site.members | where_exp:"item",
158 "item.projects contains 'foo'" }}{% endraw %}</code>
159 </p>
160 </td>
161 </tr>
162 <tr>
163 <td>
164 <p class="name"><strong>Group By</strong></p>
165 <p>Group an array's items by a given property.</p>
166 </td>
167 <td class="align-center">
168 <p>
169 <code class="filter">{% raw %}{{ site.members | group_by:"graduation_year" }}{% endraw %}</code>
170 </p>
171 <p>
172 <code class="output">[{"name"=>"2013", "items"=>[...]},
173 {"name"=>"2014", "items"=>[...]}]</code>
174 </p>
175 </td>
176 </tr>
177 <tr>
178 <td>
179 <p class="name"><strong>Group By Expression</strong></p>
180 <p>Group an array's items using a Liquid expression.</p>
181 </td>
182 <td class="align-center">
183 <p>
184 <code class="filter">{% raw %}{{ site.members | group_by_exp:"item",
185 "item.graduation_year | truncate: 3, \"\"" }}{% endraw %}</code>
186 </p>
187 <p>
188 <code class="output">[{"name"=>"201...", "items"=>[...]},
189 {"name"=>"200...", "items"=>[...]}]</code>
190 </p>
191 </td>
192 </tr>
193 <tr>
194 <td>
195 <p class="name"><strong>XML Escape</strong></p>
196 <p>Escape some text for use in XML.</p>
197 </td>
198 <td class="align-center">
199 <p>
200 <code class="filter">{% raw %}{{ page.content | xml_escape }}{% endraw %}</code>
201 </p>
202 </td>
203 </tr>
204 <tr>
205 <td>
206 <p class="name"><strong>CGI Escape</strong></p>
207 <p>
208 CGI escape a string for use in a URL. Replaces any special characters
209 with appropriate <code>%XX</code> replacements. CGI escape normally replaces a space with a plus <code>+</code> sign.
210 </p>
211 </td>
212 <td class="align-center">
213 <p>
214 <code class="filter">{% raw %}{{ "foo, bar; baz?" | cgi_escape }}{% endraw %}</code>
215 </p>
216 <p>
217 <code class="output">foo%2C+bar%3B+baz%3F</code>
218 </p>
219 </td>
220 </tr>
221 <tr>
222 <td>
223 <p class="name"><strong>URI Escape</strong></p>
224 <p>
225 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.
226 </p>
227 </td>
228 <td class="align-center">
229 <p>
230 <code class="filter">{% raw %}{{ "http://foo.com/?q=foo, \bar?" | uri_escape }}{% endraw %}</code>
231 </p>
232 <p>
233 <code class="output">http://foo.com/?q=foo,%20%5Cbar?</code>
234 </p>
235 </td>
236 </tr>
237 <tr>
238 <td>
239 <p class="name"><strong>Number of Words</strong></p>
240 <p>Count the number of words in some text.</p>
241 </td>
242 <td class="align-center">
243 <p>
244 <code class="filter">{% raw %}{{ page.content | number_of_words }}{% endraw %}</code>
245 </p>
246 <p>
247 <code class="output">1337</code>
248 </p>
249 </td>
250 </tr>
251 <tr>
252 <td>
253 <p class="name"><strong>Array to Sentence</strong></p>
254 <p>Convert an array into a sentence. Useful for listing tags. Optional argument for connector.</p>
255 </td>
256 <td class="align-center">
257 <p>
258 <code class="filter">{% raw %}{{ page.tags | array_to_sentence_string }}{% endraw %}</code>
259 </p>
260 <p>
261 <code class="output">foo, bar, and baz</code>
262 </p>
263 <p>
264 <code class="filter">{% raw %}{{ page.tags | array_to_sentence_string: 'or' }}{% endraw %}</code>
265 </p>
266 <p>
267 <code class="output">foo, bar, or baz</code>
268 </p>
269 </td>
270 </tr>
271 <tr>
272 <td>
273 <p class="name"><strong>Markdownify</strong></p>
274 <p>Convert a Markdown-formatted string into HTML.</p>
275 </td>
276 <td class="align-center">
277 <p>
278 <code class="filter">{% raw %}{{ page.excerpt | markdownify }}{% endraw %}</code>
279 </p>
280 </td>
281 </tr>
282 <tr>
283 <td>
284 <p class="name"><strong>Smartify</strong></p>
285 <p>Convert "quotes" into &ldquo;smart quotes.&rdquo;</p>
286 </td>
287 <td class="align-center">
288 <p>
289 <code class="filter">{% raw %}{{ page.title | smartify }}{% endraw %}</code>
290 </p>
291 </td>
292 </tr>
293 <tr>
294 <td>
295 <p class="name"><strong>Converting Sass/SCSS</strong></p>
296 <p>Convert a Sass- or SCSS-formatted string into CSS.</p>
297 </td>
298 <td class="align-center">
299 <p>
300 <code class="filter">{% raw %}{{ some_scss | scssify }}{% endraw %}</code>
301 <code class="filter">{% raw %}{{ some_sass | sassify }}{% endraw %}</code>
302 </p>
303 </td>
304 </tr>
305 <tr>
306 <td>
307 <p class="name"><strong>Slugify</strong></p>
308 <p>Convert a string into a lowercase URL "slug". See below for options.</p>
309 </td>
310 <td class="align-center">
311 <p>
312 <code class="filter">{% raw %}{{ "The _config.yml file" | slugify }}{% endraw %}</code>
313 </p>
314 <p>
315 <code class="output">the-config-yml-file</code>
316 </p>
317 <p>
318 <code class="filter">{% raw %}{{ "The _config.yml file" | slugify: 'pretty' }}{% endraw %}</code>
319 </p>
320 <p>
321 <code class="output">the-_config.yml-file</code>
322 </p>
323 <p>
324 <code class="filter">{% raw %}{{ "The _cönfig.yml file" | slugify: 'ascii' }}{% endraw %}</code>
325 </p>
326 <p>
327 <code class="output">the-c-nfig-yml-file</code>
328 </p>
329 <p>
330 <code class="filter">{% raw %}{{ "The cönfig.yml file" | slugify: 'latin' }}{% endraw %}</code>
331 </p>
332 <p>
333 <code class="output">the-config-yml-file</code>
334 </p>
335 </td>
336 </tr>
337 <tr>
338 <td>
339 <p class="name"><strong>Data To JSON</strong></p>
340 <p>Convert Hash or Array to JSON.</p>
341 </td>
342 <td class="align-center">
343 <p>
344 <code class="filter">{% raw %}{{ site.data.projects | jsonify }}{% endraw %}</code>
345 </p>
346 </td>
347 </tr>
348 <tr>
349 <td>
350 <p class="name"><strong>Normalize Whitespace</strong></p>
351 <p>Replace any occurrence of whitespace with a single space.</p>
352 </td>
353 <td class="align-center">
354 <p>
355 <code class="filter">{% raw %}{{ "a \n b" | normalize_whitespace }}{% endraw %}</code>
356 </p>
357 </td>
358 </tr>
359 <tr>
360 <td>
361 <p class="name"><strong>Sort</strong></p>
362 <p>Sort an array. Optional arguments for hashes: 1.&nbsp;property name 2.&nbsp;nils order (<em>first</em> or <em>last</em>).</p>
363 </td>
364 <td class="align-center">
365 <p>
366 <code class="filter">{% raw %}{{ page.tags | sort }}{% endraw %}</code>
367 </p>
368 <p>
369 <code class="filter">{% raw %}{{ site.posts | sort: 'author' }}{% endraw %}</code>
370 </p>
371 <p>
372 <code class="filter">{% raw %}{{ site.pages | sort: 'title', 'last' }}{% endraw %}</code>
373 </p>
374 </td>
375 </tr>
376 <tr>
377 <td>
378 <p class="name"><strong>Sample</strong></p>
379 <p>Pick a random value from an array. Optional: pick multiple values.</p>
380 </td>
381 <td class="align-center">
382 <p>
383 <code class="filter">{% raw %}{{ site.pages | sample }}{% endraw %}</code>
384 </p>
385 <p>
386 <code class="filter">{% raw %}{{ site.pages | sample:2 }}{% endraw %}</code>
387 </p>
388 </td>
389 </tr>
390 <tr>
391 <td>
392 <p class="name"><strong>To Integer</strong></p>
393 <p>Convert a string or boolean to integer.</p>
394 </td>
395 <td class="align-center">
396 <p>
397 <code class="filter">{% raw %}{{ some_var | to_integer }}{% endraw %}</code>
398 </p>
399 </td>
400 </tr>
401 <tr>
402 <td>
403 <p class="name"><strong>Array Filters</strong></p>
404 <p>Push, pop, shift, and unshift elements from an Array.</p>
405 <p>These are <strong>NON-DESTRUCTIVE</strong>, i.e. they do not mutate the array, but rather make a copy and mutate that.</p>
406 </td>
407 <td class="align-center">
408 <p>
409 <code class="filter">{% raw %}{{ page.tags | push: 'Spokane' }}{% endraw %}</code>
410 </p>
411 <p>
412 <code class="output">['Seattle', 'Tacoma', 'Spokane']</code>
413 </p>
414 <p>
415 <code class="filter">{% raw %}{{ page.tags | pop }}{% endraw %}</code>
416 </p>
417 <p>
418 <code class="output">['Seattle']</code>
419 </p>
420 <p>
421 <code class="filter">{% raw %}{{ page.tags | shift }}{% endraw %}</code>
422 </p>
423 <p>
424 <code class="output">['Tacoma']</code>
425 </p>
426 <p>
427 <code class="filter">{% raw %}{{ page.tags | unshift: "Olympia" }}{% endraw %}</code>
428 </p>
429 <p>
430 <code class="output">['Olympia', 'Seattle', 'Tacoma']</code>
431 </p>
432 </td>
433 </tr>
434 <tr>
435 <td>
436 <p class="name"><strong>Inspect</strong></p>
437 <p>Convert an object into its String representation for debugging.</p>
438 </td>
439 <td class="align-center">
440 <p>
441 <code class="filter">{% raw %}{{ some_var | inspect }}{% endraw %}</code>
442 </p>
443 </td>
444 </tr>
445 </tbody>
446 </table>
447 </div>
448
449 ### Options for the `slugify` filter
450
451 The `slugify` filter accepts an option, each specifying what to filter.
452 The default is `default`. They are as follows (with what they filter):
453
454 - `none`: no characters
455 - `raw`: spaces
456 - `default`: spaces and non-alphanumeric characters
457 - `pretty`: spaces and non-alphanumeric characters except for `._~!$&'()+,;=@`
458 - `ascii`: spaces, non-alphanumeric, and non-ASCII characters
459 - `latin`: like `default`, except Latin characters are first transliterated (e.g. `àèïòü` to `aeiou`) {%- include docs_version_badge.html version="3.7.0" -%}
460
461 ## Tags
462
463 * [Includes](#includes)
464 * [Code snippet highlighting](#code-snippet-highlighting)
465 * [Linking to pages, collections and posts (the new and improved way)](#links)
466 * [Linking to posts (the old way)](#linking-to-posts)
467
468 ### Includes
469
470 If you have small page snippets that you want to include in multiple places on your site, save the snippets as *include files* and insert them where required, by using the `include` tag:
471
472 {% raw %}
473 ```liquid
474 {% include footer.html %}
475 ```
476 {% endraw %}
477
478 Jekyll expects all *include files* to be placed in an `_includes` directory at the root of your source directory. In the above example, this will embed the contents of `_includes/footer.html` into the calling file.
479
480 For more advanced information on using includes, see [Includes](../includes).
481
482 ### Code snippet highlighting
483
484 Jekyll has built in support for syntax highlighting of over 60 languages
485 thanks to [Rouge](http://rouge.jneen.net). Rouge is the default highlighter
486 in Jekyll 3 and above. To use it in Jekyll 2, set `highlighter` to `rouge`
487 and ensure the `rouge` gem is installed properly.
488
489 Alternatively, you can use [Pygments](http://pygments.org) to highlight
490 your code snippets. To use Pygments, you must have Python installed on your
491 system, have the `pygments.rb` gem installed and set `highlighter` to
492 `pygments` in your site's configuration file. Pygments supports [over 100
493 languages](http://pygments.org/languages/)
494
495 To render a code block with syntax highlighting, surround your code as follows:
496
497 {% raw %}
498 ```liquid
499 {% highlight ruby %}
500 def foo
501 puts 'foo'
502 end
503 {% endhighlight %}
504 ```
505 {% endraw %}
506
507 The argument to the `highlight` tag (`ruby` in the example above) is the
508 language identifier. To find the appropriate identifier to use for the language
509 you want to highlight, look for the “short name” on the [Rouge
510 wiki](https://github.com/jayferd/rouge/wiki/List-of-supported-languages-and-lexers)
511 or the [Pygments' Lexers page](http://pygments.org/docs/lexers/).
512
513 <div class="note info">
514 <h5>Jekyll processes all Liquid filters in code blocks</h5>
515 <p>If you are using a language that contains curly braces, you
516 will likely need to place <code>{&#37; raw &#37;}</code> and
517 <code>{&#37; endraw &#37;}</code> tags around your code.</p>
518 </div>
519
520 #### Line numbers
521
522 There is a second argument to `highlight` called `linenos` that is optional.
523 Including the `linenos` argument will force the highlighted code to include line
524 numbers. For instance, the following code block would include line numbers next
525 to each line:
526
527 {% raw %}
528 ```liquid
529 {% highlight ruby linenos %}
530 def foo
531 puts 'foo'
532 end
533 {% endhighlight %}
534 ```
535 {% endraw %}
536
537 #### Stylesheets for syntax highlighting
538
539 In order for the highlighting to show up, you’ll need to include a highlighting
540 stylesheet. For an example stylesheet you can look at
541 [syntax.css](https://github.com/mojombo/tpw/tree/master/css/syntax.css). These
542 are the same styles as used by GitHub and you are free to use them for your own
543 site. If you use `linenos`, you might want to include an additional CSS class
544 definition for the `.lineno` class in `syntax.css` to distinguish the line
545 numbers from the highlighted code.
546
547
548 ## Links
549
550 ### Linking to pages {#link}
551
552 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.
553
554 You must include the file's original extension when using the `link` tag. Here are some examples:
555
556 {% raw %}
557 ```liquid
558 {{ site.baseurl }}{% link _collection/name-of-document.md %}
559 {{ site.baseurl }}{% link _posts/2016-07-26-name-of-post.md %}
560 {{ site.baseurl }}{% link news/index.html %}
561 {{ site.baseurl }}{% link /assets/files/doc.pdf %}
562 ```
563 {% endraw %}
564
565 You can also use the `link` tag to create a link in Markdown as follows:
566
567 {% raw %}
568 ```liquid
569 [Link to a document]({{ site.baseurl }}{% link _collection/name-of-document.md %})
570 [Link to a post]({{ site.baseurl }}{% link _posts/2016-07-26-name-of-post.md %})
571 [Link to a page]({{ site.baseurl }}{% link news/index.html %})
572 [Link to a file]({{ site.baseurl }}{% link /assets/files/doc.pdf %})
573 ```
574 {% endraw %}
575
576 (Including `{% raw %}{{ site.baseurl }}{% endraw %}` is optional &mdash; it depends on whether you want to preface the page URL with the `baseurl` value.)
577
578 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.
579
580 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`.
581
582 If you're unsure of the path, add `{% raw %}{{ page.path }}{% endraw %}` to the page and it will display the path.
583
584 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).
585
586 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.
587
588 ### Linking to posts
589
590 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.
591
592 {% raw %}
593 ```liquid
594 {{ site.baseurl }}{% post_url 2010-07-21-name-of-post %}
595 ```
596 {% endraw %}
597
598 If you organize your posts in subdirectories, you need to include subdirectory path to the post:
599
600 {% raw %}
601 ```liquid
602 {{ site.baseurl }}{% post_url /subdir/2010-07-21-name-of-post %}
603 ```
604 {% endraw %}
605
606 There is no need to include the file extension when using the `post_url` tag.
607
608 You can also use this tag to create a link to a post in Markdown as follows:
609
610 {% raw %}
611 ```liquid
612 [Name of Link]({{ site.baseurl }}{% post_url 2010-07-21-name-of-post %})
613 ```
614 {% endraw %}
44
55 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.
66
7 ## Pick up a theme
8
9 You can find and preview themes on different galleries:
10
11 - [GitHub.com #jekyll-theme repos](https://github.com/topics/jekyll-theme)
12 - [jamstackthemes.dev](https://jamstackthemes.dev/ssg/jekyll/)
13 - [jekyllthemes.org](http://jekyllthemes.org/)
14 - [jekyllthemes.io](https://jekyllthemes.io/)
15 - [jekyll-themes.com](https://jekyll-themes.com/)
16
17 See also: [resources](/resources/).
18
719 ## Understanding gem-based themes
820
9 When you [create a new Jekyll site](/docs/quickstart) (by running the `jekyll new <PATH>` command), Jekyll installs a site that uses a gem-based theme called [Minima](https://github.com/jekyll/minima).
10
11 With gem-based themes, some of the site's directories (such as the `assets`, `_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.
21 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).
22
23 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.
1224
1325 In the case of Minima, you see only the following files in your Jekyll site directory:
1426
1527 ```
28 .
1629 ├── Gemfile
1730 ├── Gemfile.lock
1831 ├── _config.yml
1932 ├── _posts
2033 │ └── 2016-12-04-welcome-to-jekyll.markdown
21 ├── about.md
22 └── index.md
34 ├── about.markdown
35 └── index.markdown
2336 ```
2437
2538 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.
2639
27 Gem-based themes make it easy 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.
40 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.
2841
2942 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.
3043
3245
3346 ## Overriding theme defaults
3447
35 Jekyll themes set default layouts, includes, and stylesheets. However, you can override any of the theme defaults with your own site content.
48 Jekyll themes set default data, layouts, includes, and stylesheets. However, you can override any of the theme defaults with your own site content.
3649
3750 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.
3851
4053
4154 To locate a theme's files on your computer:
4255
43 1. Run `bundle show` followed by the name of the theme's gem, e.g., `bundle show minima` for Jekyll's default theme.
44
45 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.3.0/gems/minima-2.1.0` on macOS.
56 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.
57
58 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.
4659
4760 2. Open the theme's directory in Finder or Explorer:
4861
4962 ```sh
5063 # On MacOS
51 open $(bundle show minima)
64 open $(bundle info --path minima)
65
5266 # On Windows
53 explorer /usr/local/lib/ruby/gems/2.3.0/gems/minima-2.1.0
67 # First get the gem's installation path:
68 #
69 # bundle info --path minima
70 # => C:/Ruby26-x64/lib/ruby/gems/{{ site.data.ruby.current_version }}/gems/minima-2.5.1
71 #
72 # then invoke explorer with above path, substituting `/` with `\`
73 explorer C:\Ruby26-x64\lib\ruby\gems\{{ site.data.ruby.current_version}}\gems\minima-2.5.1
74
75 # On Linux
76 xdg-open $(bundle info --path minima)
5477 ```
5578
5679 A Finder or Explorer window opens showing the theme's files and directories. The Minima theme gem contains these files:
5780
58 ```
59 ├── LICENSE.txt
60 ├── README.md
61 ├── _includes
62 │   ├── disqus_comments.html
63 │   ├── footer.html
64 │   ├── google-analytics.html
65 │   ├── head.html
66 │   ├── header.html
67 │   ├── icon-github.html
68 │   ├── icon-github.svg
69 │   ├── icon-twitter.html
70 │   └── icon-twitter.svg
71 ├── _layouts
72 │   ├── default.html
73 │   ├── home.html
74 │   ├── page.html
75 │   └── post.html
76 ├── _sass
77 │   ├── minima
78 │   │   ├── _base.scss
79 │   │   ├── _layout.scss
80 │   │   └── _syntax-highlighting.scss
81 │   └── minima.scss
82 └── assets
83 └── main.scss
84 ```
81 ```
82 .
83 ├── LICENSE.txt
84 ├── README.md
85 ├── _includes
86 │   ├── disqus_comments.html
87 │   ├── footer.html
88 │   ├── google-analytics.html
89 │   ├── head.html
90 │   ├── header.html
91 │   ├── icon-github.html
92 │   ├── icon-github.svg
93 │   ├── icon-twitter.html
94 │   └── icon-twitter.svg
95 ├── _layouts
96 │   ├── default.html
97 │   ├── home.html
98 │   ├── page.html
99 │   └── post.html
100 ├── _sass
101 │   ├── minima
102 │   │   ├── _base.scss
103 │   │   ├── _layout.scss
104 │   │   └── _syntax-highlighting.scss
105 │   └── minima.scss
106 └── assets
107 └── main.scss
108 ```
85109
86110 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.
87111
92116 Jekyll will look first to your site's content before looking to the theme's defaults for any requested file in the following folders:
93117
94118 - `/assets`
119 - `/_data`
95120 - `/_layouts`
96121 - `/_includes`
97122 - `/_sass`
98123
99124 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.
100125
101 Refer to your selected theme's documentation and source repository for more information on what files you can override.
102126 {: .note .info}
127 Refer to your selected theme's documentation and source repository for more information on which files you can override.
128
129 ### Themes with `_data` directory {%- include docs_version_badge.html version="4.3.0" -%}
130 {: #themes-with-data-directory }
131
132 Starting with version 4.3.0, Jekyll also takes into account the `_data` directory of themes. This allows data to be distributed across themes.
133
134 A typical example is text used within design elements.
135
136 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.
137
138 A theme developer will probably formulate the heading in English and put it directly into the HTML source code.
139
140 Consumers of the theme can copy the included file into their project and replace the heading there.
141
142 With the consideration of the `_data` directory there is another solution for this standard task.
143
144 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.
145
146 In this file the header is put under the key `header` and Jekyll takes care of the rest.
147
148 For theme developers, this, at first sight, is of course a bigger effort than before.
149
150 However, for the consumers of the theme, the customization is greatly simplified.
151
152 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.
153
154 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.
155
156 {: .note .warning}
157 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!
158
159 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:
160
161 - `_data/i18n.yml` with key `testimonials.header`
162 - `_data/i18n/testimonials.yml` with key `header` (which mirrors the layout of the given example)
163 - `_data/i18n/testimonials/header.yml` without any key, the headline can go straight into the file
164
165 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.
166
167 {: .note .info}
168 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`.
169
170 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.
103171
104172 ## Converting gem-based themes to regular themes
105173
109177
110178 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:
111179
112 ```
113 spec.add_runtime_dependency "jekyll-feed", "~> 0.9"
114 spec.add_runtime_dependency "jekyll-seo-tag", "~> 2.1"
180 ```ruby
181 spec.add_runtime_dependency "jekyll-feed", "~> 0.12"
182 spec.add_runtime_dependency "jekyll-seo-tag", "~> 2.6"
115183 ```
116184
117185 You should include these references in the `Gemfile` in one of two ways.
121189 ```ruby
122190 # ./Gemfile
123191
124 gem "jekyll-feed", "~> 0.9"
125 gem "jekyll-seo-tag", "~> 2.1"
192 gem "jekyll-feed", "~> 0.12"
193 gem "jekyll-seo-tag", "~> 2.6"
126194 ```
127195
128196 ```yaml
139207 # ./Gemfile
140208
141209 group :jekyll_plugins do
142 gem "jekyll-feed", "~> 0.9"
143 gem "jekyll-seo-tag", "~> 2.1"
210 gem "jekyll-feed", "~> 0.12"
211 gem "jekyll-seo-tag", "~> 2.6"
144212 end
145213 ```
146214
147215 Either way, don't forget to `bundle update`.
148216
149 However, if you're publishing on GitHub Pages you should update only your `_config.yml` as GitHub Pages doesn't load plugins via Bundler.
217 If you're publishing on GitHub Pages you should update only your `_config.yml` as GitHub Pages doesn't load plugins via Bundler.
150218
151219 Finally, remove references to the theme gem in `Gemfile` and configuration. For example, to remove `minima`:
152220
153 - Open `Gemfile` and remove `gem "minima", "~> 2.0"`.
221 - Open `Gemfile` and remove `gem "minima", "~> 2.5"`.
154222 - Open `_config.yml` and remove `theme: minima`.
155223
156224 Now `bundle update` will no longer get updates for the theme gem.
163231
164232 To install a gem-based theme:
165233
166 1. Add the theme to your site's `Gemfile`:
234 1. Add the theme gem to your site's `Gemfile`:
167235
168236 ```ruby
169237 # ./Gemfile
170238
171 gem "jekyll-theme-awesome"
172 ```
173 Or if you've started with the `jekyll new` command, replace `gem "minima", "~> 2.0"` with your theme-gem:
239 # This is an example, declare the theme gem you want to use here
240 gem "jekyll-theme-minimal"
241 ```
242
243 Or if you've started with the `jekyll new` command, replace `gem "minima", "~> 2.0"` with the gem you want, e.g:
174244
175245 ```diff
176246 # ./Gemfile
177247
178 - gem "minima", "~> 2.0"
179 + gem "jekyll-theme-awesome"
248 - gem "minima", "~> 2.5"
249 + gem "jekyll-theme-minimal"
180250 ```
181251
182252 2. Install the theme:
188258 3. Add the following to your site's `_config.yml` to activate the theme:
189259
190260 ```yaml
191 theme: jekyll-theme-awesome
261 theme: jekyll-theme-minimal
192262 ```
193263
194264 4. Build your site:
197267 bundle exec jekyll serve
198268 ```
199269
270 {: .note .info}
200271 You can have multiple themes listed in your site's `Gemfile`, but only one theme can be selected in your site's `_config.yml`.
201 {: .note .info }
202
203 If you're publishing your Jekyll site on [GitHub Pages](https://pages.github.com/), note that GitHub Pages supports only some gem-based themes. See [Supported Themes](https://pages.github.com/themes/) in GitHub's documentation to see which themes are supported.
272
273 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.
204274
205275 ## Creating a gem-based theme
206276
207 If you're a Jekyll theme developer (rather than just a consumer of themes), you can package up your theme in RubyGems and allow users to install it through Bundler.
277 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.
208278
209279 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.
210280
234304
235305 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.
236306
237 For example, if your theme has a `/_layouts/page.html` file, and a page has `layout: page` in its YAML 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.
307 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.
238308
239309 ### Assets
240310
241311 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:
242312
243 - If the file has [YAML front matter](/docs/frontmatter/) at the top, it will be rendered.
244 - If the file does not have YAML front matter, it will simply be copied over into the resulting site.
313 - If the file has [front matter](/docs/front-matter/) at the top, it will be rendered.
314 - If the file does not have front matter, it will simply be copied over into the resulting site.
245315
246316 This allows theme creators to ship a default `/assets/styles.scss` file which their layouts can depend on as `/assets/styles.css`.
247317
253323
254324 ```
255325 _sass
256 ├── jekyll-theme-awesome.scss
326 └── jekyll-theme-awesome.scss
257327 ```
258328
259329 Your theme's styles can be included in the user's stylesheet using the `@import` directive.
270340
271341 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.
272342
343 ### Pre-configuring Theme-gems {%- include docs_version_badge.html version="4.0" -%}
344
345 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.
346
347 But unlike other entities loaded from within the theme, loading the config file comes with a few restrictions, as summarized below:
348 * Jekyll's default settings cannot be overridden by a theme-config. That *ball is still in the user's court.*
349 * 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.
350 * 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.
351 * Any settings defined by the theme-config can be overridden by the user.
352
353 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.
354
355 This feature will let the theme-gem to work with *theme-specific config variables* out-of-the-box.
356
273357 ### Documenting your theme
274358
275359 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?
282366
283367 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.
284368
369 {: .note .info}
285370 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.
286 {: .info .note}
287371
288372 ### Publishing your theme
289373
309393 gem push jekyll-theme-awesome-*.gem
310394 ```
311395
312 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](http://semver.org/) while bumping your theme-version.
396 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.
2020 can be done on Ubuntu or Debian by running:
2121
2222 ```sh
23 sudo apt-get install ruby2.3-dev
23 sudo apt-get install ruby2.6-dev
2424 ```
2525
2626 On Red Hat, CentOS, and Fedora systems you can do this by running:
2727
2828 ```sh
2929 sudo yum install ruby-devel
30 ```
31
32 If you installed the above - specifically on Fedora 23 - but the extensions would still not compile, you are probably running a Fedora image that misses the `redhat-rpm-config` package. To solve this, simply run:
33
34 ```sh
35 sudo dnf install redhat-rpm-config
3630 ```
3731
3832 On Arch Linux you need to run:
10296 you run into this issue, upgrade Xcode and install the upgraded Command
10397 Line Tools.
10498
105 ### Running Jekyll as Non-Superuser (no sudo!)
99 ### Running Jekyll as Non-Superuser (no sudo!)
106100 {: #no-sudo}
107101
108102 On most flavors of Linux, macOS, and Bash on Ubuntu on Windows, it is
109103 possible to run Jekyll as a non-superuser and without having to install
110 gems to system-wide locations by adding the following lines to the end
104 gems to system-wide locations by adding the following lines to the end
111105 of your `.bashrc` file:
112106
113 ```
107 ```bash
114108 # Ruby exports
115109
116110 export GEM_HOME=$HOME/gems
117111 export PATH=$HOME/gems/bin:$PATH
118112 ```
119113
120 This tells `gem` to place its gems within the user's home folder,
121 not in a system-wide location, and adds the local `jekyll` command to the
114 This tells `gem` to place its gems within the user's home folder,
115 not in a system-wide location, and adds the local `jekyll` command to the
122116 user's `PATH` ahead of any system-wide paths.
123117
124118 This is also useful for many shared webhosting services, where user accounts
125 have only limited privileges. Adding these exports to `.bashrc` before running
119 have only limited privileges. Adding these exports to `.bashrc` before running
126120 `gem install jekyll bundler` allows a complete non-`sudo` install of Jekyll.
127121
128 To activate the new exports, either close and restart Bash, logout and
129 log back into your shell account, or run `. .bashrc` in the
122 To activate the new exports, either close and restart Bash, logout and
123 log back into your shell account, or run `. .bashrc` in the
130124 currently-running shell.
131125
132126 If you see the following error when running the `jekyll new` command,
137131
138132 Running bundle install in /home/user/test...
139133
140 Your user account isn't allowed to install to the system RubyGems.
134 Your user account is not allowed to install to the system RubyGems.
141135 You can cancel this installation and run:
142136
143 bundle install --path vendor/bundle
137 bundle install --path vendor/bundle
144138
145139 to install the gems into ./vendor/bundle/, or you can enter your password
146140 and install the bundled gems to RubyGems using sudo.
151145 Once this is done, the `jekyll new` command should work properly for
152146 your user account.
153147
154 ### Jekyll &amp; Mac OS X 10.11
155
156 With the introduction of System Integrity Protection, several directories
148 ### Jekyll &amp; macOS
149
150 With the introduction of System Integrity Protection in v10.11, several directories
157151 that were previously writable are now considered system locations and are no
158152 longer available. Given these changes, there are a couple of simple ways to get
159153 up and running. One option is to change the location where the gem will be
170164 ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
171165 ```
172166
173 Once Homebrew is installed, the second step is easy:
167 Once Homebrew is installed, the second step is to run:
174168
175169 ```sh
176170 brew install ruby
213207
214208 ## Problems running Jekyll
215209
210 ### macOS
211
212 Jekyll is compatible with macOS with ARM64 architecture.
213 However, `bundle exec jekyll serve` may [fail with older version `ffi`](https://github.com/ffi/ffi/issues/870).
214
215 You may need to run `bundle update` or update `ffi` to at least `1.14.2` manually.
216
217 ### Debian or Ubuntu
218
216219 On Debian or Ubuntu, you may need to add `/var/lib/gems/1.8/bin/` to your path
217220 in order to have the `jekyll` executable be available in your Terminal.
218221
238241
239242 ## Configuration problems
240243
241 The order of precedence for conflicting [configuration settings](../configuration/)
244 The order of precedence for conflicting [configuration settings](/docs/configuration/)
242245 is as follows:
243246
244247 1. Command-line flags
251254
252255 **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:**
253256
254 ```sh
257 ```
255258 ERROR: YOUR SITE COULD NOT BE BUILT:
256259 ------------------------------------
257260 Invalid date '<%= Time.now.strftime('%Y-%m-%d %H:%M:%S %z') %>':
258261 Document 'vendor/bundle/gems/jekyll-3.4.3/lib/site_template/_posts/0000-00-00-welcome-to-jekyll.markdown.erb'
259 does not have a valid date in the YAML front matter.
260 ```
261
262 Simply 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`.
263
262 does not have a valid date in front matter.
263 ```
264
265 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`.
264266
265267 The proper solution is to incorporate the default setting for `exclude:` rather than override it completely:
266268
267 For versions upto `v3.4.3`, the `exclude:` setting must look like following:
269 For versions up to `v3.4.3`, the `exclude:` setting must look like following:
268270
269271 ```yaml
270272 exclude:
278280 - any_additional_item # any user-specific listing goes at the end
279281 ```
280282
281 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 simply commented out to enable easy edits in future.
282
283 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.
283284
284285 ## Markup Problems
285286
292293 Liquid version 2.0 seems to break the use of `{{ "{{" }}` in templates.
293294 Unlike previous versions, using `{{ "{{" }}` in 2.0 triggers the following error:
294295
295 ```sh
296 ```
296297 '{{ "{{" }}' was not properly terminated with regexp: /\}\}/ (Liquid::SyntaxError)
297298 ```
298299
308309
309310 If you run into an issue that a static file can't be found in your
310311 production environment during build since v3.2.0 you should set your
311 [environment to `production`](../configuration/#specifying-a-jekyll-environment-at-build-time).
312 [environment to `production`](/docs/configuration/environments/).
312313 The issue is caused by trying to copy a non-existing symlink.
313314
314315 <div class="note">
5252 ### Draft Posts
5353
5454 Jekyll now lets you write draft posts, and allows you to easily preview how
55 they will look prior to publishing. To start a draft, simply create a folder
55 they will look prior to publishing. To start a draft, create a folder
5656 called `_drafts` in your site's source directory (e.g., alongside `_posts`),
57 and add a new markdown file to it. To preview your new post, simply run the
57 and add a new markdown file to it. To preview your new post, run the
5858 `jekyll serve` command with the `--drafts` flag.
5959
6060 <div class="note info">
7171 Rather than passing individual flags via the command line, you can now pass
7272 an entire custom Jekyll config file. This helps to distinguish between
7373 environments, or lets you programmatically override user-specified
74 defaults. Simply add the `--config` flag to the `jekyll` command, followed
74 defaults. Add the `--config` flag to the `jekyll` command, followed
7575 by the path to one or more config files (comma-delimited, no spaces).
7676
7777 #### As a result, the following command line flags are now deprecated:
118118 such as previewing locally before pushing to GitHub Pages. Jekyll 1.0 makes
119119 that easier with the new `--baseurl` flag. To take advantage of this
120120 feature, first add the production `baseurl` to your site's `_config.yml`
121 file. Then, throughout the site, simply prefix relative URLs
122 with `{% raw %}{{ site.baseurl }}{% endraw %}`.
121 file. Then, throughout the site, prefix relative URLs
122 with {% raw %}`{{ site.baseurl }}`{% endraw %}.
123123 When you're ready to preview your site locally, pass along the `--baseurl`
124124 flag with your local baseurl (most likely `/`) to `jekyll serve` and Jekyll
125125 will swap in whatever you've passed along, ensuring all your links work as
126126 you'd expect in both environments.
127
128127
129128 <div class="note warning">
130129 <h5 markdown="1">All page and post URLs contain leading slashes</h5>
1111 gem update jekyll
1212 ```
1313
14 Since v3.2 Jekyll requires Ruby version >= 2.1
15 {: .note .warning }
14 {: .note .warning}
15 Since version {% include docs_version_badge.html version="3.2"%}, Jekyll requires Ruby version >= 2.1.
1616
1717 <div class="note feature">
1818 <h5>Diving in</h5>
3535
3636 For `site.collections.myCollection` in Jekyll 2, you now do:
3737
38 {% raw %}
3839 ```liquid
39 {% raw %}
4040 {% assign myCollection = site.collections | where: "label", "myCollection" | first %}
41 ```
4142 {% endraw %}
42 ```
4343
4444 This is a bit cumbersome at first, but is easier than a big `for` loop.
45
46 ### Textile support
47
48 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.
4549
4650 ### Dropped dependencies
4751
7781 Introducing: `layout`. In Jekyll 2 and below, any metadata in the layout was merged onto
7882 the `page` variable in Liquid. This caused a lot of confusion in the way the data was
7983 merged and some unexpected behaviour. In Jekyll 3, all layout data is accessible via `layout`
80 in Liquid. For example, if your layout has `class: my-layout` in its YAML front matter,
81 then the layout can access that via `{% raw %}{{ layout.class }}{% endraw %}`.
84 in Liquid. For example, if your layout has `class: my-layout` in its front matter,
85 then the layout can access that via {% raw %}`{{ layout.class }}`{% endraw %}.
8286
8387 ### Syntax highlighter changed
8488
96100 created your site using Jekyll 2 and below, you may receive the following
97101 error when trying to **serve** or **build**:
98102
99 ```text
103 ```
100104 Since v3.0, permalinks for pages in subfolders must be relative to the site
101105 source directory, not the parent directory. Check
102106 https://jekyllrb.com/docs/upgrading/ for more info.
110114
111115 ### Permalinks no longer automatically add a trailing slash
112116
113 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/`.
117 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/`.
114118
115119 ### All my posts are gone! Where'd they go!
116120
0 ---
1 title: Upgrading from 3.x to 4.x
2 permalink: /docs/upgrading/3-to-4/
3 ---
4
5 A few things have changed in Jekyll 4.
6
7 Before we dive in, you need to have at least Ruby {{ site.data.ruby.min_version }}
8 installed.
9
10 Run the following in your terminal to check
11
12 ```sh
13 ruby -v
14 {{ site.data.ruby.current_version_output }}
15 ```
16
17 If you're using a supported Ruby version >= {{ site.data.ruby.min_version }}, go ahead
18 and fetch the latest version of Jekyll:
19
20 ```sh
21 gem update jekyll
22 ```
23
24 <div class="note warning">
25 <h5><code>post_url</code> Tag and Baseurl</h5>
26 <p>&nbsp;</p>
27 <p>
28 The <code>post_url</code> tag now incorporates the <code>relative_url</code> filter within itself
29 and therefore automatically prepends your site's <code>baseurl</code> to the post's <code>url</code>
30 value.
31 </p>
32 <p>
33 Please ensure that you change all instances of the <code>post_url</code> usage as following:
34 </p>
35
36 {% highlight diff %}
37 {% raw %}
38 - {{ site.baseurl }}/{% post_url 2018-03-20-hello-world.markdown %}
39 + {% post_url 2018-03-20-hello-world.markdown %}
40 {% endraw %}
41 {% endhighlight %}
42 </div>
43
44 ## Template rendering
45
46 We've slightly altered the way Jekyll parses and renders your various templates
47 to improve the overall build times. Jekyll now parses a template once, caches it
48 internally and then renders the parsed template multiple times as required by
49 your pages and documents.
50
51 The downside to this is that some of the community-authored plugins may not work
52 as they previously used to.
53
54 ## Static files in unrendered collections
55
56 Collections other than `posts` can contain static assets along with Markdown files.
57 But if the collection has not been configured with metadata `output: true`, then
58 neither its *documents* nor its *static assets* will be output to the destination
59 directory.
60
61 ## For plugin authors
62
63 * If your plugin depends on the following code: `site.liquid_renderer.file(path).parse(content)`,
64 note that the return value (`template`, an instance of *`Liquid::Template`*), from that line will
65 always be the **same object** for a given `path`. <br/>
66 The *`template`* instance is then rendered as previously, with respect to the `payload` passed to it.
67 You'll therefore have to ensure that *`payload`* is not memoized or cached in your plugin instance.
68
69 * If its a requirement that `template` you get from the above step *be different* at all times,
70 you can invoke *`Liquid::Template`* directly:
71
72 ```diff
73 - template = site.liquid_renderer.file(path).parse(content)
74 + template = Liquid::Template.parse(content)
75 ```
76
77 ## Exclusion changes
78
79 We've enhanced our default exclusion array.
80 It now looks like the following:
81
82 ```yaml
83 # default excludes
84 exclude:
85 - .sass-cache/
86 - .jekyll-cache/
87 - gemfiles/
88 - Gemfile
89 - Gemfile.lock
90 - node_modules/
91 - vendor/bundle/
92 - vendor/cache/
93 - vendor/gems/
94 - vendor/ruby/
95 ```
96
97 What's new is that this array **does not get overridden by the `exclude` array
98 in the user's config file anymore**. The user's exclude entries simply get
99 **added** to the above default array (if the entry isn't already excluded).
100
101 To forcibly "process" directories or files that have been excluded, list them
102 in the `include` array instead:
103
104 ```yaml
105 # overrides your excluded items configuration and the default include array ([".htaccess"])
106 include:
107 - .htaccess
108 - node_modules/uglifier/index.js
109 ```
110
111 The above configuration directs Jekyll to handle only
112 `node_modules/uglifier/index.js` while ignoring every other file in the
113 `node_modules` directory since that directory is "excluded" by default.
114
115 Note that the default `include` array still gets overridden by the `include`
116 array in your config file. So, be sure to add `.htaccess` to the list if you
117 need that file to be present in the generated site.
118
119 ## Kramdown v2
120
121 Jekyll has dropped support for `kramdown-1.x` entirely.
122
123 From [`v2.0` onwards](https://kramdown.gettalong.org/news.html#kramdown-200-released)
124 kramdown requires specific extensions to be additionally installed to use
125 certain features are desired outside of kramdown's core functionality.
126
127 Out of all the extensions listed in the report linked above, gem
128 `kramdown-parser-gfm` is automatically installed along with Jekyll 4.0. The
129 remaining extensions will have to be manually installed by the user depending on
130 desired functionality, by listing the extension's gem-name in their `Gemfile`.
131
132 Notes:
133 * `kramdown-converter-pdf` will be ignored by Jekyll Core. To have Jekyll convert Markdown to PDF
134 you'll have to depend on a plugin that subclasses `Jekyll::Converter` with the
135 [required methods]({% link _docs/plugins/converters.md %}).
136
137 For example:
138
139 ```ruby
140 module Jekyll
141 External.require_with_graceful_fail "kramdown-converter-pdf"
142
143 class Markdown2PDF < Converter
144 safe true
145 priority :low
146
147 def matches(ext)
148 # match only files that have an extension exactly ".markdown"
149 ext =~ /^\.markdown$/
150 end
151
152 def convert(content)
153 Kramdown::Document.new(content).to_pdf
154 end
155
156 def output_ext
157 ".pdf"
158 end
159 end
160 end
161 ```
162
163 * Vendors that provide a versioned Jekyll Environment Image (e.g. Docker Image, GitHub Pages, etc)
164 will have to manually whitelist kramdown's extension gems in their distributions for Jekyll 4.0.
165
166 ## Deprecated Configuration Options
167
168 Jekyll 4.0 has dropped support for all legacy configuration options that were deprecated over multiple
169 releases in the previous series.
170
171 To that end, we shall no longer output a deprecation warning when we encounter a legacy config key nor
172 shall we gracefully assign their values to the newer counterparts. Depending on the key, it shall either
173 be ignored or raise an `InvalidConfigurationError` error if the key is still valid but the associated
174 value is not of the valid type.
99
1010 - [From 0.x to 1.x and 2.x](/docs/upgrading/0-to-2/)
1111 - [From 2.x to 3.x](/docs/upgrading/2-to-3/)
12 - [From 3.x to 4.x](/docs/upgrading/3-to-4/)
1213
1314 ## Minor updates
1415
00 ---
1 title: Basic Usage
1 title: Command Line Usage
22 permalink: /docs/usage/
33 ---
44
5 The Jekyll gem makes a `jekyll` executable available to you in your Terminal
6 window. You can use this command in a number of ways:
5 The Jekyll gem makes a `jekyll` executable available to you in your terminal.
76
8 ```sh
9 jekyll build
10 # => The current folder will be generated into ./_site
7 The `jekyll` program has several commands but the structure is always:
118
12 jekyll build --destination <destination>
13 # => The current folder will be generated into <destination>
9 ```
10 jekyll command [argument] [option] [argument_to_option]
1411
15 jekyll build --source <source> --destination <destination>
16 # => The <source> folder will be generated into <destination>
17
18 jekyll build --watch
19 # => The current folder will be generated into ./_site,
20 # watched for changes, and regenerated automatically.
12 Examples:
13 jekyll new site/ --blank
14 jekyll serve --config _alternative_config.yml
2115 ```
2216
23 ## Override default development settings
17 Typically you'll use `jekyll serve` while developing locally and `jekyll build` when you need to generate the site for production.
2418
25 Default URL is set to `http://localhost:4000` in development environment. {% include docs_version_badge.html version="3.3.0" %}
19 For a full list of options and their argument, see [Build Command Options](/docs/configuration/options/#build-command-options).
2620
27 If you want to build for your production environment:
21 Here are some of the most common commands:
2822
29 - Set your production URL in `_config.yml` e.g. `url: https://example.com`.
30 - Run `JEKYLL_ENV=production bundle exec jekyll build`.
23 * `jekyll new PATH` - Creates a new Jekyll site with default gem-based theme at specified path. The directories will be created as necessary.
24 * `jekyll new PATH --blank` - Creates a new blank Jekyll site scaffold at specified path.
25 * `jekyll build` or `jekyll b` - Performs a one off build your site to `./_site` (by default).
26 * `jekyll serve` or `jekyll s` - Builds your site any time a source file changes and serves it locally.
27 * `jekyll clean` - Removes all generated files: destination folder, metadata file, Sass and Jekyll caches.
28 * `jekyll help` - Shows help, optionally for a given subcommand, e.g. `jekyll help build`.
29 * `jekyll new-theme` - Creates a new Jekyll theme scaffold.
30 * `jekyll doctor` - Outputs any deprecation or configuration issues.
3131
32 <div class="note info">
33 <h5>Changes to <code>_config.yml</code> are not included during automatic regeneration.</h5>
34 <p>
35 The <code>_config.yml</code> master configuration file contains global configurations
36 and variable definitions that are read once at execution time. Changes made to <code>_config.yml</code>
37 during automatic regeneration are not loaded until the next execution.
38 </p>
39 <p>
40 Note <a href="../datafiles">Data Files</a> are included and reloaded during automatic regeneration.
41 </p>
42 </div>
43
44 <div class="note warning">
45 <h5>Destination folders are cleaned on site builds</h5>
46 <p>
47 The contents of <code>&lt;destination&gt;</code> are automatically
48 cleaned, by default, when the site is built. Files or folders that are not
49 created by your site will be removed. Files and folders you wish to retain
50 in <code>&lt;destination&gt;</code> may be specified within the <code>&lt;keep_files&gt;</code>
51 configuration directive.
52 </p>
53 <p>
54 Do not use an important location for <code>&lt;destination&gt;</code>;
55 instead, use it as a staging area and copy files from there to your web server.
56 </p>
57 </div>
58
59 Jekyll also comes with a built-in development server that will allow you to
60 preview what the generated site will look like in your browser locally.
61
62 ```sh
63 jekyll serve
64 # => A development server will run at http://localhost:4000/
65 # Auto-regeneration: enabled. Use `--no-watch` to disable.
66
67 jekyll serve --livereload
68 # LiveReload refreshes your browser after a change.
69
70 jekyll serve --incremental
71 # Incremental will perform a partial build in order to reduce regeneration time.
72
73 jekyll serve --detach
74 # => Same as `jekyll serve` but will detach from the current terminal.
75 # If you need to kill the server, you can `kill -9 1234` where "1234" is the PID.
76 # If you cannot find the PID, then do, `ps aux | grep jekyll` and kill the instance.
77 ```
78
79 ```sh
80 jekyll serve --no-watch
81 # => Same as `jekyll serve` but will not watch for changes.
82 ```
83
84 These are just a few of the available [configuration options](../configuration/).
85 Many configuration options can either be specified as flags on the command line,
86 or alternatively (and more commonly) they can be specified in a `_config.yml`
87 file at the root of the source directory. Jekyll will automatically use the
88 options from this file when run. For example, if you place the following lines
89 in your `_config.yml` file:
90
91 ```yaml
92 source: _source
93 destination: _deploy
94 ```
95
96 Then the following two commands will be equivalent:
97
98 ```sh
99 jekyll build
100 jekyll build --source _source --destination _deploy
101 ```
102
103 For more about the possible configuration options, see the
104 [configuration](../configuration/) page.
105
106 <div class="note info">
107 <h5>Call for help</h5>
108 <p>
109 The <code>help</code> command is always here to remind you of all available options and usage, and also works with the <code>build</code>, <code>serve</code> and <code>new</code> subcommands, e.g <code>jekyll help new</code> or <code>jekyll help build</code>.
110 </p>
111 </div>
112
113 If you're interested in browsing these docs on-the-go, install the
114 `jekyll-docs` gem and run `jekyll docs` in your terminal.
32 To change Jekyll's default build behavior have a look through the [configuration options](/docs/configuration/).
22 permalink: /docs/variables/
33 ---
44
5 Jekyll traverses your site looking for files to process. Any files with [YAML
6 front matter](../frontmatter/) are subject to processing. For each of these
7 files, Jekyll makes a variety of data available via the [Liquid templating
8 system](https://github.com/Shopify/liquid/wiki). The
9 following is a reference of the available data.
5 Jekyll traverses your site looking for files to process. Any files with
6 [front matter](/docs/front-matter/) are subject to processing. For each of these
7 files, Jekyll makes a variety of data available via [Liquid](/docs/liquid/).
8 The following is a reference of the available data.
109
1110 ## Global Variables
1211
13 <div class="mobile-side-scroller">
14 <table>
15 <thead>
16 <tr>
17 <th>Variable</th>
18 <th>Description</th>
19 </tr>
20 </thead>
21 <tbody>
22 <tr>
23 <td><p><code>site</code></p></td>
24 <td><p>
25
26 Sitewide information + configuration settings from
27 <code>_config.yml</code>. See below for details.
28
29 </p></td>
30 </tr>
31 <tr>
32 <td><p><code>page</code></p></td>
33 <td><p>
34
35 Page specific information + the <a href="../frontmatter/">YAML front
36 matter</a>. Custom variables set via the YAML Front Matter will be
37 available here. See below for details.
38
39 </p></td>
40 </tr>
41 <tr>
42 <td><p><code>layout</code></p></td>
43 <td><p>
44
45 Layout specific information + the <a href="../frontmatter/">YAML front
46 matter</a>. Custom variables set via the YAML Front Matter in
47 layouts will be available here.
48
49 </p></td>
50 </tr>
51 <tr>
52 <td><p><code>content</code></p></td>
53 <td><p>
54
55 In layout files, the rendered content of the Post or Page being wrapped.
56 Not defined in Post or Page files.
57
58 </p></td>
59 </tr>
60 <tr>
61 <td><p><code>paginator</code></p></td>
62 <td><p>
63
64 When the <code>paginate</code> configuration option is set, this
65 variable becomes available for use. See <a
66 href="../pagination/">Pagination</a> for details.
67
68 </p></td>
69 </tr>
70 </tbody>
71 </table>
72 </div>
12 {% include docs_variables_table.html scope=site.data.jekyll_variables.global %}
7313
7414 ## Site Variables
7515
76 <div class="mobile-side-scroller">
77 <table>
78 <thead>
79 <tr>
80 <th>Variable</th>
81 <th>Description</th>
82 </tr>
83 </thead>
84 <tbody>
85 <tr>
86 <td><p><code>site.time</code></p></td>
87 <td><p>
88
89 The current time (when you run the <code>jekyll</code> command).
90
91 </p></td>
92 </tr>
93 <tr>
94 <td><p><code>site.pages</code></p></td>
95 <td><p>
96
97 A list of all Pages.
98
99 </p></td>
100 </tr>
101 <tr>
102 <td><p><code>site.posts</code></p></td>
103 <td><p>
104
105 A reverse chronological list of all Posts.
106
107 </p></td>
108 </tr>
109 <tr>
110 <td><p><code>site.related_posts</code></p></td>
111 <td><p>
112
113 If the page being processed is a Post, this contains a list of up to ten
114 related Posts. By default, these are the ten most recent posts.
115 For high quality but slow to compute results, run the
116 <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.
117
118 </p></td>
119 </tr>
120 <tr>
121 <td><p><code>site.static_files</code></p></td>
122 <td><p>
123
124 A list of all <a href="/docs/static-files/">static files</a> (i.e.
125 files not processed by Jekyll's converters or the Liquid renderer).
126 Each file has three properties: <code>path</code>,
127 <code>modified_time</code> and <code>extname</code>.
128
129 </p></td>
130 </tr>
131 <tr>
132 <td><p><code>site.html_pages</code></p></td>
133 <td><p>
134
135 A subset of `site.pages` listing those which end in `.html`.
136
137 </p></td>
138 </tr>
139 <tr>
140 <td><p><code>site.html_files</code></p></td>
141 <td><p>
142
143 A subset of `site.static_files` listing those which end in `.html`.
144
145 </p></td>
146 </tr>
147 <tr>
148 <td><p><code>site.collections</code></p></td>
149 <td><p>
150
151 A list of all the collections.
152
153 </p></td>
154 </tr>
155 <tr>
156 <td><p><code>site.data</code></p></td>
157 <td><p>
158
159 A list containing the data loaded from the YAML files located in the <code>_data</code> directory.
160
161 </p></td>
162 </tr>
163 <tr>
164 <td><p><code>site.documents</code></p></td>
165 <td><p>
166
167 A list of all the documents in every collection.
168
169 </p></td>
170 </tr>
171 <tr>
172 <td><p><code>site.categories.CATEGORY</code></p></td>
173 <td><p>
174
175 The list of all Posts in category <code>CATEGORY</code>.
176
177 </p></td>
178 </tr>
179 <tr>
180 <td><p><code>site.tags.TAG</code></p></td>
181 <td><p>
182
183 The list of all Posts with tag <code>TAG</code>.
184
185 </p></td>
186 </tr>
187 <tr>
188 <td><p><code>site.url</code></p></td>
189 <td><p>
190
191 Contains the url of your site as it is configured in the <code>_config.yml</code>.
192 For example, if you have <code>url: http://mysite.com</code>
193 in your configuration file, then it will be accessible in Liquid as
194 <code>site.url</code>. For the development environment there is
195 <a href="/news/#3-siteurl-is-set-by-the-development-server">an exception</a>,
196 if you are running <code>jekyll serve</code> in a development environment
197 <code>site.url</code> will be set to the value of <code>host</code>,
198 <code>port</code>, and SSL-related options. This defaults to
199 <code>url: http://localhost:4000</code>.
200
201 </p></td>
202 </tr>
203 <tr>
204 <td><p><code>site.[CONFIGURATION_DATA]</code></p></td>
205 <td><p>
206
207 All the variables set via the command line and your
208 <code>_config.yml</code> are available through the <code>site</code>
209 variable. For example, if you have <code>foo: bar</code>
210 in your configuration file, then it will be accessible in Liquid as <code>site.foo</code>.
211 Jekyll does not parse changes to <code>_config.yml</code> in
212 <code>watch</code> mode, you must restart Jekyll to see changes to variables.
213
214 </p></td>
215 </tr>
216 </tbody>
217 </table>
218 </div>
16 {% include docs_variables_table.html scope=site.data.jekyll_variables.site %}
21917
22018 ## Page Variables
22119
222 <div class="mobile-side-scroller">
223 <table>
224 <thead>
225 <tr>
226 <th>Variable</th>
227 <th>Description</th>
228 </tr>
229 </thead>
230 <tbody>
231 <tr>
232 <td><p><code>page.content</code></p></td>
233 <td><p>
234
235 The content of the Page, rendered or un-rendered depending upon
236 what Liquid is being processed and what <code>page</code> is.
237
238 </p></td>
239 </tr>
240 <tr>
241 <td><p><code>page.title</code></p></td>
242 <td><p>
243
244 The title of the Page.
245
246 </p></td>
247 </tr>
248 <tr>
249 <td><p><code>page.excerpt</code></p></td>
250 <td><p>
251
252 The un-rendered excerpt of a document.
253
254 </p></td>
255 </tr>
256 <tr>
257 <td><p><code>page.url</code></p></td>
258 <td><p>
259
260 The URL of the Post without the domain, but
261 with a leading slash, e.g.
262 <code>/2008/12/14/my-post.html</code>
263
264 </p></td>
265 </tr>
266 <tr>
267 <td><p><code>page.date</code></p></td>
268 <td><p>
269
270 The Date assigned to the Post. This can be overridden in a Post’s front
271 matter by specifying a new date/time in the format
272 <code>YYYY-MM-DD HH:MM:SS</code> (assuming UTC), or
273 <code>YYYY-MM-DD HH:MM:SS +/-TTTT</code> (to specify a time zone using
274 an offset from UTC. e.g. <code>2008-12-14 10:30:00 +0900</code>).
275
276 </p></td>
277 </tr>
278 <tr>
279 <td><p><code>page.id</code></p></td>
280 <td><p>
281
282 An identifier unique to a document in a Collection or a Post (useful in RSS feeds). e.g.
283 <code>/2008/12/14/my-post</code>
284 <code>/my-collection/my-document</code>
285
286 </p></td>
287 </tr>
288 <tr>
289 <td><p><code>page.categories</code></p></td>
290 <td><p>
291
292 The list of categories to which this post belongs. Categories are
293 derived from the directory structure above the <code>_posts</code>
294 directory. For example, a post at
295 <code>/work/code/_posts/2008-12-24-closures.md</code> would have this
296 field set to <code>['work', 'code']</code>. These can also be specified
297 in the <a href="../frontmatter/">YAML Front Matter</a>.
298
299 </p></td>
300 </tr>
301 <tr>
302 <td><p><code>page.tags</code></p></td>
303 <td><p>
304
305 The list of tags to which this post belongs. These can be specified in
306 the <a href="../frontmatter/">YAML Front Matter</a>.
307
308 </p></td>
309 </tr>
310 <tr>
311 <td><p><code>page.path</code></p></td>
312 <td><p>
313
314 The path to the raw post or page. Example usage: Linking back to the
315 page or post’s source on GitHub. This can be overridden in the
316 <a href="../frontmatter/">YAML Front Matter</a>.
317
318 </p></td>
319 </tr>
320 <tr>
321 <td><p><code>page.next</code></p></td>
322 <td><p>
323
324 The next post relative to the position of the current post in
325 <code>site.posts</code>. Returns <code>nil</code> for the last entry.
326
327 </p></td>
328 </tr>
329 <tr>
330 <td><p><code>page.previous</code></p></td>
331 <td><p>
332
333 The previous post relative to the position of the current post in
334 <code>site.posts</code>. Returns <code>nil</code> for the first entry.
335
336 </p></td>
337 </tr>
338 </tbody>
339 </table>
340 </div>
20 {% include docs_variables_table.html scope=site.data.jekyll_variables.page %}
34121
34222 <div class="note">
34323 <h5>ProTip™: Use Custom Front Matter</h5>
34424 <p>
345
34625 Any custom front matter that you specify will be available under
34726 <code>page</code>. For example, if you specify <code>custom_css: true</code>
348 in a page’s front matter, that value will be available as
349 <code>page.custom_css</code>.
350
27 in a page’s front matter, that value will be available as <code>page.custom_css</code>.
35128 </p>
35229 <p>
353
35430 If you specify front matter in a layout, access that via <code>layout</code>.
355 For example, if you specify <code>class: full_page</code>
356 in a layout’s front matter, that value will be available as
357 <code>layout.class</code> in the layout and its parents.
358
31 For example, if you specify <code>class: full_page</code> in a layout’s front matter,
32 that value will be available as <code>layout.class</code> in the layout and its parents.
35933 </p>
36034 </div>
36135
36 ## Theme Variables {%- include docs_version_badge.html version="4.3.0" -%}
37 {: #theme-variables }
38
39 {% include docs_variables_table.html scope=site.data.jekyll_variables.theme %}
40
36241 ## Paginator
36342
364 <div class="mobile-side-scroller">
365 <table>
366 <thead>
367 <tr>
368 <th>Variable</th>
369 <th>Description</th>
370 </tr>
371 </thead>
372 <tbody>
373 <tr>
374 <td><p><code>paginator.per_page</code></p></td>
375 <td><p>Number of Posts per page.</p></td>
376 </tr>
377 <tr>
378 <td><p><code>paginator.posts</code></p></td>
379 <td><p>Posts available for that page.</p></td>
380 </tr>
381 <tr>
382 <td><p><code>paginator.total_posts</code></p></td>
383 <td><p>Total number of Posts.</p></td>
384 </tr>
385 <tr>
386 <td><p><code>paginator.total_pages</code></p></td>
387 <td><p>Total number of pages.</p></td>
388 </tr>
389 <tr>
390 <td><p><code>paginator.page</code></p></td>
391 <td><p>The number of the current page.</p></td>
392 </tr>
393 <tr>
394 <td><p><code>paginator.previous_page</code></p></td>
395 <td><p>The number of the previous page.</p></td>
396 </tr>
397 <tr>
398 <td><p><code>paginator.previous_page_path</code></p></td>
399 <td><p>The path to the previous page.</p></td>
400 </tr>
401 <tr>
402 <td><p><code>paginator.next_page</code></p></td>
403 <td><p>The number of the next page.</p></td>
404 </tr>
405 <tr>
406 <td><p><code>paginator.next_page_path</code></p></td>
407 <td><p>The path to the next page.</p></td>
408 </tr>
409 </tbody>
410 </table>
411 </div>
43 {% include docs_variables_table.html scope=site.data.jekyll_variables.paginator %}
41244
41345 <div class="note info">
41446 <h5>Paginator variable availability</h5>
41547 <p>
416
417 These are only available in index files, however they can be located in a
418 subdirectory, such as <code>/blog/index.html</code>.
419
48 These are only available in index files, however they can be located in a subdirectory,
49 such as <code>/blog/index.html</code>.
42050 </p>
42151 </div>
+0
-218
docs/_docs/windows.md less more
0 ---
1 title: Jekyll on Windows
2 permalink: /docs/windows/
3 ---
4
5 While Windows is not an officially-supported platform, it can be used to run Jekyll with the proper tweaks. This page aims to collect some of the general knowledge and lessons that have been unearthed by Windows users.
6
7
8 ## Installing Jekyll
9
10 If you are using Windows 10 Anniversary Update, the easiest way to run Jekyll is by [installing][WSL-Guide] the new Bash on Ubuntu on Windows.
11
12
13 ### Installation via Bash on Windows 10
14
15 *Note:* You must have [Bash on Ubuntu on Windows][BASH-WSL] enabled.
16
17 First let's make sure all our packages / repositories are up to date. Open a new Command Prompt instance, and type the following:
18
19 ```sh
20 bash
21 ```
22 Your Command Prompt instance should now be a Bash instance. Now we must update our repo lists and packages.
23
24 ```sh
25 sudo apt-get update -y && sudo apt-get upgrade -y
26 ```
27 Now we can install Ruby. To do this we will use a repository from [BrightBox](https://www.brightbox.com/docs/ruby/ubuntu/), which hosts optimized versions of Ruby for Ubuntu.
28
29 ```sh
30 sudo apt-add-repository ppa:brightbox/ruby-ng
31 sudo apt-get update
32 sudo apt-get install ruby2.3 ruby2.3-dev build-essential dh-autoreconf
33 ```
34
35 Next let's update our Ruby gems:
36
37 ```sh
38 sudo gem update
39 ```
40
41 Now all that is left to do is install Jekyll.
42
43 ```sh
44 sudo gem install jekyll bundler
45 ```
46
47 Check if Jekyll installed properly by running:
48
49 ```sh
50 jekyll -v
51 ```
52
53 **And that's it!**
54
55 To start a new project named `my_blog`, just run:
56
57 ```sh
58 jekyll new my_blog
59 ```
60
61 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.
62
63 <div class="note info">
64 <h5>Non-superuser account issues</h5>
65 <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">Troubleshooting</a>.</p>
66 </div>
67
68 **Note:** Bash on Ubuntu on Windows is still under development, so you may run into issues.
69
70 [WSL-Guide]: https://msdn.microsoft.com/en-us/commandline/wsl/install_guide
71 [BASH-WSL]: https://msdn.microsoft.com/en-us/commandline/wsl/about
72
73
74 ### Installation via RubyInstaller
75
76 [RubyInstaller][] is a self-contained Windows-based installer that includes the Ruby language, an execution environment, important documentation, and more.
77
78 1. Download and Install a package manager version from [RubyInstaller Downloads][RubyInstaller-downloads].
79 2. Install Jekyll and Bundler via a command prompt window: `gem install jekyll bundler`
80 3. Check if Jekyll installed properly: `jekyll -v`
81
82 [RubyInstaller]: https://rubyinstaller.org/
83 [RubyInstaller-downloads]: https://rubyinstaller.org/downloads/
84
85
86 ### Installation via Chocolatey
87
88 A quick way to install Jekyll using Chocolatey is to follow the [installation instructions by David Burela](https://davidburela.wordpress.com/2015/11/28/easily-install-jekyll-on-windows-with-3-command-prompt-entries-and-chocolatey/):
89
90 1. Install a package manager for Windows called [Chocolatey][]
91 2. Install Ruby via Chocolatey: `choco install ruby -y`
92 3. Reopen a command prompt and install Jekyll: `gem install jekyll`
93
94 Updates in the infrastructure of Ruby may cause SSL errors when attempting to use `gem install` with versions of the RubyGems package older than 2.6. (The RubyGems package installed via the Chocolatey tool is version 2.3) If you have installed an older version, you can update the RubyGems package using the directions [here][ssl-certificate-update].
95
96 [ssl-certificate-update]: http://guides.rubygems.org/ssl-certificate-update/#installing-using-update-packages
97
98
99 ### Installing *github-pages* via Chocolatey
100
101 This section is part of an article written by [Jens Willmer][jwillmerPost]. To follow the instructions you need to have [Chocolatey][] installed on your system. If you already have a version of Ruby installed you need to uninstall it before you can continue.
102
103
104 #### Install Ruby and Ruby development kit
105
106 Open a command prompt and execute the following commands:
107
108 * `choco install ruby -version 2.2.4`
109 * `choco install ruby2.devkit` - _needed for compilation of json gem_
110
111
112 #### Configure Ruby development kit
113
114 The development kit did not set the environment path for Ruby so we need to do it.
115
116 * Open command prompt in `C:\tools\DevKit2`
117 * Execute `ruby dk.rb init` to create a file called `config.yml`
118 * Edit the `config.yml` file and include the path to Ruby `- C:/tools/ruby22`
119 * Execute the following command to set the path: `ruby dk.rb install`
120
121
122 #### Nokogiri gem installation
123
124 This gem is also needed in the github-pages and to get it running on Windows x64 we have to install a few things.
125
126 **Note:** In the current [pre release][nokogiriFails] it works out of the box with Windows x64 but this version is not referenced in the github-pages.
127
128 ```sh
129 choco install libxml2 -Source "https://www.nuget.org/api/v2/"
130
131 choco install libxslt -Source "https://www.nuget.org/api/v2/"
132
133 choco install libiconv -Source "https://www.nuget.org/api/v2/
134
135 gem install nokogiri --^
136 --with-xml2-include=C:\Chocolatey\lib\libxml2.2.7.8.7\build\native\include^
137 --with-xml2-lib=C:\Chocolatey\lib\libxml2.redist.2.7.8.7\build\native\bin\v110\x64\Release\dynamic\cdecl^
138 --with-iconv-include=C:\Chocolatey\lib\libiconv.1.14.0.11\build\native\include^
139 --with-iconv-lib=C:\Chocolatey\lib\libiconv.redist.1.14.0.11\build\native\bin\v110\x64\Release\dynamic\cdecl^
140 --with-xslt-include=C:\Chocolatey\lib\libxslt.1.1.28.0\build\native\include^
141 --with-xslt-lib=C:\Chocolatey\lib\libxslt.redist.1.1.28.0\build\native\bin\v110\x64\Release\dynamic
142 ```
143
144 #### Install github-pages
145
146 * Open command prompt and install [Bundler][]: `gem install bundler`
147 * Create a file called `Gemfile` without any extension in your root directory of your blog
148 * Copy & paste the two lines into the file:
149
150
151 ```ruby
152 source 'https://rubygems.org'
153 gem 'github-pages', group: :jekyll_plugins
154 ```
155
156 * **Note:** We use an unsecure connection because SSL throws exceptions in the version of Ruby
157 * Open a command prompt, target your local blog repository root, and install github-pages: `bundle install`
158
159
160 After this process you should have github-pages installed on your system and you can host your blog again with `jekyll s`.
161 There will be a warning on startup that you should include `gem 'wdm', '>= 0.1.0' if Gem.win_platform?` to your `Gemfile` but I could not get `jekyll s` working if I include that line so for the moment I ignore that warning.
162
163 In the future the installation process of the github-pages should be as simple as the setup of the blog. But as long as the new version of the Nokogiri ([v1.6.8][nokogiriReleases]) is not stable and referenced, it is work to get it up and running on Windows.
164
165 [jwillmerPost]: https://jwillmer.de/blog/tutorial/how-to-install-jekyll-and-pages-gem-on-windows-10-x46 "Installation instructions by Jens Willmer"
166 [Chocolatey]: https://chocolatey.org/install "Package manager for Windows"
167 [nokogiriFails]: https://github.com/sparklemotion/nokogiri/issues/1456#issuecomment-206481794 "Nokogiri fails to install on Ruby 2.3 for Windows"
168 [Bundler]: http://bundler.io/ "Ruby Dependencie Manager"
169 [nokogiriReleases]: https://github.com/sparklemotion/nokogiri/releases "Nokogiri Releases"
170
171 For a more conventional way of installing Jekyll you can follow this [complete guide to install Jekyll 3 on Windows by Sverrir Sigmundarson][windows-installjekyll3].
172
173 Optionally you can use [Autoinstall Jekyll for Windows][fastjekyll-autoinstall].
174
175 [windows-installjekyll3]: https://labs.sverrirs.com/jekyll/
176 [fastjekyll-autoinstall]: https://github.com/KeJunMao/fastjekyll#autoinstall-jekyll-for-windows
177
178
179 ## Encoding
180
181 If you use UTF-8 encoding, make sure that no `BOM` header characters exist in your files or very, very bad things will happen to
182 Jekyll. This is especially relevant when you're running Jekyll on Windows.
183
184 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. It can be done with the following command:
185
186 ```sh
187 chcp 65001
188 ```
189
190
191 ## Time-Zone Management
192
193 Since Windows doesn't have a native source of zoneinfo data, the Ruby Interpreter would not understand IANA Timezones and hence using them had the `TZ` environment variable default to UTC/GMT 00:00.
194 Though Windows users could alternatively define their blog's timezone by setting the key to use POSIX format of defining timezones, it wasn't as user-friendly when it came to having the clock altered to changing DST-rules.
195
196 Jekyll now uses a rubygem to internally configure Timezone based on established [IANA Timezone Database][IANA-database].
197 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) to enable development on Windows:
198
199 ```ruby
200 # Windows does not include zoneinfo files, so bundle the tzinfo-data gem
201 gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]
202 ```
203
204 [IANA-database]: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
205
206
207 ## Auto Regeneration
208
209 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.
210
211 Add the following to the Gemfile for your site if you have issues with auto-regeneration on Windows alone:
212
213 ```ruby
214 gem 'wdm', '~> 0.1.1' if Gem.win_platform?
215 ```
216
217 You may first have to download and install the [Ruby DevKit](https://rubyinstaller.org/downloads/) by following [the instructions here](https://github.com/oneclick/rubyinstaller/wiki/Development-Kit).
0 {% if site.google_analytics_id %}
0 {% if site.google_analytics_id -%}
11 <!-- Google Analytics (https://www.google.com/analytics) -->
22 <script>
33 !function(j,e,k,y,l,L){j.GoogleAnalyticsObject=y,j[y]||(j[y]=function(){
77
88 ga('create', '{{ site.google_analytics_id }}', 'jekyllrb.com');
99 ga('send', 'pageview');
10
1110 </script>
12 {% endif %}
11 {% endif -%}
00 <div class="unit one-fifth hide-on-mobiles">
11 <aside>
2 {% for section in site.data.docs %}
3 <h4>{{ section.title }}</h4>
4 {% include docs_ul.html items=section.docs %}
5 {% endfor %}
2 {% for section in site.data.docs_nav -%}
3 <h4>{{ section.title }}</h4>
4 <ul>
5 {%- for item in section.docs -%}
6 {%- assign p = site.docs | where: "url", item.link | first %}
7 <li {%- if page.url == p.url %} class="current" {%- endif -%}><a href="{{ p.url | relative_url }}">
8 {{- p.menu_name | default: p.title -}}
9 </a></li>
10 {%- endfor %}
11 </ul>
12 {% endfor -%}
613 </aside>
714 </div>
00 <div class="docs-nav-mobile unit whole show-on-mobiles">
11 <select onchange="if (this.value) window.location.href=this.value">
22 <option value="">Navigate the docs…</option>
3 {% for section in site.data.docs %}
3 {% for section in site.data.docs_nav -%}
44 <optgroup label="{{ section.title }}">
5 {% include docs_option.html items=section.docs %}
5 {%- for item in section.docs -%}
6 {% assign p = site.docs | where: "url", item.link | first %}
7 <option value="{{ p.url | relative_url }}">
8 {{- p.menu_name | default: p.title -}}
9 </option>
10 {%- endfor %}
611 </optgroup>
7 {% endfor %}
12 {% endfor -%}
813 </select>
914 </div>
+0
-5
docs/_includes/docs_option.html less more
0 {% for item in include.items %}
1 {% assign item_url = item | prepend:"/docs/" | append:"/" %}
2 {% assign doc = site.docs | where: "url", item_url | first %}
3 <option value="{{ doc.url }}">{{ doc.title }}</option>
4 {% endfor %}
+0
-7
docs/_includes/docs_ul.html less more
0 <ul>
1 {% for item in include.items %}
2 {% assign item_url = item | prepend:"/docs/" | append:"/" %}
3 {% assign p = site.docs | where:"url", item_url | first %}
4 <li class="{% if item_url == page.url %}current{% endif %}"><a href="{{ p.url }}">{{ p.title }}</a></li>
5 {% endfor %}
6 </ul>
0 <div class="mobile-side-scroller">
1 <table>
2 <thead>
3 <tr>
4 <th>Variable</th>
5 <th>Description</th>
6 </tr>
7 </thead>
8 <tbody>
9 {% for var in include.scope -%}
10 <tr>
11 <td><p><code>{{ var.name }}</code></p></td>
12 <td><p>{{- var.description -}}</p></td>
13 </tr>
14 {% endfor -%}
15 </tbody>
16 </table>
17 </div>
00 <footer>
11 <div class="grid">
22 <div class="unit one-third center-on-mobiles">
3 <p>Jekyll is lovingly maintained by the <a href="{{ '/team/' | relative_url }}">core team</a> of volunteers. </p>
34 <p>The contents of this website are <br />&copy;&nbsp;{{ site.time | date: '%Y' }} under the terms of the <a href="{{ site.repository }}/blob/master/LICENSE">MIT&nbsp;License</a>.</p>
45 </div>
56 <div class="unit two-thirds align-right center-on-mobiles">
67 <p>
78 Proudly hosted by
89 <a href="https://github.com">
9 <img src="/img/footer-logo.png" width="100" height="30" alt="GitHub • Social coding">
10 <img src="{{ 'img/footer-logo.png' | relative_url }}" width="100" height="30" alt="GitHub • Social coding">
11 </a>
12 </p>
13 </div>
14 <div class="unit two-thirds align-right center-on-mobiles">
15 <p>
16 Jekyll is funded thanks to its
17 <a href="https://github.com/jekyll/jekyll#sponsors">
18 sponsors!
1019 </a>
1120 </p>
1221 </div>
11 <div class="flexbox">
22 <div class="center-on-mobiles">
33 <h1>
4 <a href="/" class="logo">
4 <a href="{{ '/' | relative_url }}" class="logo">
55 <span class="sr-only">Jekyll</span>
6 <img src="/img/logo-2x.png" width="140" height="65" alt="Jekyll Logo">
6 <img src="{{ 'img/logo-2x.png' | relative_url }}" width="140" height="65" alt="Jekyll Logo">
77 </a>
88 </h1>
99 </div>
1010 <nav class="main-nav hide-on-mobiles">
11 {% include primary-nav-items.html %}
11 {% include primary-nav-items.html -%}
1212 </nav>
1313 <div class="search hide-on-mobiles">
14 {% include search/input.html %}
14 {% include search/input.html -%}
1515 </div>
1616 <div class="meta hide-on-mobiles">
1717 <ul>
18 <li>
19 <a href="{{ site.repository }}/releases/tag/v{{ site.version }}">v{{ site.version }}</a>
20 </li>
21 <li>
22 <a href="{{ site.repository }}">GitHub</a>
23 </li>
18 <li><a href="{{ site.repository }}/releases/tag/v{{ site.version }}">v{{ site.version }}</a></li>
19 <li><a href="{{ site.repository }}">GitHub</a></li>
2420 </ul>
2521 </div>
2622 </div>
2723 <nav class="mobile-nav show-on-mobiles">
28 {% include mobile-nav-items.html %}
24 {% include mobile-nav-items.html -%}
2925 </nav>
3026 </header>
00 <ul>
1 <li class="{% if page.overview %}current{% endif %}">
2 <a href="/">Home</a>
3 </li>
4 <li class="{% if page.url contains '/docs/' %}current{% endif %}">
5 <a href="/docs/home/">Docs</a>
6 </li>
7 <li class="{% if page.author %}current{% endif %}">
8 <a href="/news/">News</a>
9 </li>
10 <li class="{% if page.url contains '/help/' %}current{% endif %}">
11 <a href="/help/">Help</a>
12 </li>
13 <li>
14 <a href="{{ site.repository }}">GitHub</a>
15 </li>
1 {% for p in site.data.primary_nav -%}
2 {% if p.show_on_mobile -%}
3 <li
4 {%- if p.link == '/' -%}
5 {%- if page.url == '/' %} class="current" {% endif -%}
6 {% else -%}
7 {%- if page.url contains p.link %} class="current" {% endif -%}
8 {% endif -%}
9 ><a href="{{ p.link | relative_url }}">{{ p.title }}</a></li>
10 {% endif -%}
11 {% endfor -%}
12 <li><a href="{{ site.repository }}">GitHub</a></li>
1613 </ul>
00 <div class="unit one-fifth hide-on-mobiles">
11 <aside>
22 <ul>
3 <li class="{% if page.title == 'News' %}current{% endif %}">
4 <a href="/news/">All News</a>
3 <li {%- if page.title == 'News' %} class="current" {%- endif %}>
4 <a href="{{ '/news/' | relative_url }}">All News</a>
55 </li>
6 <li class="{% if page.title == 'Releases' %}current{% endif %}">
7 <a href="/news/releases/">Jekyll Releases</a>
6 <li {%- if page.title == 'Releases' %} class="current" {%- endif %}>
7 <a href="{{ '/news/releases/' | relative_url }}">Jekyll Releases</a>
88 </li>
99 </ul>
1010 <h4>Recent Releases</h4>
1111 <ul>
12 {% for post in site.categories.release limit:5 %}
13 <li class="{% if page.title == post.title %}current{% endif %}">
14 <a href="{{ post.url }}">Version {{ post.version }}</a>
12 {% for post in site.categories.release limit:5 -%}
13 <li {% if page.title == post.title %} class="current"{% endif %}>
14 <a href="{{ post.url | relative_url }}">Version {{ post.version }}</a>
1515 </li>
16 {% endfor %}
16 {% endfor -%}
1717 <li>
18 <a href="/docs/history/">History »</a>
18 <a href="{{ '/docs/history/' | relative_url }}">History »</a>
1919 </li>
2020 </ul>
2121 <h4>Other News</h4>
2222 <ul>
23 {% for post in site.posts %}
24 {% unless post.categories contains 'release' %}
25 <li class="{% if page.title == post.title %}current{% endif %}">
26 <a href="{{ post.url }}">{{ post.title }}</a>
27 </li>
28 {% endunless %}
29 {% endfor %}
23 {% for post in site.posts -%}
24 {% unless post.categories contains 'release' -%}
25 <li {%- if page.title == post.title %} class="current" {%- endif %}>
26 <a href="{{ post.url | relative_url }}">{{ post.title }}</a>
27 </li>
28 {% endunless -%}
29 {% endfor -%}
3030 </ul>
3131 </aside>
3232 </div>
00 <div class="docs-nav-mobile unit whole show-on-mobiles">
11 <select onchange="if (this.value) window.location.href=this.value">
22 <option value="">Navigate the blog…</option>
3 <option value="/news/">Home</option>
4 <optgroup label="v1.x">
5 {% for post in site.posts %}
6 <option value="{{ post.url }}">{{ post.title }}</option>
7 {% endfor %}
3 <option value="{{ '/news/' | relative_url }}">Home</option>
4 <optgroup label="posts">
5 {% for post in site.posts -%}
6 <option value="{{ post.url | relative_url }}">{{ post.title }}</option>
7 {% endfor -%}
88 </optgroup>
99 </select>
1010 </div>
00 <article>
11 <h2>
2 <a href="{{ post.url }}">
3 {{ post.title }}
2 <a href="{{ post.url | relative_url }}">
3 {{- post.title -}}
44 </a>
55 </h2>
66 <span class="post-category">
77 <span class="label">
8 {{ post.categories | array_to_sentence_string }}
8 {{- post.categories | array_to_sentence_string -}}
99 </span>
1010 </span>
1111 <div class="post-meta">
1212 <span class="post-date">
13 {{ post.date | date_to_string }}
13 {{- post.date | date_to_string -}}
1414 </span>
15 {% assign author = post.author %}
15 {% assign author = post.author -%}
1616 <a href="https://github.com/{{ author }}" class="post-author">
17 {% avatar user=author size=24 %}
18 {{ author }}
17 {% avatar user=author size=24 -%}
18 <span class="author-name">{{ author }}</span>
1919 </a>
2020 </div>
2121 <div class="post-content">
22 {{ post.content }}
22 {{- post.content -}}
2323 </div>
2424 </article>
0 <article>
1 <div class="cell-left">
2 <span class="post-category">
3 <span class="label">
4 {{- post.categories | array_to_sentence_string -}}
5 </span>
6 </span>
7 </div>
8 <div class="cell-right">
9 <div class="post-meta">
10 <h2 class="post-title">
11 <a href="{{ post.url | relative_url }}">
12 {{- post.title -}}
13 </a>
14 </h2>
15 <span class="post-date">
16 {{- post.date | date_to_string -}}
17 </span>
18 {% assign author = post.author -%}
19 <a href="https://github.com/{{ author }}" class="post-author">
20 {% avatar user=author size=24 -%}
21 <span class="author-name">{{ author }}</span>
22 </a>
23 </div>
24 </div>
25 </article>
00 <ul>
1 <li class="{% if page.overview %}current{% endif %}">
2 <a href="/">Home</a>
3 </li>
4 <li class="{% if page.url contains '/docs/' %}current{% endif %}">
5 <a href="/docs/home/">Docs</a>
6 </li>
7 <li class="{% if page.author %}current{% endif %}">
8 <a href="/news/">News</a>
9 </li>
10 <li class="{% if page.url contains '/help/' %}current{% endif %}">
11 <a href="/help/">Help</a>
12 </li>
1 {% for p in site.data.primary_nav -%}
2 <li
3 {%- if p.link == '/' -%}
4 {% if page.url == p.link %} class="current" {%- endif -%}
5 {% else -%}
6 {% if page.url contains p.link %} class="current" {%- endif -%}
7 {% endif -%}
8 ><a href="{{ p.link | relative_url }}">{{ p.title }}</a></li>
9 {% endfor -%}
1310 </ul>
0 <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/docsearch.js@2/dist/cdn/docsearch.min.js"></script>
1 <script type="text/javascript"> docsearch({
0 <script src="https://cdn.jsdelivr.net/npm/docsearch.js@2/dist/cdn/docsearch.min.js"></script>
1 <script> docsearch({
22 apiKey: '50fe39c839958dfad797000f33e2ec17',
33 indexName: 'jekyllrb',
44 inputSelector: '#docsearch-input',
+0
-39
docs/_includes/section_nav.html less more
0 {% comment %}
1 Map grabs the doc sections, giving us an array of arrays. Join, flattens all
2 the items to a comma delimited string. Split turns it into an array again.
3 {% endcomment %}
4 {% assign docs = site.data.docs | map: 'docs' | join: ',' | split: ',' %}
5
6 {% comment %}
7 Because this is built for every page, lets find where we are in the ordered
8 document list by comparing url strings. Then if there's something previous or
9 next, lets build a link to it.
10 {% endcomment %}
11
12 {% for document in docs %}
13 {% assign document_url = document | prepend:"/docs/" | append:"/" %}
14 {% if document_url == page.url %}
15 <div class="section-nav">
16 <div class="left align-right">
17 {% if forloop.first %}
18 <span class="prev disabled">Back</span>
19 {% else %}
20 {% assign previous = forloop.index0 | minus: 1 %}
21 {% assign previous_page = docs[previous] | prepend:"/docs/" | append:"/" %}
22 <a href="{{ previous_page }}" class="prev">Back</a>
23 {% endif %}
24 </div>
25 <div class="right align-left">
26 {% if forloop.last %}
27 <span class="next disabled">Next</span>
28 {% else %}
29 {% assign next = forloop.index0 | plus: 1 %}
30 {% assign next_page = docs[next] | prepend:"/docs/" | append:"/" %}
31 <a href="{{ next_page }}" class="next">Next</a>
32 {% endif %}
33 </div>
34 </div>
35 <div class="clear"></div>
36 {% break %}
37 {% endif %}
38 {% endfor %}
0 {% comment %}
0 {%- comment -%}
11 Map grabs the tutorials sections, giving us an array of arrays. Join, flattens all
22 the items to a comma delimited string. Split turns it into an array again.
3 {% endcomment %}
4 {% assign tutorials = site.data.tutorials | map: 'tutorials' | join: ',' | split: ',' %}
3 {%- endcomment -%}
4 {%- assign tutorials = site.data.tutorials | map: 'tutorials' | join: ',' | split: ',' -%}
55
6 {% comment %}
6 {%- comment -%}
77 Because this is built for every page, lets find where we are in the ordered
88 document list by comparing url strings. Then if there's something previous or
99 next, lets build a link to it.
10 {% endcomment %}
10 {%- endcomment -%}
1111
12 {% for tutorial in tutorials %}
13 {% assign tutorial_url = tutorial | prepend:"/tutorials/" | append:"/" %}
14 {% if tutorial_url == page.url %}
15 <div class="section-nav">
16 <div class="left align-right">
17 {% if forloop.first %}
18 <span class="prev disabled">Back</span>
19 {% else %}
20 {% assign previous = forloop.index0 | minus: 1 %}
21 {% assign previous_page = tutorials[previous] | prepend:"/tutorials/" | append:"/" %}
22 <a href="{{ previous_page }}" class="prev">Back</a>
23 {% endif %}
24 </div>
25 <div class="right align-left">
26 {% if forloop.last %}
27 <span class="next disabled">Next</span>
28 {% else %}
29 {% assign next = forloop.index0 | plus: 1 %}
30 {% assign next_page = tutorials[next] | prepend:"/tutorials/" | append:"/" %}
31 <a href="{{ next_page }}" class="next">Next</a>
32 {% endif %}
33 </div>
12 {% for tutorial in tutorials -%}
13 {% assign tutorial_url = tutorial | prepend:"/tutorials/" | append:"/" -%}
14 {% if tutorial_url == page.url -%}
15 <div class="section-nav">
16 <div class="left align-right">
17 {% if forloop.first -%}
18 <span class="prev disabled">Back</span>
19 {% else -%}
20 {% assign previous = forloop.index0 | minus: 1 -%}
21 {% assign previous_page = tutorials[previous] | prepend:"/tutorials/" | append:"/" -%}
22 <a href="{{ previous_page | relative_url }}" class="prev">Back</a>
23 {% endif -%}
3424 </div>
35 <div class="clear"></div>
36 {% break %}
37 {% endif %}
38 {% endfor %}
25 <div class="right align-left">
26 {% if forloop.last -%}
27 <span class="next disabled">Next</span>
28 {% else -%}
29 {% assign next = forloop.index0 | plus: 1 -%}
30 {% assign next_page = tutorials[next] | prepend:"/tutorials/" | append:"/" -%}
31 <a href="{{ next_page | relative_url }}" class="next">Next</a>
32 {% endif -%}
33 </div>
34 </div>
35 <div class="clear"></div>
36 {% break -%}
37 {% endif -%}
38 {% endfor -%}
0 {% assign docs = site.docs | where_exp: "doc", "doc.url contains '/step-by-step/'" -%}
1
2 {% for doc in docs -%}
3 {% if doc.url == page.url -%}
4 <div class="section-nav">
5 <div class="left align-right">
6 {% if forloop.first -%}
7 <span class="prev disabled">Back</span>
8 {% else -%}
9 {% assign previous = forloop.index0 | minus: 1 -%}
10 <a href="{{ docs[previous].url }}" class="prev">Back</a>
11 {% endif -%}
12 </div>
13 <div class="right align-left">
14 {% if forloop.last -%}
15 <span class="next disabled">Next</span>
16 {% else -%}
17 {% assign next = forloop.index0 | plus: 1 -%}
18 <a href="{{ docs[next].url }}" class="next">Next</a>
19 {% endif -%}
20 </div>
21 </div>
22 <div class="clear"></div>
23 {% break -%}
24 {% endif -%}
25 {% endfor -%}
26
27 <ol class="step-nav">
28 {% for step in docs -%}
29 <li {%- if step.url == page.url %} class="current"{% endif %}><a href="{{ step.url }}">
30 {{- step.title -}}
31 </a></li>
32 {% endfor -%}
33 </ol>
22 <head>
33 <meta charset="UTF-8">
44 <meta name="viewport" content="width=device-width,initial-scale=1">
5 <meta name="generator" content="Jekyll v{{ jekyll.version }}">
65 {% feed_meta %}
6 <link type="application/atom+xml" rel="alternate" href="{{ 'feed/release.xml' | relative_url }}" title="Jekyll releases posts" />
77 <link rel="alternate" type="application/atom+xml" title="Recent commits to Jekyll’s master branch" href="{{ site.repository }}/commits/master.atom">
8 <link rel="preconnect" href="https://fonts.gstatic.com/" crossorigin>
9 <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Lato:300,300italic,400,400italic,700,700italic,900">
10 <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/docsearch.js@2/dist/cdn/docsearch.min.css" />
11 <link rel="stylesheet" href="/css/screen.css">
12 <link rel="icon" type="image/x-icon" href="/favicon.ico">
8 <link rel="preload" href="{{ 'fonts/lato-v14-latin-300.woff2' | relative_url }}" as="font" type="font/woff2" crossorigin />
9 <link rel="preload" href="{{ 'fonts/lato-v14-latin-700.woff2' | relative_url }}" as="font" type="font/woff2" crossorigin />
10 <link rel="preload" href="{{ 'css/screen.css' | relative_url }}" as="style">
11 <link rel="stylesheet" href="{{ 'css/screen.css' | relative_url }}">
12 <link rel="icon" type="image/x-icon" href="{{ 'favicon.ico' | relative_url }}">
1313 {% seo %}
1414 <!--[if lt IE 9]>
1515 <script src="/js/html5shiv.min.js"></script>
00 <div class="unit one-fifth hide-on-mobiles">
11 <aside>
2 {% for section in site.data.tutorials %}
3 <h4>{{ section.title }}</h4>
4
5 {% include tutorials_ul.html items=section.tutorials %}
6
7 {% endfor %}
2 {% for section in site.data.tutorials -%}
3 <h4>{{ section.title }}</h4>
4 <ul>
5 {% for item in section.tutorials -%}
6 {% assign item_url = item | prepend:"/tutorials/" | append:"/" -%}
7 {% assign p = site.tutorials | where:"url", item_url | first -%}
8 <li {%- if item_url == page.url %} class="current" {%- endif %}><a href="{{ p.url | relative_url }}">
9 {{- p.title -}}
10 </a></li>
11 {% endfor -%}
12 </ul>
13 {% endfor -%}
814 </aside>
915 </div>
00 <div class="docs-nav-mobile unit whole show-on-mobiles">
11 <select onchange="if (this.value) window.location.href=this.value">
22 <option value="">Navigate the tutorials…</option>
3 {% for section in site.data.tutorials %}
3 {% for section in site.data.tutorials -%}
44 <optgroup label="{{ section.title }}">
5 {% include tutorials_option.html items=section.tutorials %}
5 {% for item in section.tutorials -%}
6 {% assign item_url = item | prepend:"/tutorials/" | append:"/" -%}
7 {% assign tutorial = site.tutorials | where: "url", item_url | first -%}
8 <option value="{{ tutorial.url | relative_url }}">{{ tutorial.title }}</option>
9 {% endfor -%}
610 </optgroup>
7 {% endfor %}
11 {% endfor -%}
812 </select>
913 </div>
+0
-5
docs/_includes/tutorials_option.html less more
0 {% for item in include.items %}
1 {% assign item_url = item | prepend:"/tutorials/" | append:"/" %}
2 {% assign tutorial = site.tutorials | where: "url", item_url | first %}
3 <option value="{{ tutorial.url }}">{{ tutorial.title }}</option>
4 {% endfor %}
+0
-7
docs/_includes/tutorials_ul.html less more
0 <ul>
1 {% for item in include.items %}
2 {% assign item_url = item | prepend:"/tutorials/" | append:"/" %}
3 {% assign p = site.tutorials | where:"url", item_url | first %}
4 <li class="{% if item_url == page.url %}current{% endif %}"><a href="{{ p.url }}">{{ p.title }}</a></li>
5 {% endfor %}
6 </ul>
0 {% include top.html %}
0 {%- include top.html -%}
11
22 <body class="wrap">
3 {% include header.html %}
3 {%- include header.html -%}
44
5 {{ content }}
5 {{- content -}}
66
7 {% include footer.html %}
8 {% include anchor_links.html %}
9 {% include analytics.html %}
10 {% include search/script.html %}
7 {%- include footer.html -%}
8 {%- include anchor_links.html -%}
9 {%- include analytics.html -%}
10 {%- include search/script.html -%}
1111 </body>
1212 </html>
11 layout: default
22 ---
33
4 <section class="docs">
5 <div class="grid">
6
7 {% include docs_contents_mobile.html %}
8
9 <div class="unit four-fifths">
10 <article>
11 <div class="improve right hide-on-mobiles">
12 <a href="https://github.com/jekyll/jekyll/edit/master/docs/{{ page.path }}"><i class="fa fa-pencil"></i> &nbsp;Improve this page</a>
13 </div>
14 <h1>{{ page.title }}</h1>
15 {{ content }}
16 {% include section_nav.html %}
17 </article>
18 </div>
19
20 {% include docs_contents.html %}
21
22 <div class="clear"></div>
23
4 <section class="docs">
5 <div class="grid">
6 {% include docs_contents_mobile.html -%}
7 <div class="unit four-fifths">
8 <article>
9 <div class="improve right hide-on-mobiles">
10 <a data-proofer-ignore href="https://github.com/jekyll/jekyll/edit/master/docs/{{ page.path }}"><i
11 class="fa fa-pencil"></i> &nbsp;Improve this page</a>
12 </div>
13 <h1>{{ page.title }}</h1>
14 {{ content }}
15 </article>
2416 </div>
25 </section>
17 {% include docs_contents.html -%}
18 <div class="clear"></div>
19 </div>
20 </section>
11 layout: default
22 ---
33
4 <section class="news">
5 <div class="grid">
6
7 {% include news_contents_mobile.html %}
8
9 <div class="unit four-fifths">
10 {{ content }}
11 </div>
12
13 {% include news_contents.html %}
14
15 <div class="clear"></div>
16
4 <section class="news">
5 <div class="grid">
6 {% include news_contents_mobile.html -%}
7 <div class="unit four-fifths">
8 {{- content -}}
179 </div>
18 </section>
10 {% include news_contents.html -%}
11 <div class="clear"></div>
12 </div>
13 </section>
44 <article>
55 <h2>
66 {{ page.title }}
7 <a href="{{ page.url }}" class="header-link" title="Permalink">
7 <a href="{{ page.url | relative_url }}" class="header-link" title="Permalink">
88 <span class="sr-only">Permalink</span>
99 <i class="fa fa-link"></i>
1010 </a>
1111 </h2>
1212 <span class="post-category">
1313 <span class="label">
14 {{ page.categories | array_to_sentence_string }}
14 {{- page.categories | array_to_sentence_string -}}
1515 </span>
1616 </span>
1717 <div class="post-meta">
1818 <span class="post-date">
19 {{ page.date | date_to_string }}
19 {{- page.date | date_to_string -}}
2020 </span>
21 {% assign author = page.author %}
21 {% assign author = page.author -%}
2222 <a href="https://github.com/{{ author }}" class="post-author">
23 {% avatar user=author size=24 %}
24 {{ author }}
23 {% avatar user=author size=24 -%}
24 <span class="author-name">{{ author }}</span>
2525 </a>
2626 </div>
2727 <div class="post-content">
33
44 <section class="standalone">
55 <div class="grid">
6
76 <div class="unit whole">
87 <article>
8 {%- if page.permalink contains "resources" %}
9 <div class="improve right hide-on-mobiles">
10 <a data-proofer-ignore href="https://github.com/jekyll/jekyll/edit/master/docs/{{ page.path }}"><i class="fa fa-pencil"></i> &nbsp;Improve this page</a>
11 </div>
12 {% endif -%}
913 <h1>{{ page.title }}</h1>
1014 {{ content }}
1115 </article>
1216 </div>
13
1417 <div class="clear"></div>
15
1618 </div>
1719 </section>
0 ---
1 layout: default
2 ---
3 <section class="docs">
4 <div class="grid">
5 {% include docs_contents_mobile.html -%}
6 <div class="unit four-fifths">
7 <article>
8 <div class="improve right hide-on-mobiles">
9 <a data-proofer-ignore href="https://github.com/jekyll/jekyll/edit/master/docs/{{ page.path }}"><i class="fa fa-pencil"></i> &nbsp;Improve this page</a>
10 </div>
11 <h1>Step by Step Tutorial</h1>
12 <h2>{{ page.position }}. {{ page.title }}</h2>
13 {{ content }}
14 {% include step-index.html -%}
15 </article>
16 </div>
17 {% include docs_contents.html -%}
18 <div class="clear"></div>
19 </div>
20 </section>
11 layout: default
22 ---
33
4 <section class="docs">
5 <div class="grid">
6
7 {% include tutorials_contents_mobile.html %}
8
9 <div class="unit four-fifths">
10 <article>
4 <section class="docs">
5 <div class="grid">
6 {%- include tutorials_contents_mobile.html -%}
7 <div class="unit four-fifths">
8 <article>
9 <header class="tutorial-header">
1110 <div class="improve right hide-on-mobiles">
1211 <a href="https://github.com/jekyll/jekyll/edit/master/docs/{{ page.path }}"><i
13 class="fa fa-pencil"></i> &nbsp;Improve this page</a>
12 class="fa fa-pencil"></i> &nbsp;Improve this page</a>
1413 </div>
15 <h1>{{ page.title }}</h1>
16 {{ content }}
17 {% include section_nav_tutorials.html %}
18 </article>
19 </div>
20
21 {% include tutorials_contents.html %}
22
23 <div class="clear"></div>
24
14 <h1 class="tutorial-title">{{ page.title }}</h1>
15 {% assign author = page.author %}
16 {% if author %}
17 <div class="tutorial-meta">
18 <span class="tutorial-date">
19 {{ page.date | date_to_long_string }}
20 </span>
21 <a href="https://github.com/{{ author }}" class="tutorial-author">
22 {% avatar user=author size=24 %}
23 <span class="author-name">{{ author }}</span>
24 </a>
25 </div>
26 {% endif %}
27 </header>
28 {{- content -}}
29 {% if page.plugin_disclaimer %}
30 <div class="disclaimer-ribbon">
31 Disclaimer: The Jekyll Core Team does not endorse the Ruby code in this tutorial and is
32 not liable to resolve any bugs therein or issues arising otherwise from the use of the
33 provided example(s) in production builds.
34 </div>
35 {% endif %}
36 {%- include section_nav_tutorials.html -%}
37 </article>
2538 </div>
26 </section>
39 {%- include tutorials_contents.html -%}
40 <div class="clear"></div>
41 </div>
42 </section>
22 date: "2013-05-06 02:12:52 +0200"
33 author: parkr
44 version: 1.0.0
5 categories: [release]
5 category: release
66 ---
77
88 Hey! After many months of hard work by Jekyll's contributors, we're excited
22 date: "2013-05-08 23:46:11 +0200"
33 author: parkr
44 version: 1.0.1
5 categories: [release]
5 category: release
66 ---
77
88 Hot on the trails of v1.0, v1.0.1 is out! Here are the highlights:
22 date: "2013-05-12 14:45:00 +0200"
33 author: parkr
44 version: 1.0.2
5 categories: [release]
5 category: release
66 ---
77
88 v1.0.2 has some key bugfixes that optionally restore some behaviour from pre-1.0
1111 * Backwards-compatibilize relative permalinks ([#1081][])
1212 * Add `jekyll doctor` command to check site for any known compatibility problems ([#1081][])
1313 * Deprecate old config `server_port`, match to `port` if `port` isn't set ([#1084][])
14 * Update pygments.rb and kramdon versions to 0.5.0 and 1.0.2, respectively ([#1061][], [#1067][])
14 * Update pygments.rb and kramdown versions to 0.5.0 and 1.0.2, respectively ([#1061][], [#1067][])
1515 * Fix issue when post categories are numbers ([#1078][])
1616 * Add a `data-lang="<lang>"` attribute to Redcarpet code blocks ([#1066][])
1717 * Catching that Redcarpet gem isn't installed ([#1059][])
22 date: "2013-06-07 21:02:13 +0200"
33 author: parkr
44 version: 1.0.3
5 categories: [release]
5 category: release
66 ---
77
88 v1.0.3 contains some key enhancements and bug fixes:
22 date: "2013-07-14 19:38:02 +0200"
33 author: parkr
44 version: 1.1.0
5 categories: [release]
5 category: release
66 ---
77
88 After a month of hard work, the Jekyll core team is excited to announce the release of
22 date: "2013-07-24 22:24:14 +0200"
33 author: parkr
44 version: 1.1.1
5 categories: [release]
5 category: release
66 ---
77
88 Coming just 10 days after the release of v1.1.0, v1.1.1 is out with a patch for
22 date: "2013-07-25 09:08:38 +0200"
33 author: mattr-
44 version: 1.0.4
5 categories: [release]
5 category: release
66 ---
77
88 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.
22 date: "2013-07-25 09:08:38 +0200"
33 author: parkr
44 version: 1.1.2
5 categories: [release]
5 category: release
66 ---
77
88 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.
22 date: "2013-09-06 22:02:41 -0400"
33 author: parkr
44 version: 1.2.0
5 categories: [release]
5 category: release
66 ---
77
88 After nearly a month and a half of hard work, the Jekyll team is happy to
22 date: 2013-09-14 20:46:50 -0400
33 author: parkr
44 version: 1.2.1
5 categories: [release]
5 category: release
66 ---
77
88 Quick turnover, anyone? A [recent incompatibility with Liquid
22 date: 2013-10-28 20:14:39 -0500
33 author: mattr-
44 version: 1.3.0.rc1
5 categories: [release]
5 category: release
66 ---
77
88 Jekyll 1.3.0 is going to be a big release! In order to make sure we
22 date: 2013-11-04 21:46:02 -0600
33 author: mattr-
44 version: 1.3.0
5 categories: [release]
5 category: release
66 ---
77
88 It's been about six weeks since v1.2.0 and the Jekyll team is happy to
22 date: 2013-11-26 19:52:20 -0600
33 author: mattr-
44 version: 1.3.1
5 categories: [release]
5 category: release
66 ---
77
88 Just in time for the US holiday Thanksgiving, we're releasing version
22 date: 2013-12-07 13:55:28 -0600
33 author: mattr-
44 version: 1.4.0
5 categories: [release]
5 category: release
66 ---
77
88 About a month after the release of Jekyll v1.3.0, we are releasing
22 date: 2013-12-09 20:44:13 -0600
33 author: mattr-
44 version: 1.4.1
5 categories: [release]
5 category: release
66 ---
77
88 Another quick turnover, anyone? A [critical
22 date: 2013-12-16 19:48:13 -0500
33 author: parkr
44 version: 1.4.2
5 categories: [release]
5 category: release
66 ---
77
88 This release fixes [a regression][] where Maruku fenced code blocks were turned
22 date: 2014-01-13 17:43:32 -0800
33 author: benbalter
44 version: 1.4.3
5 categories: [release]
5 category: release
66 ---
77
88 Jekyll 1.4.3 contains two **critical** security fixes. If you run Jekyll locally
22 date: 2014-03-24 20:37:59 -0400
33 author: parkr
44 version: 1.5.0
5 categories: [release]
5 category: release
66 ---
77
88 As work continues on Jekyll 2.0.0, we felt it was important to address two key
22 date: 2014-03-27 22:43:48 -0400
33 author: parkr
44 version: 1.5.1
5 categories: [release]
5 category: release
66 ---
77
88 The hawk-eyed [@gregose](https://github.com/gregose) spotted a bug in our
11 title: 'Jekyll turns 2.0.0'
22 author: parkr
33 version: 2.0.0
4 categories: [release]
4 category: release
55 ---
66
77 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.
1111 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!
1212 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.
1313 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 &mdash; no more messing around with Rake or Grunt!
14 4. [YAML 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.
15 5. [Custom markdown processors](/docs/configuration/#custom-markdown-processors) - 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.
16 6. [Addition of `where` and `group_by` Liquid filters](/docs/templates/#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.
14 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.
15 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.
16 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.
1717 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!
1818
1919 Check out our [changelog][] for a complete list of all (200+) changes.
22 date: 2014-05-08 22:43:17 -0400
33 author: parkr
44 version: 2.0.3
5 categories: [release]
5 category: release
66 ---
77
88 Hey again! Just wanted to let you know we've released another version of Jekyll, jam-packed with bug fixes.
1313 The StickerMule team says, *"Pine no longer!"* StickerMule has **[discounted the
1414 price of Jekyll stickers down to $1 and are offering free (domestic)
1515 shipping](https://www.stickermule.com/marketplace/825-jekyll-stickers)!**
16 Go grab one now on the StickerMule marketplace – [they'll look
17 swell on your favourite hardware.](https://twitter.com/parkr/status/430826309707902976/photo/1)
16 Go grab one now on the StickerMule marketplace – they'll look
17 swell on your favourite hardware.
22 date: 2014-06-28 17:26:59 -0400
33 author: parkr
44 version: 2.1.0
5 categories: [release]
5 category: release
66 ---
77
88 Jekyll's finally [legal to drink in the States](https://en.wikipedia.org/wiki/Legal_drinking_age).
1818 - Add support for `hl_lines` in `highlight` tag (#2532)
1919 - Post categories now merge with directory, front matter, and defaults (#2373)
2020 - New `--skip_initial_build` flag for `jekyll serve` (#2477)
21 - A bajilion bug fixes and site updates!
21 - A bajillion bug fixes and site updates!
2222
2323 Let's go party!
2424
22 date: 2014-07-01 20:16:43 -0400
33 author: parkr
44 version: 2.1.1
5 categories: [release]
5 category: release
66 ---
77
88 This is a minor release for Jekyll 2.1.0. It fixes a couple bugs and
22 date: 2014-07-29 18:59:13 -0400
33 author: parkr
44 version: 2.2.0
5 categories: [release]
5 category: release
66 ---
77
88 Jekyll 2.2.0 contains a few key updates:
22 date: 2014-08-10 20:38:34 -0400
33 author: parkr
44 version: 2.3.0
5 categories: [release]
5 category: release
66 ---
77
88 This latest release of Jekyll includes a slew of enhancements and bug
1313 * Pages, Posts, and Drafts can now be converted by multiple converters.
1414 * Static files can now be safely included in collections. They'll be placed
1515 in a `collection.files` array. `collection.docs` still holds exclusively
16 content with YAML front matter.
16 content with front matter.
1717 * Sass files can once again be rendered by Liquid. However, neither Sass
1818 nor CoffeeScript can ever have a layout. Bonus: `scssify` and `sassify`
1919 Liquid filters.
22 date: 2014-09-09 21:10:33 -0700
33 author: parkr
44 version: 2.4.0
5 categories: [release]
5 category: release
66 ---
77
88 Well, lookie here! A new release of Jekyll! v2.4.0 contains lots of goodies, including some brilliant new additions:
22 date: 2014-11-05 10:48:22 -0800
33 author: parkr
44 version: 2.5.0
5 categories: [release]
5 category: release
66 ---
77
88 A new day, a new release! Jekyll just turned 2.5.0 and has gained a lot of
1212 highlights:
1313
1414 * Require plugins in the `:jekyll_plugins` Gemfile group (turned off with an environment variable)
15 * YAML Front Matter permalinks can now contain placeholders like `:name`. Check out all the placeholders on the [Permalinks docs page](/docs/permalinks/).
15 * Front matter permalinks can now contain placeholders like `:name`. Check out all the placeholders on the [Permalinks docs page](/docs/permalinks/).
1616 * The `jsonify` filter now deep-converts arrays to liquid.
1717 * Shorted `build` and `serve` commands with `b` and `s` aliases, respectively
1818 * WEBrick will now list your directory if it can't find an index file.
2727
2828 As always, if you run into issues, please [check the issues]({{ site.repository }}/issues)
2929 and [create an issue if one doesn't exist for the bug you encountered]({{ site.repository }}/issues/new).
30 If you just need some help, the extraordinary [jekyll help team is here for
31 you!]({{ site.help_url }})
30 If you just need some help, the extraordinary jekyll help team is here for
31 you!
3232
3333 *When was the [first commit to Jekyll](https://github.com/jekyll/jekyll/commit/d189e05d236769c1e5594af9db4d6eacb86fc16e)?
3434 All the way back on October 19, 2008. It features interesting historical
22 date: 2014-11-09 09:47:52 -0800
33 author: parkr
44 version: 2.5.1
5 categories: [release]
5 category: release
66 ---
77
88 Hot on the heels of v2.5.0, this release brings relief to our Windows
22 date: 2014-11-12 18:49:08 -0800
33 author: parkr
44 version: 2.5.2
5 categories: [release]
5 category: release
66 ---
77
88 A very minor release, 2.5.2 fixes a bug with path sanitation that 2.5.1
22 date: 2014-12-22 09:03:30 -0500
33 author: parkr
44 version: 2.5.3
5 categories: [release]
5 category: release
66 ---
77
88 Happy Holidays, everyone.
22 date: 2015-01-24 00:42:31 -0800
33 author: parkr
44 version: 3.0.0.beta1
5 categories: [release]
5 category: release
66 ---
77
88 Hey!
88
99 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.
1010
11 There's already been a lot of interesting topics, including a [site showcase](https://talk.jekyllrb.com/t/showcase-sites-made-using-jekyll/18) and [a poll for Jekyll 3.0 priorities](https://talk.jekyllrb.com/t/poll-installation-priorities-for-3-0/106/9).
11 There's already been a lot of interesting topics, including a site showcase and a poll for Jekyll 3.0 priorities.
1212
1313 Come join the fun!
22 date: 2015-10-26 15:37:30 -0700
33 author: parkr
44 version: 3.0
5 categories: [release]
5 category: release
66 ---
77
88 The much-anticipated Jekyll 3.0 has been released! Key changes:
22 date: 2015-11-17 22:04:39 -0800
33 author: parkr
44 version: 3.0.1
5 categories: [release]
5 category: release
66 ---
77
88 Hey, folks! Bunch of bug fixes here. Notables:
1414 * All hooks should now be properly registered & documented
1515
1616 And a bunch more changes which you can see over in the
17 [changelog](/docs/history).
17 [changelog](/docs/history/).
1818
1919 Thanks to the 17 developers who contributed code and documentation to this
2020 patch release: Alfred Xing, Christian Trosell, Jordan Thornquest, Jordon
22 date: 2016-01-20 14:08:18 -0800
33 author: parkr
44 version: 3.0.2
5 categories: [release]
5 category: release
66 ---
77
88 A crucial bug was found in v3.0.1 which caused invalid post dates to go
22 date: 2016-01-24 13:16:12 -0800
33 author: parkr
44 version: 3.1.0
5 categories: [release]
5 category: release
66 ---
77
88 Happy weekend! To make your weekend all the better, we have just released
2020 * Fix: `jekyll clean` now accepts build flags like `--source`.
2121 * Enhancement: `include` tags can now accept multiple liquid variables
2222 * Feature: adds new `sample` liquid tag which gets random element from an array
23 * Fix: Jekyll will read in files with YAML front matter that has extraneous
23 * Fix: Jekyll will read in files with front matter that has extraneous
2424 spaces after the first line
2525 * Enhancement: extract the `title` attribute from the filename for
2626 collection items without a date
22 date: 2016-01-28 17:21:50 -0800
33 author: parkr
44 version: 3.1.1
5 categories: [release]
5 category: release
66 ---
77
88 This release squashes a few bugs :bug: :bug: :bug: noticed by a few
22 date: 2016-02-08 10:39:08 -0800
33 author: parkr
44 version: 3.0.3
5 categories: [release]
5 category: release
66 ---
77
88 [GitHub Pages upgraded to Jekyll 3.0.2][1] last week. With a testbed of
22 date: 2016-02-19 15:24:00 -0800
33 author: parkr
44 version: 3.1.2
5 categories: [release]
5 category: release
66 ---
77
88 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:
22 date: 2016-04-19 10:26:12 -0700
33 author: parkr
44 version: 3.0.4
5 categories: [release]
5 category: release
66 ---
77
88 v3.0.4 is a patch release which fixes the follow two issues:
22 date: 2016-04-19 10:26:16 -0700
33 author: parkr
44 version: 3.1.3
5 categories: [release]
5 category: release
66 ---
77
88 v3.1.3 is a patch release which fixes the follow two issues:
22 date: 2016-04-26 17:40:44 -0700
33 author: parkr
44 version: 3.0.5
5 categories: [release]
5 category: release
66 ---
77
88 This version fixes a bug affecting only v3.0.4 where autoregeneration was
22 date: 2016-05-18 16:50:37 -0700
33 author: parkr
44 version: 3.1.4
5 categories: [release]
5 category: release
66 ---
77
88 Hey Jekyllites!
1717
1818 The fixes for `layout` may not be seamless for everyone, but we believe they will be the "right thing to do" going forward.
1919
20 We are alwawys 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.
20 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.
2121
2222 For a full history of our changes, [see the changelog](/docs/history/#v3-1-4).
2323
22 date: 2016-05-18 21:35:27 -0700
33 author: parkr
44 version: 3.1.5
5 categories: [release]
5 category: release
66 ---
77
88 There's always at least one bug, right? :)
22 date: 2016-05-19 12:48:14 -0700
33 author: parkr
44 version: 3.1.6
5 categories: [release]
5 category: release
66 ---
77
88 Upon releasing 3.1.5 and kicking the tires, we noticed a glaring bug: our
22 date: 2016-07-26 15:06:49 -0700
33 author: parkr
44 version: 3.2.0
5 categories: [release]
5 category: release
66 ---
77
88 Happy Day! Jekyll v3.2.0 is out, and packed full of goodies.
22 date: 2016-08-02 13:20:11 -0700
33 author: parkr
44 version: 3.2.1
5 categories: [release]
5 category: release
66 ---
77
88 Well, 3.2.0 has been a success, but with one fatal flaw: it doesn't work on
22 date: 2016-10-06 11:10:38 -0700
33 author: parkr
44 version: 3.3.0
5 categories: [release]
5 category: release
66 ---
77
88 There are tons of great new quality-of-life features you can use in 3.3.
1919 user's site. This means you can ship SCSS and CoffeeScript, images and
2020 webfonts, and so on -- anything you'd consider a part of the
2121 *presentation*. Same rules apply here as in a Jekyll site: if it has YAML
22 front matter, it will be converted and rendered. No YAML front matter, and
22 front matter, it will be converted and rendered. No front matter, and
2323 it will simply be copied over like a static asset.
2424
2525 Note that if a user has a file of the same path, the theme content will not
3737 then `relative_url` will ensure that this baseurl is prepended to anything
3838 you pass it:
3939
40 {% highlight liquid %}
4140 {% raw %}
41 ```liquid
4242 {{ "/docs/assets/" | relative_url }} => /myproject/docs/assets
43 ```
4344 {% endraw %}
44 {% endhighlight %}
4545
4646 By default, `baseurl` is set to `""` and therefore yields (never set to
4747 `"/"`):
4848
49 {% highlight liquid %}
5049 {% raw %}
50 ```liquid
5151 {{ "/docs/assets/" | relative_url }} => /docs/assets
52 ```
5253 {% endraw %}
53 {% endhighlight %}
5454
5555 A result of `relative_url` will safely always produce a URL which is
5656 relative to the domain root. A similar principle applies to `absolute_url`.
5757 It prepends your `baseurl` and `url` values, making absolute URLs all the
5858 easier to make:
5959
60 {% highlight liquid %}
6160 {% raw %}
61 ```liquid
6262 {{ "/docs/assets/" | absolute_url }} => https://jekyllrb.com/myproject/docs/assets
63 ```
6364 {% endraw %}
64 {% endhighlight %}
6565
6666 ### 3. `site.url` is set by the development server
6767
22 date: 2016-11-14 14:29:59 -0800
33 author: parkr
44 version: 3.3.1
5 categories: [release]
5 category: release
66 ---
77
88 Hello! We have a bugfix release of Jekyll hot off the presses for you. Key
22 date: 2017-01-18 14:19:13 -0500
33 author: parkr
44 version: 3.4.0
5 categories: [release]
5 category: release
66 ---
77
88 Hey there! We have a quick update of Jekyll for you to enjoy this January.
99 Packed full of bug fixes as usual, thanks to the tireless efforts of our
1010 exceptional Jekyll community. Three changes to call out:
1111
12 1. If you're a big fan of [`where_by_exp`](/docs/templates/#filters), you'll be an
13 even bigger fan of [`group_by_exp`](/docs/templates/#filters).
12 1. If you're a big fan of [`where_by_exp`](/docs/liquid/filters/), you'll be an
13 even bigger fan of [`group_by_exp`](/docs/liquid/filters/).
1414 2. Using a custom timezone in Jekyll on Windows? Yeah, sorry that hasn't ever worked
1515 properly. We made it possible to accurately [set the timezone using IANA
16 timezone codes](https://jekyllrb.com/docs/windows/#timezone-management).
16 timezone codes](/docs/installation/windows/#time-zone-management).
1717 3. Documentation has been improved, notably on themes, includes and permalinks.
1818
1919 And [lots and lots more!](/docs/history/#v3-4-0)
22 date: 2017-03-02 14:20:26 -0500
33 author: parkr
44 version: 3.4.1
5 categories: [release]
5 category: release
66 ---
77
88 Conformity is a confounding thing.
22 date: 2017-03-09 15:41:57 -0500
33 author: parkr
44 version: 3.4.2
5 categories: [release]
5 category: release
66 ---
77
88 Another one-PR patch update, though without the same [lessons as for the
1212 **static files now respect front matter defaults**.
1313
1414 You might be asking yourself: "why would static files, files that are
15 static files explicitly because they *don't* have YAML front matter, want
16 to respect YAML front matter?" That's a great question. Let me illustrate
15 static files explicitly because they *don't* have front matter, want
16 to respect front matter?" That's a great question. Let me illustrate
1717 with an example.
1818
1919 Let's look at `jekyll-sitemap`. This plugin generates a list of documents,
2020 pages, and static files, and some metadata for them in an XML file for a
2121 Google/Yahoo/Bing/DuckDuckGo crawler to consume. If you don't want a given
22 file in this list, you set `sitemap: false` in the YAML front matter. But
23 what about static files, which don't have YAML front matter? Before this
22 file in this list, you set `sitemap: false` in front matter. But
23 what about static files, which don't have front matter? Before this
2424 release, they could not be excluded because they had no properties in YAML
2525 other than [the ones we explicitly assigned](https://github.com/jekyll/jekyll/blob/v3.4.1/lib/jekyll/static_file.rb#L98-L106).
2626 So if you had a PDF you didn't want to be in your sitemap, you couldn't use
2727 `jekyll-sitemap`.
2828
2929 With this release, you can now set [front matter
30 defaults](/docs/configuration/#front-matter-defaults) for static files:
30 defaults](/docs/configuration/front-matter-defaults/) for static files:
3131
3232 ```yaml
3333 defaults:
22 date: 2017-03-21 08:52:53 -0500
33 author: pathawks
44 version: 3.4.3
5 categories: [release]
5 category: release
66 ---
77
88 Another one-PR patch update as we continue our quest to destroy all bugs. A
22 date: 2017-06-15 17:32:32 -0400
33 author: parkr
44 version: 3.5.0
5 categories: [release]
5 category: release
66 ---
77
88 Good news! Nearly 400 commits later, Jekyll 3.5.0 has been released into
22 date: 2017-07-17 12:40:37 -0400
33 author: parkr
44 version: 3.5.1
5 categories: [release]
5 category: release
66 ---
77
88 We've released a few bugfixes in the form of v3.5.1 today:
22 date: 2017-08-12 16:31:40 -0400
33 author: parkr
44 version: 3.5.2
5 categories: [release]
5 category: release
66 ---
77
88 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:
22 date: 2017-09-21 16:38:20 -0400
33 author: parkr
44 version: 3.6.0
5 categories: [release]
5 category: release
66 ---
77
88 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.
2020
2121 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.
2222
23 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!
23 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!
2424
2525 ## Create newcomer-friendly issues
2626
22 date: 2017-10-21 21:31:40 +0200
33 author: dirtyf
44 version: 3.6.2
5 categories: [release]
5 category: release
66 ---
77
88 3.6.2 is out, it's a tiny patch release and we invite you to run `bundle update`
1212 * fully numeric layout names (we convert those to string for you now).
1313
1414 Other changes include updates to our documentation, like this [complete
15 video series by Giraffe Academy](../tutorials/video-walkthroughs/) aimed at
15 video series by Giraffe Academy](/tutorials/video-walkthroughs/) aimed at
1616 complete beginners. A big thanks to Mike for this.
1717
1818 And if you're wondering what happened to version 3.6.1, it was just our new
33 date: 2018-01-02 11:21:40 +0100
44 author: DirtyF
55 version: 3.7.0
6 categories: [release]
6 category: release
77 ---
88
99 We're happy to release a new minor for the new year.
1010 Here are a few of the latest additions from our contributors:
1111
1212 * 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.
13 * 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: .
13 * 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: .
1414 * 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.
1515 * Jekyll will now check to determine whether host machine has internet connection.
16 * A new `latin` option is available to better [handle URLs slugs](/docs/templates/#options-for-the-slugify-filter).
16 * A new `latin` option is available to better [handle URLs slugs](/docs/liquid/filters/#options-for-the-slugify-filter).
1717 * And of course many bug fixes and updates to our documentation — which you can now search thanks to our friends @Algolia.
1818 * [Full history is here](/docs/history/#v3-7-0).
1919
22 date: 2018-01-25 22:22:22 +0530
33 author: ashmaroli
44 version: 3.7.2
5 categories: [release]
5 category: release
66 ---
77
88 Close on the heels of shipping 3.7.0, we were informed of a couple of
99 regressions due to the changes made in that release. In due time, Team Jekyll
1010 set out to address those issues as early as possible.
1111
12 Days later here we're, announcing 3.7.2 (sorry for skipping 3.7.1,
12 Days later here we're, announcing 3.7.2 (sorry for skipping 3.7.1,
1313 RubyGems didn't want to play nice) that fixes numerous issues! :tada:
1414 The highlights being:
1515
2323 We addressed this by having Jekyll scan the directory path only if the user
2424 explicitly configures the `scope["path"]` using wildcards.
2525
26 Read our [documentation](/docs/configuration/#glob-patterns-in-front-matter-defaults)
26 Read our [documentation](/docs/configuration/front-matter-defaults/#glob-patterns-in-front-matter-defaults)
2727 for more details.
2828
2929 A huge shout-out to @mmistakes for bringing this to our notice and
4545 Ergo, if you set a custom location for your collections, please ensure you
4646 move all of your collections into that directory. **This includes posts and
4747 drafts as well**. Your links generated by
48 `{% raw %}{% post_url %}{% endraw %}` or `{% raw %}{% link %}{% endraw %}`
48 {% raw %}`{% post_url %}`{% endraw %} or {% raw %}`{% link %}`{% endraw %}
4949 will adapt automatically.
5050
5151 * We also found out that `gem "wdm"` boosts performance while directories are
55 categories: [team]
66 ---
77
8 Jekyll has a new Lead Developer: [Olivia](https://liv.cat/)!
8 Jekyll has a new Lead Developer: Olivia!
99
1010 After over 5 years of leading Jekyll, many releases from 0.12.1 to 3.6.0, and
1111 countless conversations in GitHub Issues, Pull Requests, Jekyll Talk, and
1818 Node.js community, both online and as a volunteer organizer with JSConf EU.
1919
2020 In my conversations with Olivia, it is clear that Jekyll's vision of
21 simplicity for the user ([no magic!](/philosophy#1-no-magic)) and letting
22 users' [content be king](/philosophy#3-content-is-king) will remain a top
21 simplicity for the user ([no magic!](/philosophy/#1-no-magic)) and letting
22 users' [content be king](/philosophy/#3-content-is-king) will remain a top
2323 priority. In just the last few weeks as the transition has been occurring,
2424 we have seen some incredible work on performance that will make future
2525 versions of Jekyll work better at scale. She will be prioritizing work on
22 date: 2018-02-25 13:02:08 +0530
33 author: ashmaroli
44 version: 3.7.3
5 categories: [release]
5 category: release
66 ---
77
88 Hello Jekyllers!! :wave:
0 ---
1 title: "Jekyll 4.0 is on the Horizon!"
2 date: "2018-04-19 16:07:00 +0100"
3 author: oe
4 categories: [community]
5 ---
6
7 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:
8
9 - Dropping support for Ruby 2.1 and 2.2. Both versions have reached their EOL period.
10 - Dropping Pygments as a dependency. We're already defaulting to Rouge, and this removes the implicit Python dependency. (finally!)
11 - Making the `link` tag use relative URLs. This is a big breaking change, but it's the cleaner solution.
12
13 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.
14
15 That being said, the development period of version 4.0 begins _now_. This means a couple of things:
16
17 - New features will only be implemented in Jekyll 4.0. There will be no 3.9.0 or the like.
18 - 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.
19 - 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!
20
21 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.
22
23 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!
24
25 Happy Jekylling! :wave:
26
27 [issue]: https://github.com/jekyll/jekyll/issues/6948
0 ---
1 title: 'Jekyll 3.8.0 Released'
2 date: 2018-04-19 19:45:15 +0530
3 author: ashmaroli
4 version: 3.8.0
5 category: release
6 ---
7
8 Aloha Jekyllers!! :wave:
9
10 After months of toiling on the codebase and shipping a couple of release-candidates, the Jekyll Team is delighted to finally
11 present `v3.8.0`, packed with optimizations, improvements, some new features and a couple of bug-fixes. Yay!!!
12
13 Under the hood, Jekyll has undergone many minor changes that will allow it to run more performantly in the coming years. :smiley:
14 Rest assured, our users should see minor improvements in their site's build times.
15
16 Speaking of improvements, users running a site containing a huge amount of posts or those who like to use our `where` filter
17 frequently in a single template, are going to see a massive reduction in their total build times!! :tada:
18
19 Hold on, this version is not just about optimizations, there are some new features as well..:
20 * Detect non-existent variables and filters specified in a template by enabling `strict_variables` and `strict_filters` under the
21 `liquid` key in your config file.
22 * Allow *date filters* to output ordinal days.
23 * `jekyll doctor` now warns you if you have opted for custom `collections_dir` but placed `_posts` directory outside that
24 directory.
25
26 ..and yes, a couple of bug-fixes, notably:
27 * Jekyll now handles future-dated documents properly.
28 * Jekyll is able to handle Liquid blocks intelligently in excerpts.
29 * A few methods that were *not meant to be publically accessible* have been entombed properly.
30 * A few bugs that still plagued our `collections_dir` feature from `v3.7` got crushed.
31
32 As always, the full list of changes since last release can be viewed [here](/docs/history/#v3-8-0).
33
34 A big thanks to the following people who contributed to our repository with pull-requests that improved our codebase, documentation
35 and tests:
36
37 Ana María Martínez Gómez, Antonio Argote, Ashwin Maroli, Awjin Ahn, Ben Balter, Benjamin Høegh, Christian Oliff, Damien Solodow,
38 David Zhang, Delson Lima, Eric Cornelissen, Florian Thomas, Frank Taillandier, Heinrich Hartmann, Jakob Vad Nielsen, John Eismeier,
39 Kacper Duras, KajMagnus, Mario Cekic, Max Vilimpoc, Michael H, Mike Kasberg, Parker Moore, Pat Hawks, Paweł Kuna, Robert Riemann,
40 Roger Rohrbach, Semen Zhydenko, Stefan Dellmuth, Tim Carry, olivia, and steelman.
41
42 Happy Jekylling!! :sparkles:
22 date: 2018-05-01 11:56:01 -0500
33 author: pathawks
44 version: 3.8.1
5 categories: [release]
5 category: release
66 ---
77
88 Happy May Day :tada:
22 date: 2018-05-19 10:30:00 -0500
33 author: pathawks
44 version: 3.8.2
5 categories: [release]
5 category: release
66 ---
77
88 Hello Jekyllers!!
22 date: 2018-06-05 09:00:00 -0500
33 author: pathawks
44 version: 3.8.3
5 categories: [release]
5 category: release
66 ---
77
88 This release fixes a regression in 3.8 where collections with `published: false`
0 ---
1 title: "Sponsoring Jekyll's development"
2 date: 2018-08-01 15:00:00 +0200
3 author: oe
4 categories: [community]
5 ---
6
7 _(TL;DR: We're open for sponsorships on our [OpenCollective page](https://opencollective.com/jekyll))_
8
9 Hi Jekyllers,
10
11 As you may know, Jekyll is a completely free and open source project. We offer
12 our software and its related plugins and documentation at no cost because we
13 believe that good software should not cost anything. We're not planning on
14 changing that, but today I want to talk about a different monetary aspect of
15 open source.
16
17 Open source developers being paid for the work they do is a rare sight. Most
18 open source software is effectively the result of hundreds and thousands of
19 hours of free labor provided by individuals who are passionate enough to work
20 outside of their day job to create software that, ironically, is being used by
21 almost every company that offers digital services. It's a problem that has
22 gotten more attention in recent years, with the open source community becoming
23 more diverse and more and more companies actively investing in providing
24 monetary support for open source developers.
25
26 Jekyll has always been a product of volunteers. Rarely has someone been paid to
27 implement a certain plugin or feature. Today, we're excited to announce that we
28 will finally be able to fund our contributors! __We are opening an
29 OpenCollective to receive individual and corporate sponsorships__.
30 This is not unheard of, [Hugo](http://gohugo.io) is also funded by sponsorships,
31 as are many other similar projects, such as
32 [webpack](https://opencollective.com/webpack),
33 [Babel](https://opencollective.com/babel) or
34 [RuboCop](https://opencollective.com/rubocop).
35
36 OpenCollective is a service that makes it easy for open source projects to
37 receive funding from individuals and companies alike. It's specifically designed
38 for open source and many other projects already use it for funding.
39
40 Sponsoring is, for us, a method to finally realize some of the more ambitious
41 goals we've had with the project for years. The closest thing we want to realize
42 is to __release Jekyll 4.0, and to make it as polished as we can__. In the
43 future, we would also like to work on other things that will improve the Jekyll
44 ecosystem. Here's a couple of ideas:
45
46 - Create a comprehensive official plugin and theme directory site
47 - Improve tooling built around measuring and improving Jekyll's performance
48 - Improve maintenance for official plugins
49 - Including the community into official decisions; making Jekyll more friendly to folks in the community
50
51 Again, these are just some ideas, but with the help of sponsoring, they are now
52 one step closer to being realized :heart:
53
54 <div align="center" style="background-color: white;padding: 1em;">
55 <a href="https://forestry/io"><img src="/img/forestry-logo.svg" alt="Forestry" /></a>
56 </div>
57
58 With that, we would like to announce our very first sponsor:
59 [__Forestry.io__](https://forestry.io)!
60 Forestry is a CMS that integrates with your Jekyll sites and lets you update
61 content using a beautiful interface, and then automatically commits it back to
62 your GitHub repository. We're excited to have them on board on a new, exciting
63 step of our journey.
64
65 Will anything change for Jekyll users? The answer is no - this step does not
66 impact the Jekyll software in any aspect. In fact, you might see positive
67 changes, such as more features and better performance. Surprisingly, that's what
68 happens when you properly fund people for their work!
69
70 If you have been a long time user for Jekyll and would like to give something
71 back to the project, you can consider a small monthly donation to our
72 [OpenCollective page](http://opencollective.com/jekyll). If your company heavily
73 relies on Jekyll, do consider sponsoring us!
74
75 Contact [matt@jekyllrb.com](mailto:matt@jekyllrb.com) and we'll figure something out together.
76
77 Thanks for sticking with us, and happy Jekylling! :tada:
0 ---
1 title: "Security Fixes for series 3.6, 3.7 and 3.8"
2 date: 2018-09-19 18:00:00 +0530
3 author: ashmaroli
4 category: release
5 version: 3.8.4
6 ---
7
8 Hi Jekyllers,
9
10 We have patched a **critical vulnerability** reported to GitHub a couple of weeks ago and have released a set of new gems to
11 bring that patch to you. The vulnerability allowed arbitrary file reads with the cunning use of the `include:` setting in the
12 config file.
13
14 By simply including a symlink in the `include` array allowed the symlinked file to be read into the build when they shouldn't
15 actually be read in any circumstance.  
16 Further details regarding the patch can be viewed at the [pull request URL]({{ site.repository }}/pull/7224)
17
18 The patch has been released as versions `3.6.3`, `3.7.4` and `3.8.4`.  
19 Thanks to @parkr `v3.7.4` was released a couple of weeks prior and has been bundled with `github-pages-v192`.
20
21
22 Please keep in mind that this issue affects _all previously released Jekyll versions_. If you have not had
23 a good reason to upgrade to `3.6`, `3.7` or `3.8` yet, we advise that you do so at the earliest.
24
25 As always, Happy Jekylling! :sparkles:
0 ---
1 title: 'Jekyll 3.8.5 Released'
2 date: 2018-11-04 20:58:20 +0100
3 author: oe
4 version: 3.8.5
5 category: release
6 ---
7
8 This release fixes a bug where multiple Liquid tags were not supported in
9 excerpts.
10
11 Thanks to @ashmaroli for fixing this issue in [#7250].
12
13 Happy Jekylling!
14
15 [#7250]: https://github.com/jekyll/jekyll/pull/7250
0 ---
1 title: Jekyll 4.0.0.pre.alpha1 Released
2 date: 2019-03-18 18:17:31 +0100
3 author: dirtyf
4 version: 4.0.0.pre.alpha1
5 category: release
6 ---
7
8 Dear Jekyllers,
9
10 Time has come to release a first alpha for Jekyll 4!
11
12 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.
13
14 If you're a plugin developer, we definitely need your feedback, especially if your plugin does not work with v4.
15
16 Jekyll now exposes a [caching API](/tutorials/cache-api/), that some plugins could benefit from.
17
18 Also be aware that it's a new *major* version, and it comes with a few breaking changes, notably :
19
20 1. We dropped support for [Ruby 2.3 who goes EOL at the end of the month](https://www.ruby-lang.org/en/downloads/).
21 GitHub Pages runs Ruby 2.5.x, services like Netlify or Forestry already upgraded to latest Ruby 2.6.x.
22 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).
23 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.
24 4. We dropped support for Pygments, RedCarpet and rdiscount.
25 5. We bumped kramdown to v2.
26
27 Checkout the complete [changelog](https://github.com/jekyll/jekyll/releases/tag/v4.0.0.pre.alpha1) for more details.
28
29 To test this pre version run:
30
31 ```sh
32 gem install jekyll --pre
33 ```
34
35 Please test this version thoroughly and file bugs as you encounter them.
36
37 Thanks to our dear contributors for helping making Jekyll better:
38
39 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
40
41 Happy Jekylling everyone!
0 ---
1 title: Jekyll 4.0.0.pre.beta1 Released
2 date: 2019-08-04 10:43:31 -0500
3 author: mattr-
4 version: 4.0.0.pre.beta1
5 categories: [release]
6 redirect_from: /news/2019/07/20/jekyll-4-0-0-pre-beta1-released/
7 ---
8
9 Dear Jekyllers,
10
11 It's time for another pre-release of Jekyll 4! 🎉
12
13 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:
14
15 Jekyll 4.0 is a new *major* version and it comes with a few breaking changes, notably :
16
17 1. We dropped support for [Ruby 2.3 which EOL at the end of March 2019](https://www.ruby-lang.org/en/downloads/).
18 GitHub Pages runs Ruby 2.5.x, services like Netlify or Forestry already upgraded to latest Ruby 2.6.x.
19 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).
20 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.
21 4. We dropped support for Pygments, RedCarpet and rdiscount.
22 5. We bumped kramdown to v2.
23
24 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.
25
26 Checkout the complete [changelog](https://github.com/jekyll/jekyll/releases/tag/v4.0.0.pre.beta1) for more details.
27
28 To test this pre version run:
29
30 ```sh
31 gem install jekyll --pre
32 ```
33
34 Please test this version thoroughly and file bugs as you encounter them.
35
36 Thanks to our dear contributors for helping making Jekyll better:
37
38 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
39
40 Happy Jekylling everyone!
0 ---
1 title: 'Jekyll 4.0.0 Released'
2 date: 2019-08-20 10:00:00 -0500
3 author: mattr-
4 version: 4.0.0
5 category: release
6 ---
7
8 Hi! 👋 I bring some good news! Jekyll 4.0.0 is finally here! 🎉
9
10 There's quite a bit in this release to unpack, so let me hit the high points quickly:
11 - Ruby 2.4.0 or greater is now required.
12 - Rouge 3.0 or greater is now required for syntax highlighting.
13 - Jekyll builds should be much faster.
14 - Kramdown 2.1 is now the default markdown engine.
15 - Sass processing should be faster.
16 - We dropped support for a lot of stuff, specifically:
17 - Pygments
18 - RedCarpet
19 - RDiscount
20
21
22 Alright, so with the high points out of the way, let's get into the details a little bit.
23
24 ### Cache all the things! 💰
25
26 While some optimizations first made an appearance with Jekyll 3.8.0, Jekyll 4.0 takes
27 it to another level altogether.
28
29 Jekyll 4.0 caches the processing done by Liquid in memory. So every Liquid
30 template is processed only as required. If you have 10 pages depending on a
31 single layout, the layout is cached and that data is then rendered as per the
32 10 different contexts of the individual files.
33
34 There's also a disk cache! Jekyll can now cache data to disk to avoid repeated
35 processing of content that doesn't change between build sessions. Currently,
36 this is limited to markdown. So while the very first build will take a certain
37 amount of time, consequent builds for content that hasn't changed will take
38 much less time due to the disk-cache. Disk caching is disabled for `safe` mode,
39 however.
40
41 ### Super-powered content transformations 💪
42
43 We've upgraded Sass support so it should be faster. There's also
44 support for sourcemaps now! Under the hood, our Sass support uses the `SassC`
45 library now, which is supported directly by the Sass team, which should mean
46 better support for everybody in the long run.
47
48 Kramdown is updated to version 2.1. This also brings with it a bunch of changes
49 to the Kramdown configuration, as the Kramdown team have extracted a fair
50 number of features into separate gems. Support for GitHub Flavored Markdown is
51 enabled by default, but if you're using another Kramdown extension in your
52 site, you'll likely need to update your plugin configuration. See the [upgrade
53 guide](/docs/upgrading/3-to-4/) for more details.
54
55 The `link` and `post_url` tags no longer need `site.baseurl` prepended every
56 time they're used. Those tags now use our `relative_url` filter to take care of
57 this for you. Existing uses of the prepending pattern will break though!
58 Sorry! :sweat_smile:
59
60 A few other smaller features when it comes to content:
61 - The `link` tag understands Liquid variables in the same fashion our
62 `include` tag does now.
63 - Disable Liquid processing for a particular page / document by adding
64 `render_with_liquid: false` to its front matter.
65 - Liquid's binary `and` and `or` operations can be used in the `where_exp`
66 filter for more powerful filtering
67
68 There's some goodies for theme community as well. Developers may now bundle a
69 `config.yml` into their theme-gem to provide some boilerplate configurations for
70 the theme. Like other resources in the theme, these configuration values can also
71 be customized at the user's end.
72
73
74 Check out the [full history](/docs/history/#v4-0-0) and the various pull requests
75 for more details on all the enhancements and bug-fixes.
76
77 ### Upgrading 📈
78
79 First, read the [upgrade guide](/docs/upgrading/3-to-4/)!
80
81 Next, Edit your project's `Gemfile` to test Jekyll v4.x:
82
83 ```ruby
84 gem "jekyll", "~> 4.0"
85 ```
86
87 Then run `bundle update` to update all dependencies. Unless you're using
88 third-party plugins that haven't yet added support for Jekyll 4.0, you should be
89 good to go.
90
91 Plugins and themes authors must relax the jekyll dependency in their `gemspec` file
92 to allow for Jekyll v4.0:
93
94 `spec.add_runtime_dependency "jekyll", ">= 3.6", "< 5.0"`
95
96 If your favorite plugin hasn't relaxed that dependency yet, please gently
97 encourage them to do so. :slightly_smiling_face:
98
99 ### Have questions❓
100
101 Please reach out on our [community forum](https://talk.jekyllrb.com)
102
103
104 ### Thank you!! 🙇
105
106 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.
107
108 In addition, special thanks to the 139 contributors who made this
109 release possible via a pull request submission (in alphabetical order): Aidan
110 Fitzgerald, Akshat Kedia, Ale Muñoz, Alex Wood,
111 Alexey Kopytko, Alexey Pelykh, Ali Thompson, Ana María Martínez Gómez,
112 Ananthakumar, Andreas Möller, Andrew Lyndem, Andrew Marcuse, Andy Alt, Anne
113 Gentle, Anny, Anuj Bhatnagar, argv-minus-one, Arjun Thakur, Arthur Attwell,
114 Ashwin Maroli, Behrang, Belhassen Chelbi, Ben Keith, Ben Otte, Bilawal Hameed,
115 Bjorn Krols, Boris Schapira, Boris van Hoytema, Brett C, Chris Finazzo, Chris
116 Oliver, chrisfinazzo, Christian Oliff, Christoph Päper, Damien Solodow, Dan
117 Allen, Dan Friedman, Daniel Höpfl, David J. Malan, David Kennell, David Zhang,
118 Denis McDonald, Derek Smart, Derpy, Dusty Candland, Edgar Tinajero, Elvio
119 Vicosa, ExE Boss, Fons van der Plas, Frank Taillandier, Gareth Cooper, Grzegorz
120 Kaczorek, Haris Bjelic, Hodong Kim, ikeji, Isaac Goodman, Jacob Byers, Jakob
121 Krigovsky, James Rhea, Jan Pobořil, jess, jingze_lu, Joe Shannon, Jordan Morgan,
122 Jörg Steinsträter, Jorie Tappa, Josue Caraballo, jpasholk, Justin Vallelonga,
123 Karel Bílek, Keith Mifsud, Kelly-Ann Green, Ken Salomon, Kevin Plattret, krissy,
124 Kyle Barbour, Lars Kanis, Leandro Facchinetti, Liam Rosenfeld, Luis Enrique
125 Perez Alvarez, Luis Guillermo Yáñez, Ma HongJun, makmm, Manu Mathew, Mario,
126 Martin Scharm, Matt Kraai, Matt Massicotte, Matt Rogers, Matthew Rathbone,
127 Maxwell Gerber, Mertcan Yücel, Michael Bishop, Michael Hiiva, Michelle Greer,
128 Mike Kasberg, Mike Neumegen, mo khan, Monica Powell, Nicolas Hoizey, Nikhil
129 Benesch, Nikhil Swaminathan, Nikita Skalkin, Niklas Eicker, ninevra, Olivia
130 Hugger, Parker Moore, Pat Hawks, Patrick Favre-Bulle, Paul Kim, penguinpet,
131 Philip Belesky, Preston Lim, Ralph, Robert Riemann, Rosário Pereira Fernandes,
132 Sadik Kuzu, Samuel Gruetter, Scott Killen, Sri Pravan Paturi, Stephan Fischer,
133 Stephen Weiss, Steven Westmoreland, strangehill, Sundaram Kalyan Vedala, Thanos
134 Kolovos, Timo Schuhmacher, Tobias, Tom Harvey, Tushar Prajapati, Victor Afanasev,
135 Vinicius Flores, Vitor Oliveira, Wouter Schoot, XhmikosR, Yi Feng Xie, Zhang
136 Xiangze, 김정환, 104fps.
137
138 Happy Jekylling everyone!
0 ---
1 title: 'Jekyll 4.0.1 Released'
2 date: 2020-05-08 18:00:00 +0100
3 author: dirtyf
4 version: 4.0.1
5 category: release
6 ---
7
8 Jekyll 4.0.1 is out and fixes a few issues reported by the community since 4.0.
9
10 - 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)).
11 - Liquid variables are now properly cached.
12 - Jekyll build will no longer fail for collections with a custom permalink containing static files.
13 - Jekyll filters now properly recognize integers.
14
15 👀 A [release changelog](https://github.com/jekyll/jekyll/releases/tag/v4.0.1) is available for your perusal.
16
17 🙏 Many thanks to our contributors without whom this release could not be
18 possible: @ashmaroli and [Ivan Gromov](https://github.com/summerisgone)
19
20 Expect more fixes and improvements on the next minor release!
21
22 Happy Jekylling!
0 ---
1 title: 'Jekyll 4.1.0 Released'
2 date: 2020-05-27 15:20:30 +0530
3 author: ashmaroli
4 version: 4.1.0
5 category: release
6
7 filters_linked_to:
8 - where expression
9 - find expression
10 - find
11 - number of words
12 ---
13
14 Hello Jekyllers!
15
16 It's time for yet another release that includes enhancements, optimizations and bug-fixes. Highlights of this release
17 are:
18
19 * Jekyll now supports rendering excerpts for *pages* in addition to documents and posts.
20 * The [`where_exp`][where-expression-filter] filter got enhanced. Earlier, one could just use either `and` or `or` once
21 per expression. Now, one may use those binary operators multiple times in the filter's expression.
22 * Jekyll has a new set of filters based on *its flavor* of the `where` and `where_exp` filters. Named
23 [`find`][find-filter] and [`find_exp`][find-expression-filter] filters respectively, they work similar to their ancestors
24 except that they return **the first object** that satisfies the given conditions.
25 * Jekyll's [`number_of_words`][number-of-words-filter] filter can now take an optional argument to better count words
26 of text containing Chinese, Japanese or Korean characters.
27 * One may now use `:slugified_categories` in their permalink configurations to generate a more apt URL (categories are
28 downcased and non-alphanumeric characters replaced by dashes) for their for posts and documents.
29 * The logic for *slugifying* a given string has been enhanced to support more Unicode characters.
30 * If you face issues from Jekyll importing a config file bundled within a theme, you can now disable the import entirely
31 by setting `ignore_theme_config: true` in your site's configuration file.
32 * If you face issues from Jekyll's disk-caching feature, you can now disable the mechanism without opting to build in
33 `safe` mode, by either setting `disable_disk_cache: true` in your configuration file or by passing the CLI switch
34 `--disable-disk-cache` to `jekyll build` or `jekyll serve` commands.
35 * When you build a site with the `--profile` switch, Jekyll will now additionally output a small table showing the amount
36 of time taken during various stages of the *build process*.
37 * Jekyll's development server now supports certificates based on Elliptic-curve cryptography.
38
39 For the interest of plugin authors:
40 * Excerpts won't be generated for `Jekyll::Page` subclasses automatically unless such instances have an `excerpt` key in
41 their `data` hash.
42
43 For the interest of gem-based theme authors:
44 * From `v4.1.0` onwards, a newly generated theme workspace (via `jekyll new-theme ...`) will have the gemspec configured
45 to bundle a `_config.yml` at the root of the workspace. If you don't wish to include the configuration file in the
46 released gem, please remove `|_config\.yml` from the regular expression in the gemspec.
47
48 {% for filter in page.filters_linked_to %}
49 {% assign filter_slug = filter | slugify %}
50 [{{ filter_slug }}-filter]: {{ filter_slug | prepend: '/docs/liquid/filters/#' | relative_url }}
51 {% endfor %}
52
53 ### Have questions?
54
55 Please reach out on our [community forum](https://talk.jekyllrb.com)
56
57
58 ### Thank you!! :bow:
59
60 We are thankful to our community for all the contributions that helped shape this release.
61 Special thanks to the following 78 contributors (in alphabetical order) who made this release possible and took the time
62 to submit a pull request:
63
64 Aaron Adams, Aaron K Redshaw, Alexandre Zanni, Anindita Basu, Arthur Zey, Artyom Tokachev, Ashwin Maroli, Atlas Cove,
65 Ben Stolovitz, Billy Kong, Christian Oliff, codenitpicker, csquare, Damien St Pierre, Daniel Leidert, David Zhang,
66 ddocs, dgolant, dkalev, Dmitry Egorov, dotnetCarpenter, Edward Thomson, Eric Knibbe, Frank Taillandier, Gabriel Rubens,
67 Gareth Mcshane, Grzegorz Kaczorek, guanicoe, Harry Wood, HTeuMeuLeu, iBug, İsmail Arılık, Itay Shakury, Ivan Gromov,
68 Ivan Raszl, J·Y, James Buckley, Jason Taylor, JC, jeffreytse, Johan Wigert, jonas-krummenacher, Justin Jia,
69 Kayce Basques, Kieran Barker, Leo, Liam Bigelow, lizharris, Lizzy Kate, Luis Puente, Mark Bennett, Matt Penna,
70 Matt Rogers, matt swanson, Max Chadwick, michaelcurrin, Mike Kasberg, Mike Neumegen, Muhammed Salih, Nikhil Benesch,
71 Paramdeo Singh, Patrik Eriksson, Phil Nash, Philip Eriksson, R.P. Pedraza, Radoslav Karlík, Riccardo Porreca,
72 sharath Kumar, Simone Arpe, Takashi Udagawa, Tobias Klüpfel, Toby Glei, vhermecz, Viktor Szakats, Ward Sandler, wzy,
73 XhmikosR, Zlatan Vasović.
0 ---
1 title: 'Jekyll 4.1.1 Released'
2 date: 2020-06-24 16:45:35 +0530
3 author: ashmaroli
4 version: 4.1.1
5 category: release
6 ---
7
8 Jekyll 4.1.0 brought two notable changes: *Page-excerpts* and *Liquid Drop for Page objects*.
9 However these seemingly benign changes had unexpected adverse side-effects which did not figure in our tests.
10
11 The Core team decided that the best way forward is to revert introduction of the Liquid drop for Pages but push back
12 generating excerpts for pages behind a flag until `v5.0`.
13
14 Page-excerpts are henceforth an opt-in experimental feature which can be enabled by setting `page_excerpts: true` in
15 your configuration file. Due to its experimental nature, we have narrowed the scope for page-excerpts to limit their
16 negative effect on builds. Excerpts will not be generated for pages that *do not* output into an HTML file even if
17 `page_excerpts: true` has been set in the configuration file.
18
19 Another known issue with page-excerpts is that an infinite loop is created in certain use-cases such as any construct
20 that involves iterating through `site.pages` directly within a `Jekyll::Page` instance. A couple of examples would be
21 having a variant of either of the following code blocks inside a page source, say `index.markdown` or `about.markdown`:
22
23 {% raw %}
24
25 ```liquid
26 {% for entry in site.pages %}
27 {{ entry.name }}
28 {% endfor %}
29 ```
30
31 ```liquid
32 {{ site.pages | sort: 'title' }}
33 ```
34
35 {% endraw %}
36
37 Therefore, we advise caution when opting to use the page-excerpt feature.
0 ---
1 title: 'Jekyll 3.9.0 Released'
2 author: parkr
3 version: 3.9.0
4 categories: [release]
5 ---
6
7 Jekyll 3.9.0 allows use of kramdown v2, the latest series of kramdown.
8
9 If you choose to upgrade, please note that the GitHub-Flavored Markdown
10 parser and other features of kramdown v1 are now distributed via
11 separate gems. If you would like to continue using these features, you will
12 need to add the gems to your `Gemfile`. They are as follows:
13
14 - GFM parser – `kramdown-parser-gfm`
15 - coderay syntax highlighter – `kramdown-syntax-coderay`
16 - mathjaxnode math engine – `kramdown-math-mathjaxnode`
17 - sskatex math engine – `kramdown-math-sskatex`
18 - katex math engine – `kramdown-math-katex`
19 - ritex math engine – `kramdown-math-ritex`
20 - itex2mml math engine – `kramdown-math-itex2mml`
21
22 Jekyll will require the given gem when the configuration requires it, and
23 will show a helpful message when a dependency is missing.
24
25 You can check out the patches and see all the details in [the release notes](/docs/history/#v3-9-0)
26
27 Happy Jekylling!
+0
-28
docs/_posts/2020-08-06-jekyll-3-9-0-released.markdown less more
0 ---
1 title: 'Jekyll 3.9.0 Released'
2 author: parkr
3 version: 3.9.0
4 categories: [release]
5 ---
6
7 Jekyll 3.9.0 allows use of kramdown v2, the latest series of kramdown.
8
9 If you choose to upgrade, please note that the GitHub-Flavored Markdown
10 parser and other features of kramdown v1 are now distributed via
11 separate gems. If you would like to continue using these features, you will
12 need to add the gems to your `Gemfile`. They are as follows:
13
14 - GFM parser – `kramdown-parser-gfm`
15 - coderay syntax highlighter – `kramdown-syntax-coderay`
16 - mathjaxnode math engine – `kramdown-math-mathjaxnode`
17 - sskatex math engine – `kramdown-math-sskatex`
18 - katex math engine – `kramdown-math-katex`
19 - ritex math engine – `kramdown-math-ritex`
20 - itex2mml math engine – `kramdown-math-itex2mml`
21
22 Jekyll will require the given gem when the configuration requires it, and
23 will show a helpful message when a dependency is missing.
24
25 You can check out the patches and see all the details in [the release notes](/docs/history/#v3-9-0)
26
27 Happy Jekylling!
0 ---
1 title: "Jekyll 4.2.0 Released"
2 date: 2020-12-14 14:12:20 +0530
3 author: ashmaroli
4 version: 4.2.0
5 category: release
6 ---
7
8 Greetings Jekyllers! Jekyll v4.2.0 is out!
9
10 This release gives you a new hook named `:post_convert` that allows modifying rendered HTML contents before they are
11 placed into the designated layout(s).
12
13 Detecting files that get written into the same destination path has been a part of the diagnostics from `jekyll doctor`
14 for quite some time now. However, v4.2 has integrated that feature into the build process itself.
15
16 On the topic of log output, the `--verbose` output got a bit more verbose. Instead of just showing *documents* that are
17 being read, the output will now also show *pages* and *layouts* that are being read into the site.
18
19 Additionally, we have stopped overriding the `site.url` to `http://localhost:4000` in absolute URLs while developing
20 via `jekyll serve`.
21
22 As always, you can go through [the full list of changes](/docs/history/#v4-2-0) if you are interested in the various
23 memory-allocation optimizations made to Jekyll.
24
25 Special thanks to our community members who helped improving Jekyll codebase and documentation from v4.1.1:
26 Adam Alton, Alex Malaszkiewicz, Alexey Pelykh, Brittany Joiner, bytecode1024, Christopher Brown, Chuck Houpt,
27 Corey Megown, Dan Nemenyi, Enrico Tolotto, fauno, Felix Breidenstein, Francesco Bianco, Frank Taillandier,
28 Gabriel Staples, iBug, Jacobo Vidal, jaybe@jekyll, jesuslerma, jnozsc, joelkennedy, Joe Marshall, Liam Cooke,
29 Lou Rectoret, Malathi, m-naumann, Nicholas Paxford, Nikita Skalkin, Parker Moore, Pratyaksh Gautam, Rachel Cheyfitz,
30 SaintMalik, Seeker, Shannon Kularathna, Steven Xu, Takuya N, Thelonius Kort and Toby Glei.
0 ---
1 title: 'Jekyll 3.9.1 Released'
2 date: 2021-04-08 10:51:12 -0400
3 author: parkr
4 version: 3.9.1
5 categories: [release]
6 ---
7
8 This patch release of the 3.9 series is released to fix a bug where the
9 `include` tag does not allow valid filename characters. For example, this
10 would previously fail:
11
12 {% raw %}
13 ```text
14 {% include my-logo@2x.svg %}
15 ```
16 {% endraw %}
17
18 This release adds support for the following characters in filenames:
19
20 - `@`
21 - `-`
22 - `(` and `)`
23 - `+`
24 - `~`
25 - `#`
26
27 Happy Jekylling!
0 ---
1 title: 'Goodbye, Dear Frank.'
2 date: 2021-09-14 11:28:02 -0500
3 author: ashmaroli
4 categories: [team, community]
5 ---
6
7 Over the weekend, the Jekyll core team learned of the passing of one of our own: *Frank Taillandier*, popularly known
8 by his GitHub username @DirtyF.
9
10 Ruby not being his forte, he chose to avoid code-level changes and instead focus on what he did best &mdash; *engage with
11 the community*.
12
13 He helped resolve complaints reported on the GitHub issue tracker, ensured that Jekyll documentation remained simple for
14 novice users yet detailed enough for advanced users seeking additional information.
15
16 He also served as the administrator for Jekyll's public [discourse forum](https://talk.jekyllrb.com/) where he not only
17 addressed queries from users and provided tips to improve Jekyll workflow, he also shared feedback on Jekyll sites
18 created by the community, and used the forum as a platform to gather feedback on unreleased iterations of Jekyll and
19 in-house plugins.
20
21 Abreast with latest developments in the Web-verse, Frank was always quick to introduce technologies that vastly improved
22 maintenance in the Jekyll organization. He was instrumental in setting up deploy previews for patches to Jekyll's
23 documentation site and later wiring GitHub Actions to handle continuous integrations for Jekyll and in-house projects.
24
25 In spite of spiritually moving away from Jekyll during the later part of his career, choosing to concentrate efforts on
26 furthering JAMstack projects, he greatly remained active on Jekyll's development channel on Slack relaying key feedback
27 from the community or discuss concerns regarding the future of Jekyll at length.
28
29 Having untimely left Jekyll and our community with an unfillable void, he will be missed immensely. :broken_heart:
30
31 Rest in Peace, friend and colleague. :bouquet:
0 ---
1 title: "Jekyll 4.2.1 Released"
2 date: 2021-09-27 14:45:46 +0530
3 author: ashmaroli
4 version: 4.2.1
5 category: release
6 ---
7
8 Hello Jekyllers!
9
10 The Jekyll team is happy to announce the release of `v4.2.1` which fixes a couple of
11 regressions introduced in `v4.2.0` and another bug inherited from Jekyll 3.
12
13 In `v4.2.0`, we decided to stop overriding {% raw %}`{{ site.url }}`{% endraw %} with
14 the *localhost* address when running the command `jekyll serve` with the default
15 *development* mode. While the intent behind the change was to avoid forcing users to
16 generate a *production build* separately by invoking `jekyll build`, it however had an
17 unforeseen consequence &mdash; absolute URLs for assets now pointed to
18 resources that were at times not yet been deployed to the configured `site.url`. That
19 broke the users' local development workflow.
20
21 `v4.2.0` also added a series of optimizations surrounding the generation of Liquid
22 representation for a site's standalone pages and layouts. However, that prevented
23 {% raw %}`{{ page.content }}`{% endraw %} and other mutable attributes from reflecting
24 the latest state of the requested attribute, thereby breaking the render of all resources
25 that were dependent on such mutable attributes.
26
27 The last fix included in this release addresses the issue where incremental regeneration
28 ignored changes to documents in collections when the site is configured to use a custom
29 `collections_dir` for all collections.
30
31 Special thanks to @benik for helping us understand the regression caused by the decision
32 to stop overriding `site.url` and proposing to revert the change. Another special thanks
33 to @pdmosses for helping us discover the regression surrounding Liquid representation of
34 pages by providing with a test repository.
35
36 <div style="padding:8px 0 2px;text-align:center;background:rgba(240,0,0,0.1)">
37 :bouquet: <span style="margin:0 6px;font-size:0.75em;vertical-align:top">
38 Dedicated to our colleague Frank who passed away recently</span> :bouquet:
39 </div>
0 ---
1 title: "Jekyll 4.2.2 Released"
2 date: 2022-03-03 19:15:20 +0530
3 author: ashmaroli
4 version: 4.2.2
5 category: release
6 ---
7
8 Hello Jekyllers!
9
10 Jekyll 4.2.2 has been released. Unlike prior releases, this is a simple maintenance release and may be skipped.
11
12 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
13 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
14 line-endings instead of Unix-style LF line-endings.
15
16 For our end-users, this difference holds no significance. However, a third-party entity vendoring the release faced a roadblock. The executable
17 program `jekyll` apparently misplaced the executable bit because of the change in line-endings.
18
19 To that end, the Jekyll team decided to use the GitHub Actions service to build and publish releases. In-house plugins have already published
20 releases via this route serving as trials. Henceforth, and unless explicitly reported, all Jekyll releases will be built on GitHub Actions'
21 Ubuntu platform and published to Rubygems by @jekyllbot irrespective of the maintainer overseeing the release.
22
23 That is all for now.
24 Happy Jekyllin'!!
25
26 *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
27 modern improvements to Sass.*
28
29 [sass-220]: https://github.com/jekyll/jekyll-sass-converter/tree/v2.2.0#sass-embedded
0 ---
1 title: 'Jekyll 3.9.2 Released'
2 date: 2022-03-27 13:20:00 -0700
3 author: parkr
4 version: 3.9.2
5 categories: [release]
6 ---
7
8 Hey Jekyllers,
9
10 Quick bug-fix release for you all today:
11
12 1. Ruby 3.0 and 3.1 support :tada: (you will need to run `bundle add webrick` for `jekyll serve` to work)
13 2. `jekyll serve` will no longer inject a charset into the MIME type for
14 binary types
15 3. Incremental regeneration now handles includes in collection files
16 correctly
17
18 That's all, Happy Jekylling!
0 ---
1 title: 'Jekyll 4.3.0 Released'
2 date: 2022-10-20 10:20:22 -0500
3 author: ashmaroli
4 version: 4.3.0
5 category: release
6 ---
7
8 Hello Jekyllers!
9
10 The Jekyll team is happy to announce the release of `v4.3.0` shipping with some nice improvements and bug-fixes.
11
12 ## Improvements
13
14 ### Dependencies
15
16 - Gem `webrick` is now a listed dependency. You no longer have to add the gem to your Gemfile when using Jekyll with
17 Ruby 3.0 or newer.
18 - You may now use Rouge v4 or continue using Rouge v3.x by explicitly mentioning the version in your Gemfile.
19 - Support for gem `tzinfo` v2 and non-half-hour offsets have been added.
20 - You will be able to use v3 of `jekyll-sass-converter` when it ships.
21
22 ### Builds
23
24 - Added support for bundling and loading data files from within a theme-gem similar to existing theme-gem contents.
25 - Changes to data files at source will now be respected during incremental builds.
26 - `site.static_files` now include static files within a collection.
27 - You may now configure converters for CSV data.
28 - `.jekyll-cache` or its equivalent custom cache directory will be automatically ignored by Git.
29 - Vendor the current latest mime-types dataset for use with local development server.
30
31 {% raw %}
32 ### Liquid Templates
33
34 - `basename` attribute of documents are now exposed to Liquid as `name`, for example `{{ page.name }}`. Excerpts delegate
35 to associated document attribute.
36 - Top-level variable `{{ theme }}` introduced to expose gemspec details of theme-gem. (Valid only when using theme-gem)
37 {% endraw %}
38
39 ## Bug-fixes
40
41 Some noteworthy bug-fixes include:
42
43 - Respect `BUNDLE_GEMFILE` when loading Jekyll plugins via Bundler.
44 - Prevent loading versions older than kramdown-2.3.1 as a security measure.
45 - Trigger livereloading even if the site has *no pages*.
46 - Ensure the expected class of theme config is returned following a merger.
47 - Enable BOM encoding only if configured encoding is 'UTF-8'.
48 - Respect server protocol while injecting livereload script.
49 - The table output for `--profile` stops printing incorrect "TOTALS" row.
50
51 [The full list of changes](/docs/history/#v4-3-0) may be perused if interested.
52
53 As always, we are grateful to the many contributors that helped improve the project codebase and documentation:
54
55 <small>Ashwin Maroli, Frank Taillandier, Matt Rogers, Parker Moore, Kelvin M. Klann, Josh Soref, Youssef Boulkaid,
56 Emily Grace Seville, Robert Martin, jaybe@jekyll, Ben Keith, Jonathan Darrer, Kaben, Mike Kasberg, Moncef Belyamani,
57 Phil Ross, Sesh Sadasivam, Adam Bell, Alaz Tetik, Alex Malaszkiewicz, Alex Saveau, Andreas Deininger, Andrew Davis,
58 Andrew Gutekanst, Andrii Abramov, Aram Akhavan, Atlas Cove, Attaphong Rattanaveerachanon, Ben Whetton, Chris Keefe,
59 Clayton Smith, Craig H Maynard, Curious Cat, Daniel Haim, Daniel Kehoe, Daryl Hepting, David Bruant, David Zhang,
60 Edson Jiménez, Eric Cousineau, Gary, Giuseppe Bertone, Ikko Ashimine, JJ, JT, Jeff Wilcox, Jeffrey Veen,
61 Jesse van der Pluijm, John Losito, Kantanat-Stamp, Kirstin Heidler, Korbs, Laurence Andrews, Liam Bigelow, Maik Riechert,
62 Meet Gor, Meg Gutshall, Michael Gerzabek, MichaelCordingley, Miguel Brandão, Nahin Khan, Nemo, Nicholas Paxford,
63 Nick Coish, Otto Urpelainen, Parikshit87, Phil Kirlin, Qasim Qureshi, Ricardo N Feliciano, Rishi Raj Jain, SNVMK,
64 SaintMalik, Sampath Sukesh Ravolaparthi, Shannon Kularathna, Shyam Mohan K, Takuya N, Tejas Bubane, Toshimaru, Tyler887,
65 Vinhas Kevin, alena-ko, fauno, lm, lucafrance, nusu, shorty, なつき</small>
66
67 ---
68
69 ### Announcement
70
71 I would like to inform you that following this release, Jekyll will start developing towards a v5.0 milestone that will
72 **definitely contain breaking changes**. I have set up a [tentative roadmap at the GitHub repository][roadmap] to give everyone
73 a glimpse of the PROBABLE OUTCOME. Towards that end, we will no longer accept documentation fixes on `master`. The `4.3-stable`
74 branch will be used to build and deploy the site for https://jekyllrb.com.
75
76 Jekyll 3.x series is now under security-maintenance phase. Only security patches will be released when necessary.
77
78 Jekyll 4.x series will continue receiving bug-fixes and security-patches only. Depending on the state of progress towards v5.0,
79 there will be *at least* one minor version release serving as a transitionary version containing deprecations and bridge code
80 to ease the eventual upgrade to v5.0.
81
82 [roadmap]: {{ site.repository }}/issues/9156
83
84 ---
85
86 That is all for now.
87 Happy Jekyllin'!!
0 ---
1 title: 'Jekyll 4.3.1 Released'
2 date: 2022-10-26 19:09:42 +0530
3 author: ashmaroli
4 version: 4.3.1
5 category: release
6 ---
7
8 Hello Jekyllers!
9
10 We're shipping `v4.3.1` containing fixes for two issues with v4.3.0:
11 - Jekyll now respects user-defined `name` attribute for collection documents when accessed in Liquid templates.
12 - Revert the changes made to trigger incremental rebuilds when data files are changed.
13
14 Thanks to the users who took the time to report the issues to us.
15 Happy Jekyllin'
16
17 P.S. Development towards v5 has taken a back seat as of now. I plan on releasing a v4.4.0 instead.
18 That said, please feel free to comment on the [tentative roadmap to v5][roadmap].
19
20 [roadmap]: {{ site.repository }}/issues/9156
00 .searchbox {
1 display: inline-block;
2 position: relative;
3 width: 200px;
4 height: 32px !important;
15 padding-top: 1px;
6 white-space: nowrap;
7 box-sizing: border-box;
8 visibility: visible !important;
9 }
10
11 .searchbox .algolia-autocomplete {
12 display: block;
13 width: 100%;
14 height: 100%;
15 }
16
17 .searchbox__wrapper {
18 width: 100%;
19 height: 100%;
20 z-index: 999;
21 position: relative;
22 }
23
24 .searchbox__input {
25 display: inline-block;
26 box-sizing: border-box;
27 transition: box-shadow 0.4s ease, background 0.4s ease;
28 border: 0;
29 border-radius: 16px;
30 box-shadow: inset 0 0 0 1px #cccccc;
31 background: #ffffff !important;
32 padding: 0;
33 padding-right: 26px;
34 padding-left: 32px;
35 width: 100%;
36 height: 100%;
37 vertical-align: middle;
38 white-space: normal;
39 font-size: 12px;
40 appearance: none;
41 }
42
43 .searchbox__input::-webkit-search-decoration, .searchbox__input::-webkit-search-cancel-button, .searchbox__input::-webkit-search-results-button, .searchbox__input::-webkit-search-results-decoration {
44 display: none;
45 }
46
47 .searchbox__input:hover {
48 box-shadow: inset 0 0 0 1px #b3b3b3;
49 }
50
51 .searchbox__input:focus, .searchbox__input:active {
52 outline: 0;
53 box-shadow: inset 0 0 0 1px #aaaaaa;
54 background: #ffffff;
55 }
56
57 .searchbox__input::placeholder {
58 color: #aaaaaa;
59 }
60
61 .searchbox__submit {
62 position: absolute;
63 top: 0;
64 margin: 0;
65 border: 0;
66 border-radius: 16px 0 0 16px;
67 background-color: rgba(69, 142, 225, 0);
68 padding: 0;
69 width: 32px;
70 height: 100%;
71 vertical-align: middle;
72 text-align: center;
73 font-size: inherit;
74 user-select: none;
75 right: inherit;
76 left: 0;
77 }
78
79 .searchbox__submit::before {
80 display: inline-block;
81 margin-right: -4px;
82 height: 100%;
83 vertical-align: middle;
84 content: '';
85 }
86
87 .searchbox__submit:hover, .searchbox__submit:active {
88 cursor: pointer;
89 }
90
91 .searchbox__submit:focus {
92 outline: 0;
93 }
94
95 .searchbox__submit svg {
96 width: 14px;
97 height: 14px;
98 vertical-align: middle;
99 fill: #6d7e96;
100 }
101
102 .searchbox__reset {
103 display: block;
104 position: absolute;
105 top: 8px;
106 right: 8px;
107 margin: 0;
108 border: 0;
109 background: none;
110 cursor: pointer;
111 padding: 0;
112 font-size: inherit;
113 user-select: none;
114 fill: rgba(0, 0, 0, 0.5);
115 }
116
117 .searchbox__reset.hide {
118 display: none;
119 }
120
121 .searchbox__reset:focus {
122 outline: 0;
123 }
124
125 .searchbox__reset svg {
126 display: block;
127 margin: 4px;
128 width: 8px;
129 height: 8px;
130 }
131
132 .searchbox__input:valid ~ .searchbox__reset {
133 display: block;
134 animation-name: sbx-reset-in;
135 animation-duration: 0.15s;
136 }
137
138 @keyframes sbx-reset-in {
139 0% {
140 transform: translate3d(-20%, 0, 0);
141 opacity: 0;
142 }
143 100% {
144 transform: none;
145 opacity: 1;
146 }
147 }
148
149 .algolia-autocomplete.algolia-autocomplete-right .ds-dropdown-menu {
150 right: 0 !important;
151 left: inherit !important;
152 }
153
154 .algolia-autocomplete.algolia-autocomplete-right .ds-dropdown-menu:before {
155 right: 48px;
156 }
157
158 .algolia-autocomplete.algolia-autocomplete-left .ds-dropdown-menu {
159 left: 0 !important;
160 right: inherit !important;
161 }
162
163 .algolia-autocomplete.algolia-autocomplete-left .ds-dropdown-menu:before {
164 left: 48px;
165 }
166
167 .algolia-autocomplete .ds-dropdown-menu {
168 position: relative;
169 top: -6px;
170 border-radius: 4px;
171 margin: 6px 0 0;
172 padding: 0;
173 text-align: left;
174 height: auto;
175 position: relative;
176 background: transparent;
177 border: none;
178 z-index: 999;
179 max-width: 600px;
180 min-width: 500px;
181 box-shadow: 0 1px 0 0 rgba(0, 0, 0, 0.2), 0 2px 3px 0 rgba(0, 0, 0, 0.1);
182 }
183
184 .algolia-autocomplete .ds-dropdown-menu:before {
185 display: block;
186 position: absolute;
187 content: '';
188 width: 14px;
189 height: 14px;
190 background: #fff;
191 z-index: 1000;
192 top: -7px;
193 border-top: 1px solid #d9d9d9;
194 border-right: 1px solid #d9d9d9;
195 transform: rotate(-45deg);
196 border-radius: 2px;
197 }
198
199 .algolia-autocomplete .ds-dropdown-menu .ds-suggestions {
200 position: relative;
201 z-index: 1000;
202 margin-top: 8px;
203 }
204
205 .algolia-autocomplete .ds-dropdown-menu .ds-suggestions a:hover {
206 text-decoration: none;
207 }
208
209 .algolia-autocomplete .ds-dropdown-menu .ds-suggestion {
210 cursor: pointer;
211 }
212
213 .algolia-autocomplete .ds-dropdown-menu .ds-suggestion.ds-cursor .algolia-docsearch-suggestion.suggestion-layout-simple {
214 background-color: rgba(69, 142, 225, 0.05);
215 }
216
217 .algolia-autocomplete .ds-dropdown-menu .ds-suggestion.ds-cursor .algolia-docsearch-suggestion:not(.suggestion-layout-simple) .algolia-docsearch-suggestion--content {
218 background-color: rgba(69, 142, 225, 0.05);
219 }
220
221 .algolia-autocomplete .ds-dropdown-menu [class^='ds-dataset-'] {
222 position: relative;
223 border: solid 1px #d9d9d9;
224 background: #fff;
225 border-radius: 4px;
226 overflow: auto;
227 padding: 0 8px 8px;
228 }
229
230 .algolia-autocomplete .ds-dropdown-menu * {
231 box-sizing: border-box;
232 }
233
234 .algolia-autocomplete .algolia-docsearch-suggestion {
235 display: block;
236 position: relative;
237 padding: 0 8px;
238 background: #fff;
239 color: #02060c;
240 overflow: hidden;
241 }
242
243 .algolia-autocomplete .algolia-docsearch-suggestion--highlight {
244 color: #174d8c;
245 background: rgba(143, 187, 237, 0.1);
246 padding: 0.1em 0.05em;
247 }
248
249 .algolia-autocomplete .algolia-docsearch-suggestion--category-header .algolia-docsearch-suggestion--category-header-lvl0
250 .algolia-docsearch-suggestion--highlight,
251 .algolia-autocomplete .algolia-docsearch-suggestion--category-header .algolia-docsearch-suggestion--category-header-lvl1
252 .algolia-docsearch-suggestion--highlight {
253 padding: 0 0 1px;
254 background: inherit;
255 box-shadow: inset 0 -2px 0 0 rgba(69, 142, 225, 0.8);
256 color: inherit;
257 }
258
259 .algolia-autocomplete .algolia-docsearch-suggestion--text .algolia-docsearch-suggestion--highlight {
260 padding: 0 0 1px;
261 background: inherit;
262 box-shadow: inset 0 -2px 0 0 rgba(69, 142, 225, 0.8);
263 color: inherit;
264 }
265
266 .algolia-autocomplete .algolia-docsearch-suggestion--content {
267 display: block;
268 float: right;
269 width: 70%;
270 position: relative;
271 padding: 5.33333px 0 5.33333px 10.66667px;
272 cursor: pointer;
273 }
274
275 .algolia-autocomplete .algolia-docsearch-suggestion--content:before {
276 content: '';
277 position: absolute;
278 display: block;
279 top: 0;
280 height: 100%;
281 width: 1px;
282 background: #ddd;
283 left: -1px;
284 }
285
286 .algolia-autocomplete .algolia-docsearch-suggestion--category-header {
287 position: relative;
288 border-bottom: 1px solid #ddd;
289 display: none;
290 margin-top: 8px;
291 padding: 4px 0;
292 font-size: 1em;
293 color: #33363d;
294 }
295
296 .algolia-autocomplete .algolia-docsearch-suggestion--wrapper {
297 width: 100%;
298 float: left;
299 padding: 8px 0 0 0;
300 }
301
302 .algolia-autocomplete .algolia-docsearch-suggestion--subcategory-column {
303 float: left;
304 width: 30%;
305 padding-left: 0;
306 text-align: right;
307 position: relative;
308 padding: 5.33333px 10.66667px;
309 color: #a4a7ae;
310 font-size: 0.9em;
311 word-wrap: break-word;
312 }
313
314 .algolia-autocomplete .algolia-docsearch-suggestion--subcategory-column:before {
315 content: '';
316 position: absolute;
317 display: block;
318 top: 0;
319 height: 100%;
320 width: 1px;
321 background: #ddd;
322 right: 0;
323 }
324
325 .algolia-autocomplete .algolia-docsearch-suggestion--subcategory-inline {
326 display: none;
327 }
328
329 .algolia-autocomplete .algolia-docsearch-suggestion--title {
330 margin-bottom: 4px;
331 color: #02060c;
332 font-size: 0.9em;
333 font-weight: bold;
334 }
335
336 .algolia-autocomplete .algolia-docsearch-suggestion--text {
337 display: block;
338 line-height: 1.2em;
339 font-size: 0.85em;
340 color: #63676d;
341 }
342
343 .algolia-autocomplete .algolia-docsearch-suggestion--no-results {
344 width: 100%;
345 padding: 8px 0;
346 text-align: center;
347 font-size: 1.2em;
348 }
349
350 .algolia-autocomplete .algolia-docsearch-suggestion--no-results::before {
351 display: none;
352 }
353
354 .algolia-autocomplete .algolia-docsearch-suggestion code {
355 padding: 1px 5px;
356 font-size: 90%;
357 border: none;
358 color: #222222;
359 background-color: #ebebeb;
360 border-radius: 3px;
361 font-family: Menlo, Monaco, Consolas, 'Courier New', monospace;
362 }
363
364 .algolia-autocomplete .algolia-docsearch-suggestion code .algolia-docsearch-suggestion--highlight {
365 background: none;
366 }
367
368 .algolia-autocomplete .algolia-docsearch-suggestion.algolia-docsearch-suggestion__main .algolia-docsearch-suggestion--category-header {
369 display: block;
370 }
371
372 .algolia-autocomplete .algolia-docsearch-suggestion.algolia-docsearch-suggestion__secondary {
373 display: block;
374 }
375
376 @media all and (min-width: 768px) {
377 .algolia-autocomplete .algolia-docsearch-suggestion .algolia-docsearch-suggestion--subcategory-column {
378 display: block;
379 }
380 }
381
382 @media all and (max-width: 768px) {
383 .algolia-autocomplete .algolia-docsearch-suggestion .algolia-docsearch-suggestion--subcategory-column {
384 display: inline-block;
385 width: auto;
386 text-align: left;
387 float: left;
388 padding: 0;
389 color: #02060c;
390 font-size: 0.9em;
391 font-weight: bold;
392 text-align: left;
393 opacity: 0.5;
394 }
395 .algolia-autocomplete .algolia-docsearch-suggestion .algolia-docsearch-suggestion--subcategory-column:before {
396 display: none;
397 }
398 .algolia-autocomplete .algolia-docsearch-suggestion .algolia-docsearch-suggestion--subcategory-column:after {
399 content: '|';
400 }
401 .algolia-autocomplete .algolia-docsearch-suggestion .algolia-docsearch-suggestion--content {
402 display: inline-block;
403 width: auto;
404 text-align: left;
405 float: left;
406 padding: 0;
407 }
408 .algolia-autocomplete .algolia-docsearch-suggestion .algolia-docsearch-suggestion--content:before {
409 display: none;
410 }
411 }
412
413 .algolia-autocomplete .suggestion-layout-simple.algolia-docsearch-suggestion {
414 border-bottom: solid 1px #eee;
415 padding: 8px;
416 margin: 0;
417 }
418
419 .algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--content {
420 width: 100%;
421 padding: 0;
422 }
423
424 .algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--content::before {
425 display: none;
426 }
427
428 .algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--category-header {
429 margin: 0;
430 padding: 0;
431 display: block;
432 width: 100%;
433 border: none;
434 }
435
436 .algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--category-header-lvl0 {
437 opacity: 0.6;
438 font-size: 0.85em;
439 }
440
441 .algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--category-header-lvl1 {
442 opacity: 0.6;
443 font-size: 0.85em;
444 }
445
446 .algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--category-header-lvl1::before {
447 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>');
448 content: '';
449 width: 10px;
450 height: 10px;
451 display: inline-block;
452 }
453
454 .algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--wrapper {
455 width: 100%;
456 float: left;
457 margin: 0;
458 padding: 0;
459 }
460
461 .algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--duplicate-content, .algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--subcategory-inline {
462 display: none !important;
463 }
464
465 .algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--title {
466 margin: 0;
467 color: #458ee1;
468 font-size: 0.9em;
469 font-weight: normal;
470 }
471
472 .algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--title::before {
473 content: '#';
474 font-weight: bold;
475 color: #458ee1;
476 display: inline-block;
477 }
478
479 .algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--text {
480 margin: 4px 0 0;
481 display: block;
482 line-height: 1.4em;
483 padding: 5.33333px 8px;
484 background: #f8f8f8;
485 font-size: 0.85em;
486 opacity: 0.8;
487 }
488
489 .algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--text .algolia-docsearch-suggestion--highlight {
490 color: #3f4145;
491 font-weight: bold;
492 box-shadow: none;
493 }
494
495 .algolia-autocomplete .algolia-docsearch-footer {
496 width: 134px;
497 height: 20px;
498 z-index: 2000;
499 margin-top: 10.66667px;
500 float: right;
501 font-size: 0;
502 line-height: 0;
503 }
504
505 .algolia-autocomplete .algolia-docsearch-footer--logo {
506 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");
507 background-repeat: no-repeat;
508 background-position: center;
509 background-size: 100%;
510 overflow: hidden;
511 text-indent: -9000px;
512 padding: 0 !important;
513 width: 100%;
514 height: 100%;
515 display: block;
516 }
517
518 // Overrides
519
520 .searchbox {
2521 .searchbox__input {
3522 padding: 6px 5px 5px 29px;
4523 font-size: 0.75em;
00 @font-face {
11 font-family: 'FontAwesome';
2 font-weight: normal;
3 font-style: normal;
4 font-display: swap;
25 src: url('../fonts/FontAwesome.eot?9h6hxj');
36 src: url('../fonts/FontAwesome.eot?9h6hxj#iefix') format('embedded-opentype'),
47 url('../fonts/FontAwesome.woff?9h6hxj') format('woff'),
58 url('../fonts/FontAwesome.ttf?9h6hxj') format('truetype'),
69 url('../fonts/FontAwesome.svg?9h6hxj#FontAwesome') format('svg');
7 font-weight: normal;
8 font-style: normal;
910 }
11
1012 .fa {
1113 display: inline-block;
1214 font: normal normal normal 14px/1 FontAwesome;
1517 -webkit-font-smoothing: antialiased;
1618 -moz-osx-font-smoothing: grayscale;
1719 }
20
1821 .fa-link:before {
1922 content: "\f0c1";
2023 }
24
2125 .fa-pencil:before {
2226 content: "\f040";
2327 }
0 // *.woff2 support: Chrome 26+, Opera 23+, FireFox 3.6
1 // *.woff support: Chrome 6+, Firefox 3.6+, IE9+, Safari 5.1+
2
3 // Lato Light (300)
4 @font-face {
5 font-family: 'Lato';
6 font-style: normal;
7 font-weight: 300;
8 src: local('Lato Light'), local('Lato-Light'),
9 url('../fonts/lato-v14-latin-300.woff2') format('woff2'),
10 url('../fonts/lato-v14-latin-300.woff') format('woff');
11 font-display: swap;
12 }
13
14 // Lato Light, Italic (300)
15 @font-face {
16 font-family: 'Lato';
17 font-style: italic;
18 font-weight: 300;
19 src: local('Lato Light Italic'), local('Lato-LightItalic'),
20 url('../fonts/lato-v14-latin-300italic.woff2') format('woff2'),
21 url('../fonts/lato-v14-latin-300italic.woff') format('woff');
22 font-display: swap;
23 }
24
25 // Lato Regular (400)
26 @font-face {
27 font-family: 'Lato';
28 font-style: normal;
29 font-weight: 400;
30 src: local('Lato Regular'), local('Lato-Regular'),
31 url('../fonts/lato-v14-latin-regular.woff2') format('woff2'),
32 url('../fonts/lato-v14-latin-regular.woff') format('woff');
33 font-display: swap;
34 }
35
36 // Lato Regular, Italic (400)
37 @font-face {
38 font-family: 'Lato';
39 font-style: italic;
40 font-weight: 400;
41 src: local('Lato Italic'), local('Lato-Italic'),
42 url('../fonts/lato-v14-latin-italic.woff2') format('woff2'),
43 url('../fonts/lato-v14-latin-italic.woff') format('woff');
44 font-display: swap;
45 }
46
47 // Lato Bold (700)
48 @font-face {
49 font-family: 'Lato';
50 font-style: normal;
51 font-weight: 700;
52 src: local('Lato Bold'), local('Lato-Bold'),
53 url('../fonts/lato-v14-latin-700.woff2') format('woff2'),
54 url('../fonts/lato-v14-latin-700.woff') format('woff');
55 font-display: swap;
56 }
57
58 // Lato Bold, Italic (700)
59 @font-face {
60 font-family: 'Lato';
61 font-style: italic;
62 font-weight: 700;
63 src: local('Lato Bold Italic'), local('Lato-BoldItalic'),
64 url('../fonts/lato-v14-latin-700italic.woff2') format('woff2'),
65 url('../fonts/lato-v14-latin-700italic.woff') format('woff');
66 font-display: swap;
67 }
68
69 // Lato Black (900)
70 @font-face {
71 font-family: 'Lato';
72 font-style: normal;
73 font-weight: 900;
74 src: local('Lato Black'), local('Lato-Black'),
75 url('../fonts/lato-v14-latin-900.woff2') format('woff2'),
76 url('../fonts/lato-v14-latin-900.woff') format('woff');
77 font-display: swap;
78 }
79
80 // Lato Black, Italic (900)
81 @font-face {
82 font-family: 'Lato';
83 font-style: italic;
84 font-weight: 900;
85 src: local('Lato Black Italic'), local('Lato-BlackItalic'),
86 url('../fonts/lato-v14-latin-900italic.woff2') format('woff2'),
87 url('../fonts/lato-v14-latin-900italic.woff') format('woff');
88 font-display: swap;
89 }
00 .highlight {
11 .hll { background-color: #ffffcc }
2 .c { color: #999; font-style: italic } /* Comment */
3 .err { color: #ffffff} /* Error */
4 .g { color: #ffffff} /* Generic */
5 .k { color: #f0e68c} /* Keyword */
6 .l { color: #ffffff} /* Literal */
7 .n { color: #ffffff} /* Name */
8 .o { color: #ffffff} /* Operator */
9 .x { color: #ffffff} /* Other */
10 .p { color: #98b9ef} /* Punctuation */
11 .cm { color: #87ceeb} /* Comment.Multiline */
12 .cp { color: #cd5c5c} /* Comment.Preproc */
13 .c1 { color: #87ceeb} /* Comment.Single */
14 .cs { color: #87ceeb} /* Comment.Special */
15 .gd { color: #ce342c} /* Generic.Deleted */
16 .ge { color: #c000c0; text-decoration: underline} /* Generic.Emph */
2 .err { color: #ce342c } /* Error */
3 .c { color: #818181 } /* Comment */
4 .g { color: #ffffff } /* Generic */
5 .k { color: #ff4287 } /* Keyword */
6 .l { color: #ffffff } /* Literal */
7 .n { color: #ffffff } /* Name */
8 .o { color: #ffffff } /* Operator */
9 .x { color: #ffffff } /* Other */
10 .p { color: #ffffff } /* Punctuation */
11 .cm { color: #818181 } /* Comment.Multiline */
12 .cp { color: #d1c2f4 } /* Comment.Preproc */
13 .c1 { color: #818181 } /* Comment.Single */
14 .cs { color: #818181 } /* Comment.Special */
15 .gd { color: #ce342c } /* Generic.Deleted */
16 .ge { color: #c000c0; text-decoration: underline } /* Generic.Emph */
1717 .gr { color: #c0c0c0; font-weight: bold; background-color: #c00000 } /* Generic.Error */
18 .gh { color: #cd5c5c} /* Generic.Heading */
19 .gi { color: #27b42c} /* Generic.Inserted */
18 .gh { color: #ffffff } /* Generic.Heading */
19 .gi { color: #27b42c } /* Generic.Inserted */
2020 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. */
21 .gp { color: #ffffff} /* Generic.Prompt */
22 .gs { color: #ffffff} /* Generic.Strong */
23 .gu { color: #cd5c5c} /* Generic.Subheading */
21 .gp { color: #ffffff } /* Generic.Prompt */
22 .gs { color: #ffffff } /* Generic.Strong */
23 .gu { color: #ffffff } /* Generic.Subheading */
2424 .gt { color: #c0c0c0; font-weight: bold; background-color: #c00000 } /* Generic.Traceback */
25 .kc { color: #f0e68c} /* Keyword.Constant */
26 .kd { color: #f0e68c} /* Keyword.Declaration */
27 .kn { color: #f0e68c} /* Keyword.Namespace */
28 .kp { color: #f0e68c} /* Keyword.Pseudo */
29 .kr { color: #f0e68c} /* Keyword.Reserved */
30 .kt { color: #bdb76b} /* Keyword.Type */
31 .ld { color: #ffffff} /* Literal.Date */
32 .m { color: #ffffff} /* Literal.Number */
33 .s { color: #ffffff} /* Literal.String */
34 .na { color: #ffffff} /* Name.Attribute */
35 .nb { color: #ffffff} /* Name.Builtin */
36 .nc { color: #ffffff} /* Name.Class */
37 .no { color: #ffa0a0} /* Name.Constant */
38 .nd { color: #ffffff} /* Name.Decorator */
39 .ni { color: #ffdead} /* Name.Entity */
40 .ne { color: #ffffff} /* Name.Exception */
41 .nf { color: #ffffff} /* Name.Function */
42 .nl { color: #ffffff} /* Name.Label */
43 .nn { color: #ffffff} /* Name.Namespace */
44 .nx { color: #ffffff} /* Name.Other */
45 .py { color: #ffffff} /* Name.Property */
46 .nt { color: #f0e68c} /* Name.Tag */
47 .nv { color: #88d472} /* Name.Variable */
48 .ow { color: #ffffff} /* Operator.Word */
49 .w { color: #ffffff} /* Text.Whitespace */
50 .mf { color: #ffffff} /* Literal.Number.Float */
51 .mh { color: #ffffff} /* Literal.Number.Hex */
52 .mi { color: #ffffff} /* Literal.Number.Integer */
53 .mo { color: #ffffff} /* Literal.Number.Oct */
54 .sb { color: #ffffff} /* Literal.String.Backtick */
55 .sc { color: #ffffff} /* Literal.String.Char */
56 .sd { color: #ffffff} /* Literal.String.Doc */
57 .s2 { color: #ffffff} /* Literal.String.Double */
58 .se { color: #ffffff} /* Literal.String.Escape */
59 .sh { color: #ffffff} /* Literal.String.Heredoc */
60 .si { color: #ffffff} /* Literal.String.Interpol */
61 .sx { color: #ffffff} /* Literal.String.Other */
62 .sr { color: #ffffff} /* Literal.String.Regex */
63 .s1 { color: #ffffff} /* Literal.String.Single */
64 .ss { color: #ffffff} /* Literal.String.Symbol */
65 .bp { color: #ffffff} /* Name.Builtin.Pseudo */
66 .vc { color: #98fb98} /* Name.Variable.Class */
67 .vg { color: #98fb98} /* Name.Variable.Global */
68 .vi { color: #98fb98} /* Name.Variable.Instance */
69 .il { color: #ffffff} /* Literal.Number.Integer.Long */
25 .kc { color: #ff4287 } /* Keyword.Constant */
26 .kd { color: #ff4287 } /* Keyword.Declaration */
27 .kn { color: #ff4287 } /* Keyword.Namespace */
28 .kp { color: #ff4287 } /* Keyword.Pseudo */
29 .kr { color: #ff4287 } /* Keyword.Reserved */
30 .kt { color: #bdb76b } /* Keyword.Type */
31 .ld { color: #ffffff } /* Literal.Date */
32 .m { color: #ffffff } /* Literal.Number */
33 .s { color: #ffe580 } /* Literal.String */
34 .na { color: #b6e382 } /* Name.Attribute */
35 .nb { color: #ffffff } /* Name.Builtin */
36 .nc { color: #b6e382 } /* Name.Class */
37 .no { color: #87ceeb } /* Name.Constant */
38 .nd { color: #ffffff } /* Name.Decorator */
39 .ni { color: #ffdead } /* Name.Entity */
40 .ne { color: #ffffff } /* Name.Exception */
41 .nf { color: #ffffff } /* Name.Function */
42 .nl { color: #ffffff } /* Name.Label */
43 .nn { color: #ffffff } /* Name.Namespace */
44 .nx { color: #ffffff } /* Name.Other */
45 .py { color: #ffffff } /* Name.Property */
46 .nt { color: #ff4287 } /* Name.Tag */
47 .nv { color: #ffffff } /* Name.Variable */
48 .ow { color: #ffffff } /* Operator.Word */
49 .w { color: #ffffff } /* Text.Whitespace */
50 .mf { color: #ffffff } /* Literal.Number.Float */
51 .mh { color: #ffffff } /* Literal.Number.Hex */
52 .mi { color: #ffffff } /* Literal.Number.Integer */
53 .mo { color: #ffffff } /* Literal.Number.Oct */
54 .sb { color: #ffffff } /* Literal.String.Backtick */
55 .sc { color: #ffffff } /* Literal.String.Char */
56 .sd { color: #ffffff } /* Literal.String.Doc */
57 .s2 { color: #ffe580 } /* Literal.String.Double */
58 .se { color: #ffffff } /* Literal.String.Escape */
59 .sh { color: #ffffff } /* Literal.String.Heredoc */
60 .si { color: #ffffff } /* Literal.String.Interpol */
61 .sx { color: #ffffff } /* Literal.String.Other */
62 .sr { color: #ffffff } /* Literal.String.Regex */
63 .s1 { color: #ffe580 } /* Literal.String.Single */
64 .ss { color: #a47bea } /* Literal.String.Symbol */
65 .bp { color: #ffffff } /* Name.Builtin.Pseudo */
66 .vc { color: #98fb98 } /* Name.Variable.Class */
67 .vg { color: #98fb98 } /* Name.Variable.Global */
68 .vi { color: #ffffff } /* Name.Variable.Instance */
69 .il { color: #ffffff } /* Literal.Number.Integer.Long */
7070 .bash .nv {
7171 user-select: none;
7272 }
7373 }
74
75 .language-liquid {
76 .highlight {
77 .p { color: #87ceeb }
78 .kr { color: #87ceeb }
79 .nf { color: #b899ff }
80 .nt { color: #87ceeb }
81 .nv { color: #b6e382 }
82 }
83 }
84
85 .language-sh .highlight * { color: #eaeaea }
00 /* Base */
11
2 html {
3 box-sizing: border-box;
4 }
5
6 *,
7 *:before,
8 *:after {
9 box-sizing: inherit;
10 }
2 @charset "utf-8";
3
4 html {
5 box-sizing: border-box;
6 }
7
8 *,
9 *:before,
10 *:after {
11 box-sizing: inherit;
12 }
1113
1214 body {
13 font: 300 21px Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif;
15 font-family: 'Lato', 'Helvetica Neue', Helvetica, Arial, sans-serif;
16 font-size: 21px;
17 font-weight: 300;
1418 color: #ddd;
1519 background-color: #333;
1620 @include box-shadow(inset 0 3px 30px rgba(0,0,0,.3));
1721 text-shadow: 0 1px 3px rgba(0,0,0,.5);
18 -webkit-font-feature-settings: "kern" 1;
19 -moz-font-feature-settings: "kern" 1;
20 -o-font-feature-settings: "kern" 1;
21 font-feature-settings: "kern" 1;
22 font-kerning: normal;
22
23 // Not legible with 300 weight
24 // -moz-osx-font-smoothing: grayscale;
25 // -webkit-font-smoothing: antialiased;
26 text-rendering: optimizeLegibility;
27
28 -webkit-font-feature-settings: 'kern' on, 'liga' on, 'calt' on, 'onum', 'pnum';
29 -moz-font-feature-settings: 'kern' on, 'liga' on, 'calt' on, 'onum', 'pnum';
30 -ms-font-feature-settings: 'kern' on, 'liga' on, 'calt' on, 'onum', 'pnum';
31 -o-font-feature-settings: 'kern' on, 'liga' on, 'calt' on, 'onum', 'pnum';
32 font-feature-settings: 'kern' on, 'liga' on, 'calt' on, 'onum', 'pnum';
2333 margin: 0;
2434 }
2535
223233
224234 img {
225235 display: inline-block;
226 position: relative;
227 top: 8px;
236 vertical-align: middle;
228237 margin-left: 5px;
229238 opacity: .8;
230239 padding: 1px;
664673 }
665674
666675 .post-date,
667 .post-author { margin-left: 10px; }
676 .post-author { margin-left: 15px; }
677 .post-author .author-name { margin-left: 8px }
668678
669679 .news article + article {
670 margin-top: -10px;
671 @include border-radius(0 0 10px 10px);
680 margin-top: -6px;
681 padding-top: 15px;
682 padding-bottom: 15px;
672683 border-top: 1px solid #555;
684 border-radius: 0;
673685 @include box-shadow(0 -1px 0 #2f2f2f);
674 }
686
687 h2.post-title {
688 margin-bottom: 0.45em;
689 }
690 .post-date { margin-left: 0 }
691 }
692
693 /* Tutorials */
694
695 .tutorial-header {
696 float: none;
697 margin-bottom: 30px;
698 padding: 0 0 15px;
699 background: transparent;
700 border-bottom: 1px solid #545454;
701 .improve { padding-top: 14px }
702 .tutorial-title {
703 display: block;
704 width: calc(100% - 160px);
705 font-size: 1.75em
706 }
707 }
708
709 .tutorial-meta {
710 @extend .post-meta;
711 font-weight: 400;
712 text-shadow: none;
713 .tutorial-date,
714 .tutorial-author {
715 display: inline-block;
716 height: 24px
717 }
718 }
719
720 .tutorial-date {
721 vertical-align: sub
722 }
723 .tutorial-author {
724 margin-left: 15px;
725 .author-name { vertical-align: middle }
726 }
727
675728
676729 /* Code Highlighting */
677
678730
679731 pre,
680732 code {
681733 white-space: pre;
682734 display: inline-block;
683735 margin: 0;
684 font: 14px/1.8em Menlo, Consolas, "Courier New", Courier, "Liberation Mono", monospace;
736 font: 14px/1.625em Menlo, Consolas, "Courier New", Courier, "Liberation Mono", monospace;
685737 padding: 0 0.5em;
686738 }
687739
690742 }
691743
692744 .highlight,
745 a > code,
693746 p > pre,
694747 p > code,
695748 p > nobr > code,
697750 li> pre,
698751 h5 > code,
699752 .note > code {
700 background-color: #2b2b2b;
753 background-color: #272727;
701754 color: #fff;
702755 max-width: 100%;
703756 overflow-x: auto;
708761 0 -1px 0 rgba(0,0,0,.5));
709762 }
710763
764 a > code { color: inherit }
765
766 .note .highlight {
767 width: 94%;
768 pre code {
769 font-size: 0.9em;
770 background-color: transparent;
771 box-shadow: none;
772 }
773 }
774
711775 .note code {
712776 background-color: #333;
713777 background-color: rgba(0,0,0,0.2);
727791 overflow: auto;
728792 }
729793
730 pre.highlight { padding: 10px 0.5em; }
794 pre.highlight,
795 .highlight > pre {
796 padding: 10px 0.5em;
797 }
731798
732799 .highlighter-rouge .highlight {
733800 @extend .highlight;
734801 margin: 0;
802 }
803
804 div.highlighter-rouge + div.highlighter-rouge {
805 margin: 30px 0 0;
735806 }
736807
737808 /* HTML Elements */
885956 code.filter,
886957 code.output {
887958 margin-bottom: 2px;
959 }
960
961 table#builtin-hooks {
962 td {
963 text-align: center;
964 &:first-of-type {
965 max-width: 330px
966 }
967 }
888968 }
889969
890970 /* Note types */
9571037 }
9581038 }
9591039
1040 p.note {
1041 color: #fff;
1042 font-weight: 400;
1043 font-size: .75em;
1044
1045 &:after {
1046 line-height: 1.21;
1047 }
1048 }
1049
9601050 .info {
9611051 background-color: #0389aa;
9621052 background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/Pgo8c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgdmlld0JveD0iMCAwIDEgMSIgcHJlc2VydmVBc3BlY3RSYXRpbz0ibm9uZSI+CiAgPGxpbmVhckdyYWRpZW50IGlkPSJncmFkLXVjZ2ctZ2VuZXJhdGVkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjAlIiB5MT0iMCUiIHgyPSIwJSIgeTI9IjEwMCUiPgogICAgPHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzAzODlhYSIgc3RvcC1vcGFjaXR5PSIxIi8+CiAgICA8c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiMwMDYxN2YiIHN0b3Atb3BhY2l0eT0iMSIvPgogIDwvbGluZWFyR3JhZGllbnQ+CiAgPHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9IjEiIGhlaWdodD0iMSIgZmlsbD0idXJsKCNncmFkLXVjZ2ctZ2VuZXJhdGVkKSIgLz4KPC9zdmc+);
10901180 left: 0;
10911181 width: 100%;
10921182 height: 100%;
1183 }
1184
1185 .imageWrapper {
1186 width: 100%;
1187 height: 0;
1188 padding-bottom: 62.623762376237624%; /* You define this doing height / width * 100% */
1189 position: relative;
1190 background: #717171;
1191 display: block;
1192
1193 img {
1194 width: 100%;
1195 position: absolute;
1196 opacity: 0;
1197 }
1198 img.b-loaded {
1199 opacity: 1;
1200 transition: opacity .5s;
1201 }
10931202 }
10941203
10951204
11251234 }
11261235
11271236 .language-sh {
1237 margin-top: 5px;
1238 margin-bottom: 5px;
11281239 position: relative;
11291240 &:before {
1130 display: table;
1241 display: inline-table;
11311242 padding: 8px;
11321243 width: 100%;
11331244 padding: 5px 0;
11401251 background-image: -webkit-linear-gradient(top, #f7f7f7 0%, #cfcfcf 7%, #aaaaaa 100%);
11411252 background-image: -moz-linear-gradient(top, #f7f7f7 0%, #cfcfcf 7%, #aaaaaa 100%);
11421253 background-image: -o-linear-gradient(top, #f7f7f7 0%, #cfcfcf 7%, #aaaaaa 100%);
1143 background-image: linear-gradient(top, #f7f7f7 0%,#cfcfcf 7%,#aaaaaa 100%);
1254 background-image: linear-gradient(to bottom, #ddd 0%,#999 84%,#bbb 100%);
11441255 filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#f7f7f7', endColorstr='#aaaaaa',GradientType=0 );
1145 border-bottom: 1px solid #111;
11461256 text-align: center;
11471257 content: "terminal";
1148 @include border-radius(5px 5px 0 0);
1258 @include border-radius(3px 3px 0 0);
11491259 @include box-shadow(0 3px 10px rgba(0,0,0,.5));
11501260 }
11511261 .highlight {
1152 @include border-radius(0 0 5px 5px);
1262 @include border-radius(0 0 3px 3px);
1263 }
1264 div.highlight {
1265 border: 1px solid;
1266 border-color: transparent #bbb #bbb #bbb;
1267 @include box-shadow(0 3px 10px rgba(0,0,0,.5));
11531268 }
11541269 pre.highlight {
1270 min-height: 48px;
11551271 background: #1c1c1c;
11561272 }
1157 }
1273 code {
1274 font-size: 15px
1275 }
1276 }
1277
1278 .showcase {
1279 display: flex;
1280 list-style: none;
1281 padding: 0;
1282 margin: 0;
1283 flex-wrap: wrap;
1284 justify-content: space-between;
1285
1286 li {
1287 flex: 1 1 300px;
1288 box-sizing: border-box;
1289 margin: 10px;
1290 figure {
1291 margin: 0;
1292 }
1293 figcaption {
1294 text-align: center;
1295 }
1296 &.spacer {
1297 height: 0;
1298 margin: 0;
1299 }
1300 }
1301 }
1302
1303 .step-nav {
1304 background: #2b2b2b;
1305 border-radius: 5px;
1306 color: #fc0;
1307 padding: 8px 45px;
1308
1309 li {
1310 margin: 0;
1311 padding: 0;
1312
1313 &.current, &.current a {
1314 color: #f90;
1315 font-weight: bold;
1316 }
1317 }
1318 }
1319
1320 ol div.highlighter-rouge {
1321 margin: 8px 0 10px 0;
1322 }
1323
1324 .disclaimer-ribbon {
1325 position: relative;
1326 width: calc(100% + 100px);
1327 margin: 45px -50px 0;
1328 padding: 5px 20px;
1329 font-size: 0.725em;
1330 font-weight: 600;
1331 color: #efc98f;
1332 background: #695949;
1333 &:before, &:after {
1334 position: absolute;
1335 top: -9px;
1336 content: "";
1337 display: table;
1338 border: 5px solid;
1339 z-index: -1;
1340 }
1341 &:before {
1342 left: 0;
1343 border-color: transparent #b28b70 #b28b70 transparent;
1344 }
1345 &:after {
1346 right: 0;
1347 border-color: transparent transparent #b28b70 #b28b70;
1348 }
1349 }
0 ---
1 title: Cache API
2 author: pathawks
3 date: 2018-08-17 12:56:24 -0400
4 ---
5
6 Jekyll includes a caching API, which is used both internally as well as exposed
7 for plugins, which can be used to cache the output of deterministic functions to
8 speed up site generation. This cache will be persistent across builds, but
9 cleared when Jekyll detects any changes to `_config.yml`.
10
11 ## Jekyll::Cache.new(name) → new_cache
12
13 If there has already been a cache created with `name`, this will return a
14 reference to that existing Cache. Otherwise, create a new Cache called `name`.
15
16 If this Cache will be used by a Gem-packaged plugin, `name` should either be the
17 name of the Gem, or prefixed with the name of the Gem followed by `::` (if a
18 plugin expects to use multiple Caches). If this Cache will be used internally by
19 Jekyll, `name` should be the name of the class that is using the Cache (ie:
20 `"Jekyll::Converters::Markdown"`).
21
22 Cached objects are shared between all Caches created with the same `name`, but
23 are _not_ shared between Caches with different names. There can be an object
24 stored with key `1` in `Jekyll::Cache.new("a")` and an object stored with key
25 `1` in `Jekyll::Cache.new("b")` and these will not point to the same cached
26 object. This way, you do not need to ensure that keys are globally unique.
27
28 ## getset(key) {block}
29
30 This is the most common way to utilize the Cache.
31
32 `block` is a bit of code that takes a lot of time to compute, but always
33 generates the same output given a particular input (like converting Markdown to
34 HTML). `key` is a `String` (or an object with `to_s`) that uniquely identifies
35 the input to the function.
36
37 If `key` already exists in the Cache, it will be returned and `block` will never
38 be executed. If `key` does not exist in the Cache, `block` will be executed and
39 the result will be added to the Cache and returned.
40
41 ```ruby
42 def cache
43 @@cache ||= Jekyll::Cache.new("ConvertMarkdown")
44 end
45
46 def convert_markdown_to_html(markdown)
47 cache.getset(markdown) do
48 expensive_conversion_method(markdown)
49 end
50 end
51 ```
52
53 In the above example, `expensive_conversion_method` will only be called once for
54 any given `markdown` input. If `convert_markdown_to_html` is called a second
55 time with the same input, the cached output will be returned.
56
57 Because posts will frequently remain unchanged from one build to the next, this
58 is an effective way to avoid performing the same computations each time the site
59 is built.
60
61 ## clear
62
63 This will clear all cached objects from a particular Cache. The Cache will be
64 empty, both in memory and on disk.
65
66 ### The following methods will probably only be used in special circumstances
67
68 ## cache[key] → value
69
70 Fetches `key` from Cache and returns its `value`. Raises if `key` does not exist
71 in Cache.
72
73 ## cache[key] = value
74
75 Adds `value` to Cache under `key`.
76 Returns nothing.
77
78 ## key?(key) → true or false
79
80 Returns `true` if `key` already exists in Cache. False otherwise.
81
82 ## delete(key)
83
84 Removes `key` from Cache.
85 Returns nothing.
+0
-521
docs/_tutorials/convert-existing-site-to-jekyll.md less more
0 ---
1 layout: tutorials
2 permalink: /tutorials/convert-site-to-jekyll/
3 title: Convert an HTML site to Jekyll
4 ---
5
6 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.
7
8 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.
9
10 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.
11
12 Although websites can have sophisticated features and controls, we'll keep things simple in this tutorial.
13
14 ## What is a Jekyll Website?
15
16 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 %}).
17
18 We'll start with a *basic Jekyll site* consisting of three files:
19
20 ```
21 ├── _config.yml
22 ├── _layouts
23 │   └── default.html
24 └── index.md
25 ```
26
27 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`.
28
29 ```sh
30 $ touch _config.yml index.md default.html
31 $ mkdir _layouts && mv default.html _layouts
32 ```
33
34 Fire up your favorite editor, and populate the contents of the `default.html` and `index.md` files as follows:
35
36 **_config.yml**
37
38 ```yaml
39 name: My Jekyll Website
40 ```
41
42 **_layouts/default.html**
43
44 ```html
45 <!DOCTYPE html>
46 <html>
47 <body>
48 {% raw %}{{ content }}{% endraw %}
49 </body>
50 </html>
51 ```
52
53 **index.md**
54
55 ```yaml
56 ---
57 title: My page
58 layout: default
59 ---
60
61 # {% raw %}{{ page.title }}{% endraw %}
62
63 Content is written in [Markdown](https://learnxinyminutes.com/docs/markdown/). Plain text format allows you to focus on your **content**.
64
65 <!--
66 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.
67 -->
68 ```
69
70 Now `cd` to `my_jekyll_site` and serve the site with the built-in server:
71
72 ```
73 cd my_jekyll_site
74 jekyll serve
75 ```
76
77 If you have a Gemfile, [use Bundler]({% link _docs/quickstart.md %}#about-bundler) by typing `bundle exec jekyll serve` instead.
78 {: .note .info}
79
80 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.
81
82 This is a Jekyll site at the most basic functional level. Here's what is happening:
83
84 * 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.
85 * Jekyll looks for files with [front matter tags]({% link _docs/frontmatter.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).
86 * 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.
87 * The processed files get written as `.html` files in the `_site` directory.
88
89 You can read more about how Jekyll processes the files in [order of Interpretation]({% link _tutorials/orderofinterpretation.md %}).
90
91 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.
92
93 ## 1. Create a template for your default layout
94
95 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.
96
97 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.
98
99 {: .note .info}
100 Regardless of the site, do check the license and make sure you have permission to copy and use the code.
101
102 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 &mdash; that is, each page or post will use this layout when Jekyll builds the site.
103
104 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]({% link _docs/templates.md %}) to retrieve dynamic content.
105
106 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.
107
108 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.)
109
110 Jekyll provides some [filters]({% link _docs/templates.md %}#filters) to prepend a site URL before path. For example, you could preface your stylesheet like this:
111
112 ```liquid
113 {% raw %}{{ "/assets/style.css" | relative_url }}{% endraw %}
114 ```
115
116 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/`).
117
118 You can also use an `absolute_url` filter. This filter will prepend the `url` *and* `baseurl` value to the input:
119
120 ```liquid
121 {% raw %}{{ "/assets/style.css" | absolute_url }}{% endraw %}
122 ```
123
124 Again, both `url` and `baseurl` can be defined in your site's config file, like this:
125
126 ```
127 url: http://mysite.com
128 baseurl: /blog
129 ```
130
131 The result in the output will be `http://mysite.com/blog/assets/style.css`.
132
133 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.
134
135 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.
136
137 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.
138
139 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.
140
141 ## 2. Identify the content part of the layout
142
143 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 %}`.
144
145 Remove the content part (keep everything else: navigation menu, sidebar, footer, etc.) and replace it with `{% raw %}{{ content }}{% endraw %}`.
146
147 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.
148
149 ## 3. Create a couple of files with front matter tags
150
151 Create a couple of files (`index.md` and `about.md`) in your root directory.
152
153 In your `index.md` file, add some front matter tags containing a `title` and `layout` property, like this:
154
155 ```yaml
156 ---
157 title: Home
158 layout: default
159 ---
160
161 Some page content here...
162 ```
163
164 Create another page for testing called `about.md` with similar front matter tags.
165
166 {: .note .info}
167 If you don't specify a layout in your pages, Jekyll will simply render that page as an unstyled basic HTML page.
168
169 ## 4. Add a configuration file
170
171 Add a `_config.yml` file in your root directory. In `_config.yml`, you can optionally specify the markdown filter you want. By default, [kramdown](https://kramdown.gettalong.org/) is used (without the need to specify it). If no other filter is specified, your config file will automatically apply the following as a default setting:
172
173 ```
174 markdown: kramdown
175 ```
176
177 You can also specify [some options](https://kramdown.gettalong.org/converter/html.html) for kramdown to make it behave more like [GitHub Flavored Markdown (GFM)](https://github.github.com/gfm/):
178
179 ```
180 kramdown:
181 input: GFM
182 auto_ids: true
183 hard_wrap: false
184 syntax_highlighter: rouge
185 ```
186
187 ## 5. Test your pages
188
189 Now run `jekyll serve` and toggle between your `index.html` and `about.html` pages. The default layout should load for both pages.
190
191 You've now extracted your content out into separate files and defined a common layout for pages.
192
193 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:
194
195 ```
196 ---
197 title: Sample page
198 layout: homepage
199 ---
200 ```
201
202 This page would then use the `homepage.html` template in the `_layouts` folder.
203
204 You can even set [default front matter tags]({% link _docs/configuration.md %}#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. Anywayd, setting defaults is beyond the scope of this tutorial, let's get back to work.
205
206 ## 6. Configure site variables
207
208 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.
209
210 In your `default.html` layout, look for the `title` tags below your `head` tags:
211
212 ```
213 <title>ACME Website</title>
214 ```
215
216 Insert the following site variables:
217
218 ```
219 {% raw %}<title>{{ page.title }} | {{ site.title }}</title>{% endraw %}
220 ```
221
222 Open `_config.yml` and add a `title` property for your site's name.
223
224 ```
225 title: ACME Website
226 ```
227
228 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.
229
230 Stop your Jekyll server with <kbd>Ctrl</kbd> + <kbd>C</kbd> and restart it. Verify that the `title` tags are populating correctly.
231
232 {: .note .info}
233 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.
234
235 If you have other variables to populate in your site, rinse and repeat.
236
237 ## 7. Show posts on a page
238
239 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.
240
241 Add some posts in a `_posts` folder following the standard `YYYY-MM-DD-title.md` post format:
242
243 * `2017-01-02-my-first-post.md`
244 * `2017-01-15-my-second-post.md`
245 * `2017-02-08-my-third-post.md`
246
247 In each post, add some basic content:
248
249 ```
250 ---
251 title: My First Post
252 layout: default
253 ---
254
255 Some sample content...
256 ```
257
258 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:
259
260 ```
261 ---
262 layout: default
263 ---
264
265 {% raw %}{{ content }}
266 <ul class="myposts">
267 {% for post in site.posts %}
268 <li><a href="{{ post.url }}">{{ post.title}}</a>
269 <span class="postDate">{{ post.date | date: "%b %-d, %Y" }}</span>
270 </li>
271 {% endfor %}
272 </ul>{% endraw %}
273 ```
274
275 Create a file called `blog.md` in your root directory and specify the `home` layout:
276
277 ```
278 ---
279 title: Blog
280 layout: home
281 ---
282 ```
283
284 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.
285
286
287 ### How layouts work
288
289 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.
290
291 The following diagram shows how layouts work in Jekyll:
292
293 <img src="../../img/jekylllayoutconcept.png" alt="Concept of Jekyll layouts" />
294
295 {: .image-description}
296 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`.
297
298 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).
299
300 In your browser, go to `blog.html` and see the list of posts.
301 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.
302
303 {: .note .info}
304 At minimum, a layout should contain `{% raw %}{{ content }}{% endraw %}`, which acts as a receiver for the *content* to be rendered.
305
306 ### For loops
307
308 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.
309
310 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:
311
312 ```liquid
313 {% raw %}<ul class="myposts">
314 {% for post in site.categories.podcasts limit:3 %}
315 <li><a href="{{ post.url }}">{{ post.title}}</a>
316 <span class="postDate">{{ post.date | date: "%b %-d, %Y" }}</span>
317 </li>
318 {% endfor %}{% endraw %}
319 ```
320
321 This loop would get the latest three posts that have a category called `podcasts` in the front matter.
322
323 ## 8. Configure navigation
324
325 Now that you've configured posts, let's configure page navigation. Most websites have some navigation either in the sidebar or header area.
326
327 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.
328
329 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:
330
331 ```html
332 {% raw %}<ul>
333 {% assign mypages = site.pages | sort: "order" %}
334 {% for page in mypages %}
335 <li><a href="{{ page.url | absolute_url }}">{{ page.title }}</a></li>
336 {% endfor %}
337 </ul>{% endraw %}
338 ```
339
340 This example assumes each page would have front matter containing both a `title` and `order` property like this:
341
342 ```
343 ---
344 title: My page
345 order: 2
346 ---
347 ```
348
349 Here the `order` property will define how the pages get sorted, with `1` appearing first in the list.
350
351 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.
352
353 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:
354
355 ```yaml
356 - title: Sample page 1
357 url: /page-1-permalink/
358
359 - title: Sample page 2
360 url: /page-2-permalink/
361
362 - title: Sample page 3
363 url: /page-3-permalink/
364 ```
365
366 {: .note .info}
367 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/).
368
369 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.
370
371 To print the list of pages from the data file, use code like this:
372
373 ```html
374 {% raw %}<ul>
375 {% for link in site.data.navigation %}
376 <li><a href="{{ link.url }}">{{ link.title }}</a></li>
377 {% endfor %}
378 </ul>{% endraw %}
379 ```
380
381 If you have more sophisticated requirements around navigation, such as when building a documentation site, see the [detailed tutorial on navigation](/tutorials/navigation/).
382
383 ## 9. Simplify your site with includes
384
385 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.
386
387 Add a folder called `_includes` in your root directory. In that folder, add a file there called `sidebar.html`.
388
389 Remove your sidebar code from your `default.html` layout and insert it into the `sidebar.html` file.
390
391 Where the sidebar code previously existed in `default.html`, pull in your "include" like this:
392
393 ```liquid
394 {% raw %}{% include sidebar.html %}{% endraw %}
395 ```
396
397 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.
398
399 ## 10. RSS feed
400
401 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:
402
403 ```xml
404 ---
405 layout: null
406 ---
407
408 {% raw %}<?xml version="1.0" encoding="UTF-8" ?>
409 <rss version="2.0">
410
411 <channel>
412 <title>{{ site.title }}</title>
413 <link>{{ site.url }}</link>
414 <description>{{ site.description }}</description>
415 <lastBuildDate>{{ site.time | date_to_rfc822 }}</lastBuildDate>
416 {% for post in site.posts %}
417 <item>
418 <description>
419 {{ post.content | escape | truncate: '400' }}
420 </description>
421 <pubDate>{{ post.date | date_to_rfc822 }}</pubDate>
422 <guid>
423 {{ post.url | prepend: site.url }}
424 </guid>
425 </item>
426 {% endfor %}
427 </channel>
428 </rss>{% endraw %}
429 ```
430
431 Make sure your `_config.yml` file has properties for `title`, `url`, and `description`.
432
433 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).
434
435 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:
436
437 ```html
438 <link rel="alternate" type="application/rss+xml" href="{% raw %}{{ site.url }}{% endraw %}/feed.xml" title="{% raw %}{{ site.title }}{% endraw %}">
439 ```
440
441 You can also auto-generate your posts feed by adding a gem called [`jekyll-feed`][jekyll-feed]. This gem will also work on GitHub Pages.
442
443 ## 11. Add a sitemap
444
445 Finally, add a [site map](https://www.sitemaps.org/protocol.html). Create a `sitemap.xml` file in your root directory and add this code:
446
447 ```xml
448 ---
449 layout: null
450 search: exclude
451 ---
452
453 {% raw %}<?xml version="1.0" encoding="UTF-8"?>
454 <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
455
456 {% for page in site.pages %}
457 <url>
458 <loc>{{page.url}}</loc>
459 <lastmod>{{site.time | date: '%Y-%m-%d' }}</lastmod>
460 <changefreq>daily</changefreq>
461 <priority>0.5</priority>
462 </url>
463 {% endfor %}
464
465 {% for post in site.posts %}
466 <url>
467 <loc>{{post.url}}</loc>
468 <lastmod>{{site.time | date: '%Y-%m-%d' }}</lastmod>
469 <changefreq>daily</changefreq>
470 <priority>0.5</priority>
471 </url>
472 {% endfor %}
473
474 </urlset>{% endraw %}
475 ```
476
477 Again, we're using a `for` loop here to iterate through all posts and pages to add them to the sitemap.
478
479 You can also auto-generate your sitemap by adding a gem called [`jekyll-sitemap`][jekyll-sitemap]. This gem will also work on GitHub Pages.
480
481 ## 12. Add external services
482
483 For other services you might need (such as contact forms, search, comments, and more), look for third-party services. For example, you might use the following:
484
485 * For comments: [Disqus](https://disqus.com/)
486 * For a newsletter: [Tinyletter](https://tinyletter.com/)
487 * For contact forms: [Wufoo](https://www.wufoo.com/)
488 * For search: [Algolia Docsearch](https://community.algolia.com/docsearch/)
489
490 For more details on services for static sites, see the [Third Parties](https://learn.cloudcannon.com/jekyll-third-parties/) list and tutorials from CloudCannon.
491
492 Your Jekyll pages consist of HTML, CSS, and JavaScript, so pretty much any code you need to embed will work without a problem.
493
494 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.
495
496 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:
497
498 ```
499 ---
500 layout: null
501 ---
502 ```
503
504 ## 13. Conclusion
505
506 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.
507
508 To deploy your site, consider using [GitHub Pages](https://pages.github.com/), [Netlify](https://www.netlify.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.
509
510 You can also package your layouts, includes and assets into a Ruby `gem` and [make it a Jekyll theme]({% link _docs/themes.md %}#creating-a-theme).
511
512 ## Additional resources
513
514 Here are some additional tutorials on creating Jekyll sites:
515
516 * [Convert a static site to Jekyll](http://jekyll.tips/jekyll-casts/converting-a-static-site-to-jekyll/)
517 * [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/)
518
519 [jekyll-sitemap]: https://help.github.com/articles/sitemaps-for-github-pages/
520 [jekyll-feed]: https://help.github.com/articles/atom-rss-feeds-for-github-pages/
0 ---
1 title: Convert an HTML site to Jekyll
2 author: tomjoht
3 date: 2017-02-10 21:58:56 -0800
4 ---
5
6 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.
7
8 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.
9
10 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.
11
12 Although websites can have sophisticated features and controls, we'll keep things simple in this tutorial.
13
14 ## What is a Jekyll Website?
15
16 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 %}).
17
18 We'll start with a *basic Jekyll site* consisting of three files:
19
20 ```
21 .
22 ├── _config.yml
23 ├── _layouts
24 │   └── default.html
25 └── index.md
26 ```
27
28 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`.
29
30 ```sh
31 touch _config.yml index.md default.html
32 mkdir _layouts && mv default.html _layouts
33 ```
34
35 Fire up your favorite editor, and populate the contents of the `default.html` and `index.md` files as follows:
36
37 **_config.yml**
38
39 ```yaml
40 name: My Jekyll Website
41 ```
42
43 **_layouts/default.html**
44
45 {% raw %}
46 ```html
47 <!DOCTYPE html>
48 <html>
49 <body>
50 {{ content }}
51 </body>
52 </html>
53 ```
54 {% endraw %}
55
56 **index.md**
57
58 {% raw %}
59 ```markdown
60 ---
61 title: My page
62 layout: default
63 ---
64
65 # {{ page.title }}
66
67 Content is written in [Markdown](https://learnxinyminutes.com/docs/markdown/).
68 Plain text format allows you to focus on your **content**.
69
70 <!--
71 You can use HTML elements in Markdown, such as the comment element, and they won't
72 be affected by a markdown parser. However, if you create an HTML element in your
73 markdown file, you cannot use markdown syntax within that element's contents.
74 -->
75 ```
76 {% endraw %}
77
78 Now `cd` to `my_jekyll_site` and serve the site with the built-in server:
79
80 ```sh
81 cd my_jekyll_site
82 jekyll serve
83 ```
84
85 {: .note .info}
86 If you have a Gemfile, [use Bundler](/docs/ruby-101/#bundler) by typing `bundle exec jekyll serve` instead.
87
88 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.
89
90 This is a Jekyll site at the most basic functional level. Here's what is happening:
91
92 * 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.
93 * 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).
94 * 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.
95 * The processed files get written as `.html` files in the `_site` directory.
96
97 You can read more about how Jekyll processes the files in [order of Interpretation]({% link _tutorials/orderofinterpretation.md %}).
98
99 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.
100
101 ## 1. Create a template for your default layout
102
103 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.
104
105 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.
106
107 {: .note .info}
108 Regardless of the site, do check the license and make sure you have permission to copy and use the code.
109
110 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 &mdash; that is, each page or post will use this layout when Jekyll builds the site.
111
112 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.
113
114 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.
115
116 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.)
117
118 Jekyll provides some [filters](/docs/liquid/filters/) to prepend a site URL before path. For example, you could preface your stylesheet like this:
119
120 {% raw %}
121 ```liquid
122 {{ "/assets/style.css" | relative_url }}
123 ```
124 {% endraw %}
125
126 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/`).
127
128 You can also use an `absolute_url` filter. This filter will prepend the `url` *and* `baseurl` value to the input:
129
130 {% raw %}
131 ```liquid
132 {{ "/assets/style.css" | absolute_url }}
133 ```
134 {% endraw %}
135
136 Again, both `url` and `baseurl` can be defined in your site's config file, like this:
137
138 ```yaml
139 url: http://mysite.com
140 baseurl: /blog
141 ```
142
143 The result in the output will be `http://mysite.com/blog/assets/style.css`.
144
145 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.
146
147 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.
148
149 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.
150
151 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.
152
153 ## 2. Identify the content part of the layout
154
155 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 %}.
156
157 Remove the content part (keep everything else: navigation menu, sidebar, footer, etc.) and replace it with {% raw %}`{{ content }}`{% endraw %}.
158
159 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.
160
161 ## 3. Create a couple of files with front matter tags
162
163 Create a couple of files (`index.md` and `about.md`) in your root directory.
164
165 In your `index.md` file, add some front matter tags containing a `title` and `layout` property, like this:
166
167 ```markdown
168 ---
169 title: Home
170 layout: default
171 ---
172
173 Some page content here...
174 ```
175
176 Create another page for testing called `about.md` with similar front matter tags.
177
178 {: .note .info}
179 If you don't specify a layout in your pages, Jekyll will simply render that page as an unstyled basic HTML page.
180
181 ## 4. Add a configuration file
182
183 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:
184
185 ```yaml
186 markdown: kramdown
187 kramdown:
188 input: GFM
189 ```
190
191 You can find additional [Markdown Options](/docs/configuration/markdown/) in the Jekyll docs, though it's unlikely that you'll need them.
192
193 ## 5. Test your pages
194
195 Now run `jekyll serve` and toggle between your `index.html` and `about.html` pages. The default layout should load for both pages.
196
197 You've now extracted your content out into separate files and defined a common layout for pages.
198
199 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:
200
201 ```yaml
202 ---
203 title: Sample page
204 layout: homepage
205 ---
206 ```
207
208 This page would then use the `homepage.html` template in the `_layouts` folder.
209
210 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.
211
212 ## 6. Configure site variables
213
214 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.
215
216 In your `default.html` layout, look for the `title` tags below your `head` tags:
217
218 ```
219 <title>ACME Website</title>
220 ```
221
222 Insert the following site variables:
223
224 {% raw %}
225 ```liquid
226 <title>{{ page.title }} | {{ site.title }}</title>
227 ```
228 {% endraw %}
229
230 Open `_config.yml` and add a `title` property for your site's name.
231
232 ```yaml
233 title: ACME Website
234 ```
235
236 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.
237
238 Stop your Jekyll server with <kbd>Ctrl</kbd> + <kbd>C</kbd> and restart it. Verify that the `title` tags are populating correctly.
239
240 {: .note .info}
241 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.
242
243 If you have other variables to populate in your site, rinse and repeat.
244
245 ## 7. Show posts on a page
246
247 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.
248
249 Add some posts in a `_posts` folder following the standard `YYYY-MM-DD-title.md` post format:
250
251 * `2017-01-02-my-first-post.md`
252 * `2017-01-15-my-second-post.md`
253 * `2017-02-08-my-third-post.md`
254
255 In each post, add some basic content:
256
257 ```markdown
258 ---
259 title: My First Post
260 layout: default
261 ---
262
263 Some sample content...
264 ```
265
266 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:
267
268 {% raw %}
269 ```liquid
270 ---
271 layout: default
272 ---
273
274 {{ content }}
275 <ul class="myposts">
276 {% for post in site.posts %}
277 <li><a href="{{ post.url }}">{{ post.title}}</a>
278 <span class="postDate">{{ post.date | date: "%b %-d, %Y" }}</span>
279 </li>
280 {% endfor %}
281 </ul>
282 ```
283 {% endraw %}
284
285 Create a file called `blog.md` in your root directory and specify the `home` layout:
286
287 ```yaml
288 ---
289 title: Blog
290 layout: home
291 ---
292 ```
293
294 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.
295
296 ### How layouts work
297
298 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.
299
300 The following diagram shows how layouts work in Jekyll:
301
302 <img src="../../img/jekylllayoutconcept.png" alt="Concept of Jekyll layouts" />
303
304 {: .image-description}
305 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`.
306
307 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).
308
309 In your browser, go to `blog.html` and see the list of posts.
310 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.
311
312 {: .note .info}
313 At minimum, a layout should contain {% raw %}`{{ content }}`{% endraw %}, which acts as a receiver for the *content* to be rendered.
314
315 ### For loops
316
317 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.
318
319 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:
320
321 {% raw %}
322 ```liquid
323 <ul class="myposts">
324 {% for post in site.categories.podcasts limit:3 %}
325 <li><a href="{{ post.url }}">{{ post.title}}</a>
326 <span class="postDate">{{ post.date | date: "%b %-d, %Y" }}</span>
327 </li>
328 {% endfor %}
329 </ul>
330 ```
331 {% endraw %}
332
333 This loop would get the latest three posts that have a category called `podcasts` in the front matter.
334
335 ## 8. Configure navigation
336
337 Now that you've configured posts, let's configure page navigation. Most websites have some navigation either in the sidebar or header area.
338
339 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.
340
341 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:
342
343 {% raw %}
344 ```liquid
345 <ul>
346 {% assign mypages = site.pages | sort: "order" %}
347 {% for page in mypages %}
348 <li><a href="{{ page.url | absolute_url }}">{{ page.title }}</a></li>
349 {% endfor %}
350 </ul>
351 ```
352 {% endraw %}
353
354 This example assumes each page would have front matter containing both a `title` and `order` property like this:
355
356 ```yaml
357 ---
358 title: My page
359 order: 2
360 ---
361 ```
362
363 Here the `order` property will define how the pages get sorted, with `1` appearing first in the list.
364
365 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.
366
367 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:
368
369 ```yaml
370 - title: Sample page 1
371 url: /page-1-permalink/
372
373 - title: Sample page 2
374 url: /page-2-permalink/
375
376 - title: Sample page 3
377 url: /page-3-permalink/
378 ```
379
380 {: .note .info}
381 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/).
382
383 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.
384
385 To print the list of pages from the data file, use code like this:
386
387 {% raw %}
388 ```liquid
389 <ul>
390 {% for link in site.data.navigation %}
391 <li><a href="{{ link.url }}">{{ link.title }}</a></li>
392 {% endfor %}
393 </ul>
394 ```
395 {% endraw %}
396
397 If you have more sophisticated requirements around navigation, such as when building a documentation site, see the [detailed tutorial on navigation](/tutorials/navigation/).
398
399 ## 9. Simplify your site with includes
400
401 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.
402
403 Add a folder called `_includes` in your root directory. In that folder, add a file there called `sidebar.html`.
404
405 Remove your sidebar code from your `default.html` layout and insert it into the `sidebar.html` file.
406
407 Where the sidebar code previously existed in `default.html`, pull in your "include" like this:
408
409 {% raw %}
410 ```liquid
411 {% include sidebar.html %}
412 ```
413 {% endraw %}
414
415 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.
416
417 ## 10. RSS feed
418
419 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:
420
421 {% raw %}
422 ```liquid
423 ---
424 layout: null
425 ---
426
427 <?xml version="1.0" encoding="UTF-8" ?>
428 <rss version="2.0">
429
430 <channel>
431 <title>{{ site.title }}</title>
432 <link>{{ site.url }}</link>
433 <atom:link href="{{ page.url | prepend: site.url }}" rel="self" type="application/rss+xml" />
434 <description>{{ site.description }}</description>
435 <lastBuildDate>{{ site.time | date_to_rfc822 }}</lastBuildDate>
436 {% for post in site.posts %}
437 <item>
438 <title>{{ post.title }}</title>
439 <link>
440 {{ post.url | prepend: site.url }}
441 </link>
442 <description>
443 {{ post.content | escape | truncate: '400' }}
444 </description>
445 <pubDate>{{ post.date | date_to_rfc822 }}</pubDate>
446 <guid>
447 {{ post.url | prepend: site.url }}
448 </guid>
449 </item>
450 {% endfor %}
451 </channel>
452 </rss>
453 ```
454 {% endraw %}
455
456 Make sure your `_config.yml` file has properties for `title`, `url`, and `description`.
457
458 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).
459
460 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:
461
462 {% raw %}
463 ```liquid
464 <link rel="alternate" type="application/rss+xml" href="{{ site.url }}/feed.xml" title="{{ site.title }}">
465 ```
466 {% endraw %}
467
468 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.
469
470 ## 11. Add a sitemap
471
472 Finally, add a [site map](https://www.sitemaps.org/protocol.html). Create a `sitemap.xml` file in your root directory and add this code:
473
474 {% raw %}
475 ```liquid
476 ---
477 layout: null
478 search: exclude
479 ---
480
481 <?xml version="1.0" encoding="UTF-8"?>
482 <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
483
484 {% for page in site.pages %}
485 <url>
486 <loc>{{page.url}}</loc>
487 <lastmod>{{site.time | date: '%Y-%m-%d' }}</lastmod>
488 <changefreq>daily</changefreq>
489 <priority>0.5</priority>
490 </url>
491 {% endfor %}
492
493 {% for post in site.posts %}
494 <url>
495 <loc>{{post.url}}</loc>
496 <lastmod>{{site.time | date: '%Y-%m-%d' }}</lastmod>
497 <changefreq>daily</changefreq>
498 <priority>0.5</priority>
499 </url>
500 {% endfor %}
501
502 </urlset>
503 ```
504 {% endraw %}
505
506 Again, we're using a `for` loop here to iterate through all posts and pages to add them to the sitemap.
507
508 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.
509
510 ## 12. Add external services
511
512 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.
513
514 Your Jekyll pages consist of HTML, CSS, and JavaScript, so pretty much any code you need to embed will work without a problem.
515
516 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.
517
518 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:
519
520 ```yaml
521 ---
522 layout: null
523 ---
524 ```
525
526 ## 13. Conclusion
527
528 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.
529
530 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.
531
532 You can also package your layouts, includes and assets into a Ruby `gem` and [make it a Jekyll theme](/docs/themes/).
533
534 ## Additional resources
535
536 Here are some additional tutorials on creating Jekyll sites:
537
538 * [Convert a static site to Jekyll](http://jekyll.tips/jekyll-casts/converting-a-static-site-to-jekyll/)
539 * [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/)
0 ---
1 title: Tabulate CSV Data
2 author: MichaelCurrin
3 date: 2020-04-01 20:30:00 +0200
4 ---
5
6 This tutorial shows how to use Jekyll to read a CSV and render the data as an HTML table.
7
8 This approach will:
9
10 - use the CSV's first row as the HTML table header.
11 - use remaining rows for the body of the table.
12 - preserve the order of the columns from the original CSV.
13 - be flexible enough to work with _any_ valid CSV that is referenced.
14
15 There is no need to specify what the names of the columns are, or how many columns there are.
16 The trick to this tutorial is that, when we iterate over the row data, we pick up the _first row_
17 and unpack that so we can get the header names.
18
19 Follow the steps below to convert a sample CSV of authors into an HTML table.
20
21
22 ## 1. Create a CSV
23
24 Create a CSV file in your [Data files]({{ '/docs/datafiles/' | relative_url }}) directory so
25 that Jekyll will pick it up. A sample path and CSV data are shown below:
26
27 `_data/authors.csv`
28
29 ```
30 First name,Last name,Age,Location
31 John,Doe,35,United States
32 Jane,Doe,29,France
33 Jack,Hill,25,Australia
34 ```
35
36 That data file will now be available in Jekyll like this:
37
38 {% raw %}
39 ```liquid
40 {{ site.data.authors }}
41 ```
42 {% endraw %}
43
44
45 ## 2. Add a table
46
47 Choose an HTML or markdown file where you want your table to be shown.
48
49 For example: `table_test.md`
50
51 ```yaml
52 ---
53 title: Table test
54 ---
55 ```
56
57 ### Inspect a row
58
59 Grab the first row and see what it looks like using the `inspect` filter.
60
61 {% raw %}
62 ```liquid
63 {% assign row = site.data.authors[0] %}
64 {{ row | inspect }}
65 ```
66 {% endraw %}
67
68
69 The result will be a _hash_ (an object consisting of key-value pairs) which looks like this:
70
71 ```ruby
72 {
73 "First name"=>"John",
74 "Last name"=>"Doe",
75 "Age"=>"35",
76 "Location"=>"United States"
77 }
78 ```
79
80 Note that Jekyll _does_ in fact preserve the order here, based on the original CSV.
81
82
83 ### Unpack a row
84
85 A simple solution would be to hardcode the field names when looking up the row values by key.
86
87 {% raw %}
88 ```liquid
89 {{ row["First name"] }}
90 {{ row["Last name"] }}
91 ```
92 {% endraw %}
93
94 But we prefer a solution that will work for _any_ CSV, without specifying the column names upfront.
95 So we iterate over the `row` object using a `for` loop:
96
97 {% raw %}
98 ```liquid
99 {% assign row = site.data.authors[0] %}
100 {% for pair in row %}
101 {{ pair | inspect }}
102 {% endfor %}
103 ```
104 {% endraw %}
105
106 This produces the following. Note the first item in each pair is the _key_ and the second will be
107 the _value_.
108
109 ```
110 ["First name", "John"]
111 ["Last name", "Doe"]
112 ["Age", "35"]
113 ["Location", "United States"]
114 ```
115
116 ### Create a table header row
117
118 Here we make a table with a single table row (`tr`), made up of table header (`th`) tags. We find
119 the header name by getting the first element (at index `0`) from `pair`. We ignore the second
120 element as we don't need the value yet.
121
122 {% raw %}
123 ```liquid
124 <table>
125 {% for row in site.data.authors %}
126 {% if forloop.first %}
127 <tr>
128 {% for pair in row %}
129 <th>{{ pair[0] }}</th>
130 {% endfor %}
131 </tr>
132 {% endif %}
133 {% endfor %}
134 </table>
135 {% endraw %}
136 ```
137
138 For now, we do not display any content from the second row onwards. We achieve this by using
139 `forloop.first`, since this will return true for the _first_ row and false otherwise.
140
141
142 ### Add table data rows
143
144 In this section we add the data rows to the table. Now, we use the second element of `pair`
145 to find the value.
146
147 For convenience, we render using the `tablerow` tag - this works like a `for` loop, but the inner
148 data will be rendered with `tr` and `td` HTML tags for us. Unfortunately, there is no equivalent for
149 the header row, so we must write that out in full, as in the previous section.
150
151 {% raw %}
152 ```liquid
153 ---
154 title: Table test
155 ---
156
157 <table>
158 {% for row in site.data.authors %}
159 {% if forloop.first %}
160 <tr>
161 {% for pair in row %}
162 <th>{{ pair[0] }}</th>
163 {% endfor %}
164 </tr>
165 {% endif %}
166
167 {% tablerow pair in row %}
168 {{ pair[1] }}
169 {% endtablerow %}
170 {% endfor %}
171 </table>
172 ```
173 {% endraw %}
174
175
176 With the code above, the complete table would look like this:
177
178 <table>
179 <tr>
180 <th>First name</th>
181 <th>Last name</th>
182 <th>Age</th>
183 <th>Location</th>
184 </tr>
185 <tr>
186 <td>John</td>
187 <td>Doe</td>
188 <td>35</td>
189 <td>United States</td>
190 </tr>
191 <tr>
192 <td>Jane</td>
193 <td>Doe</td>
194 <td>29</td>
195 <td>France</td>
196 </tr>
197 <tr>
198 <td>Jack</td>
199 <td>Hill</td>
200 <td>25</td>
201 <td>Australia</td>
202 </tr>
203 </table>
204
205 That's it - you can now turn a CSV into an HTML table using Jekyll.
206
207 ## Next steps
208
209 - Change the field names in the CSV.
210 - Choose a different CSV.
211 - Add CSS styling to your table.
212 - Render the table using a JSON or YAML input file.
00 ---
1 layout: tutorials
2 permalink: /tutorials/custom-404-page/
31 title: Custom 404 Page
2 author: ashmaroli
3 date: 2017-03-11 17:23:24 +0530
44 ---
55
66 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.
7
87
98 ## On GitHub Pages
109
1110 Any `404.html` at the **root of your `_site` directory** will be served automatically by GitHub Pages and the local WEBrick development server.
1211
13 Simply add a `404.md` or `404.html` at the root of your site's source directory and include the YAML Front Matter data to use the theme's base layout.
12 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.
1413
1514 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.
1615
4746
4847 More info on configuring Apache Error Pages can found in [official documentation](https://httpd.apache.org/docs/current/mod/core.html#errordocument).
4948
50
5149 ## Hosting on Nginx server
5250
5351 The procedure is just as simple as configuring Apache servers, but slightly different.
5452
55 Add the following to the nginx configuration file, `nginx.conf`, which is usually located inside `/etc/nginx/` or `/etc/nginx/conf/`:
53 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:
5654
5755 ```nginx
5856 server {
6361 }
6462 ```
6563
64 If the `server` block already exists, only add the code inside the `server` block given above.
6665 The `location` directive prevents users from directly browsing the 404.html page.
66
67 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).
68
69 <p class="note warning">
70 Proceed with caution while editing the configuration file.
71 </p>
00 ---
1 layout: tutorials
21 title: Tutorials
32 permalink: /tutorials/home/
43 redirect_from: /tutorials/index.html
54 ---
65
7 In contrast to [Docs](/docs/home/), Tutorials provide more detailed, narrative instruction that cover a variety of Jekyll topics and scenarios. Tutorials might contain the following:
6 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:
87
98 * Step-by-step processes through particular scenarios or challenges
109 * Full walk-throughs using sample data, showing inputs and results from the sample data
1413
1514 In short, tutorials aren't the core reference information in docs. They walk users through processes from beginning to end.
1615
17 {: .info .note}
18 **Note:** The Tutorials section is new, so there aren't many tutorials yet. You can add a tutorial here to help populate this section.
16 {: .note .info}
17 The Tutorials section is new, so there aren't many tutorials yet. You can add a tutorial here to help populate this section.
1918
2019 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.
2120
2322
2423 We welcome your tutorial contributions. To add your tutorial:
2524
26 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/).
25 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/).
2726 2. Add your tutorial in the `_tutorials` collection.
2827 3. Make sure your tutorial has the same front matter items as other tutorial items.
2928 5. Add a reference to your tutorial filename in `_data/tutorials.yml`. This allows your tutorial to appear in the Tutorials sidebar.
3029 6. Follow the regular git workflow to submit the pull request.
3130
3231 When you submit your pull request, the Jekyll documentation team will review your contribution and either merge it or suggest edits.
33
34
00 ---
1 layout: tutorials
2 permalink: /tutorials/navigation/
31 title: Navigation
2 author: tomjoht
3 date: 2017-01-24 15:38:17 -0800
44 ---
55
66 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.
4747
4848 **Liquid**
4949
50 ```liquid
51 {% raw %}<h2>{{ site.data.samplelist.docs_list_title }}</h2>
50 {% raw %}
51 ```liquid
52 <h2>{{ site.data.samplelist.docs_list_title }}</h2>
5253 <ul>
5354 {% for item in site.data.samplelist.docs %}
54 <li><a href="{{ item.url }}" alt="{{ item.title }}">{{ item.title }}</a></li>
55 <li><a href="{{ item.url }}">{{ item.title }}</a></li>
5556 {% endfor %}
56 </ul>{% endraw %}
57 ```
58
59 **Result**
60 <div class="highlight result">
57 </ul>
58 ```
59 {% endraw %}
60
61 **Result**
62 <div class="highlight result" data-proofer-ignore>
6163 <h2>ACME Documentation</h2>
6264 <ul>
63 <li><a href="#" alt="Introduction">Introduction</a></li>
64 <li><a href="#" alt="Configuration">Configuration</a></li>
65 <li><a href="#" alt="Deployment">Deployment</a></li>
66 </ul>
67 </div>
68
69 {: .note .info }
70 For the results in these fictitious samples, `#` is manually substituted for the actual link value to avoid 404 errors.)
65 <li><a href="#">Introduction</a></li>
66 <li><a href="#">Configuration</a></li>
67 <li><a href="#">Deployment</a></li>
68 </ul>
69 </div>
70
71 {: .note .info}
72 For the results in these fictitious samples, `#` is manually substituted for the actual link value (to avoid 404 errors.)
7173
7274 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`).
7375
9597 {% assign doclist = site.data.samplelist.docs | sort: 'title' %}
9698 <ol>
9799 {% for item in doclist %}
98 <li><a href="{{ item.url }}" alt="{{ item.title }}">{{ item.title }}</a></li>
100 <li><a href="{{ item.url }}">{{ item.title }}</a></li>
99101 {% endfor %}
100102 </ol>
101103 ```
103105
104106 **Result**
105107
106 <div class="highlight result">
108 <div class="highlight result" data-proofer-ignore>
107109 <ol>
108 <li><a href="#" alt="Configuration">Configuration</a></li>
109 <li><a href="#" alt="Deployment">Deployment</a></li>
110 <li><a href="#" alt="Introduction">Introduction</a></li>
110 <li><a href="#">Configuration</a></li>
111 <li><a href="#">Deployment</a></li>
112 <li><a href="#">Introduction</a></li>
111113 </ol>
112114 </div>
113115
173175 {% endraw %}
174176
175177 **Result**
176 <div class="highlight result">
178 <div class="highlight result" data-proofer-ignore>
177179 <h3>Group 1</h3>
178180 <ul>
179181 <li><a href="#">Thing 1</a></li>
280282
281283 **Result**
282284
283 <div class="highlight result">
285 <div class="highlight result" data-proofer-ignore>
284286 <div>
285287 <h3>Group 1</h3>
286288 <ul>
351353
352354 **Result**
353355
354 <div class="highlight result">
356 <div class="highlight result" data-proofer-ignore>
355357 <ul>
356358 <li><a href="#">Introduction</a></li>
357359 <li><a href="#">Configuration</a></li>
368370 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.
369371
370372 **CSS**
373
371374 ```css
372375 .result li.active a {
373376 color: lightgray;
374377 cursor: default;
375 }
376 ```
378 }
379 ```
380
377381 **Liquid**
378382
379383 {% raw %}
395399 }
396400 </style>
397401
398 <div class="highlight result">
402 <div class="highlight result" data-proofer-ignore>
399403 <ul>
400404 <li class=""><a href="#">Introduction</a></li>
401405 <li class=""><a href="#">Configuration</a></li>
405409
406410 In this case, assume `Deployment` is the current page.
407411
408 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.
412 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.
409413
410414 ## Scenario 7: Including items conditionally
411415
412416 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.
413417
414418 **YAML**
419
415420 ```yaml
416421 docs2_list_title: ACME Documentation
417422 docs2:
445450
446451 **Result**
447452
448 <div class="highlight result">
453 <div class="highlight result" data-proofer-ignore>
449454 <ul>
450455 <li><a href="#">Introduction</a></li>
451456 <li><a href="#">Configuration</a></li>
456461
457462 ## Scenario 8: Retrieving items based on front matter properties
458463
459 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 YAML front matter of each page or collection and get the content based on properties in the front matter.
464 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.
460465
461466 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.)
462467
527532
528533 The result would be as follows:
529534
530 <div class="highlight result">
535 <div class="highlight result" data-proofer-ignore>
531536 <h3>Getting Started</h3>
532537 <ul>
533538 <li><a href="#">Sample1</a></li>
563568
564569 **Result**
565570
566 <div class="highlight result">
571 <div class="highlight result" data-proofer-ignore>
567572 <h2>Getting-started</h2>
568573 <ul>
569574 <li><a href="#">Sample2</a></li>
585590
586591 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:
587592
588 ```yaml
593 ```json
589594 [
590595 {"name": "getting-started", "items": [Sample 1, Sample 2],"size": 2},
591 {"name": "configuration", "items": [Topic 1, Topic 2], "size": 2},
592 {"name": "deployment", "items": [Widget 1, Widget 2, "size": 2}
596 {"name": "configuration", "items": [Topic 1, Topic 2], "size": 2},
597 {"name": "deployment", "items": [Widget 1, Widget 2], "size": 2}
593598 ]
594599 ```
595600
602607 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.
603608
604609 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.
610
611 ## Scenario 9: Nested tree navigation with recursion
612
613 Suppose you want a nested tree navigation of any depth. We can achieve this by recursively looping through our tree of navigation links.
614
615 **YAML**
616
617 ```yaml
618 nav:
619 - title: Deployment
620 url: deployment.html
621 subnav:
622 - title: Heroku
623 url: heroku.html
624 subnav:
625 - title: Jekyll on Heroku
626 url: jekyll-on-heroku.html
627 - title: Help
628 url: help.html
629 ```
630
631 **Liquid**
632
633 First, we'll create an include that we can use for rendering the navigation tree. This file would be `_includes/nav.html`
634
635 {% raw %}
636 ```liquid
637 <ul>
638 {% for item in include.nav %}
639 <li><a href="{{ item.url }}">{{ item.title }}</a></li>
640
641 {% if item.subnav %}
642 {% include nav.html nav=item.subnav %}
643 {% endif %}
644 {% endfor %}
645 </ul>
646 ```
647 {% endraw %}
648
649 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.
650
651 {% raw %}
652 ```liquid
653 {% include nav.html nav=page.nav %}
654 ```
655 {% endraw %}
656
657 Our include will use this first, then look through each item for a `subnav` property to recursively render the nested lists.
658
659 **Result**
660 <div class="highlight result" data-proofer-ignore>
661 <ul>
662 <li><a href="#">Deployment</a></li>
663 <ul>
664 <li><a href="#">Heroku</a></li>
665 <ul>
666 <li><a href="#">Jekyll On Heroku</a></li>
667 </ul>
668 </ul>
669 <li><a href="#">Help</a></li>
670 </ul>
671 </div>
00 ---
1 layout: tutorials
2 permalink: /tutorials/orderofinterpretation/
31 title: Order of interpretation
2 author: tomjoht
3 date: 2017-01-29 21:45:03 -0800
44 ---
55
66 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.
1515
1616 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.)
1717
18 2. **Liquid**. Jekyll processes any [Liquid](https://github.com/Shopify/liquid) formatting in pages that contain [front matter]({% link _docs/frontmatter.md %}). You can identify Liquid as follows:
19 * **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 &mdash; for example, `{% raw %}{% endhighlight %}{% endraw %}`.
20 * **Liquid variables** start and end with double curly braces. For example: `{% raw %}{{ site.myvariable }}{% endraw %}` or `{% raw %}{{ content }}{% endraw %}`.
21 * **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 %}`.
18 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:
19 * **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 &mdash; for example, {% raw %}`{% endhighlight %}`{% endraw %}.
20 * **Liquid variables** start and end with double curly braces. For example: {% raw %}`{{ site.myvariable }}`{% endraw %} or {% raw %}`{{ content }}`{% endraw %}.
21 * **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 %}.
2222
2323 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.
2424
25 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.
25 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.
2626
2727 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.
2828
8888 ```
8989 {% endraw %}
9090
91 The `highlight` tag *is* Liquid. (Liquid passes the content to Rouge or Pygments 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.
91 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.
9292
9393 ### Liquid mixed with JavaScript isn't rendered
9494
114114 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:
115115
116116 {% raw %}
117 ```js
117 ```javascript
118118 <button onclick="someFunction()">Click me</button>
119119
120120 <p id="intro"></p>
128128 ```
129129 {% endraw %}
130130
131 When Jekyll builds the site, this `someContent` property populates the script's values, converting `{% raw %}{{ page.someContent }}{% endraw %}` to `"This is some content"`.
131 When Jekyll builds the site, this `someContent` property populates the script's values, converting {% raw %}`{{ page.someContent }}`{% endraw %} to `"This is some content"`.
132132
133133 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.
134134
00 ---
1 layout: tutorials
2 permalink: /tutorials/using-jekyll-with-bundler/
31 title: Using Jekyll with Bundler
2 author: mkasberg
3 date: 2018-03-06 21:33:25 -0700
44 ---
55
66 > Bundler provides a consistent environment for Ruby projects by tracking and
88
99 [Bundler](https://bundler.io) can be a great tool to use with Jekyll. Because it
1010 tracks dependencies on a per-project basis, it is particularly useful if you
11 need to run different versions of Jekyll in different projects, or if you don't
12 want to install Jekyll at the system or user level. This tutorial will show you
13 how to create a new Jekyll project using Bundler and without installing Jekyll
14 outside the project.
11 need to run different versions of Jekyll in different projects.
12
13 In addition, because it can (optionally) install dependencies in the project
14 folder, it can help you avoid permissions issues you might otherwise run into.
15 The usual way to use Jekyll is to install Jekyll to the system's default gem
16 installation directory and then run `jekyll new`. In this tutorial, we'll show
17 you how to create a new Jekyll project using Bundler and without installing gems
18 outside the project directory.
19
20 <div class="note info">
21 <h5>This is not the simplest way to start using Jekyll</h5>
22 <p>
23 This tutorial helps you get Jekyll set up using Bundler, and optionally
24 without any system-wide gem installations. If prefer installing the jekyll
25 command to your default gem installation directory, you might want the
26 <a href="{% link _docs/index.md %}">Quickstart</a>.
27 </p>
28 </div>
1529
1630 ## Before You Begin
1731
3145 bundle init
3246 ```
3347
34 ## Configure Bundler
48 ## Configure Bundler Install Path
3549
36 This step is optional, but encouraged. We're going to configure Bundler to install
37 gems in the `./vendor/bundle/` project subdirectory. This allows us to install
38 our dependencies in an isolated environment, ensuring they don't conflict with
39 other gems on your system. If you skip this step, Bundler will install your
40 dependencies globally on your system.
50 This step is optional. In this step, we're going to configure Bundler to install
51 gems in the `./vendor/bundle/` project subdirectory. The advantage of doing this
52 is that bundler will install gems within your project folder instead of the
53 location used by `gem install`. This can help you avoid permissions errors you
54 might otherwise get during gem installation, depending how you installed Ruby.
55 If you skip this step, Bundler will install your dependencies to the location
56 used by `gem install`.
57
4158
4259 ```sh
43 bundle install --path vendor/bundle
60 bundle config set --local path 'vendor/bundle'
4461 ```
4562
4663 <div class="note info">
5673
5774 Now, we're going to use Bundler to add Jekyll as a dependency of our new
5875 project. This command will add the Jekyll gem to our Gemfile and install it to
59 the `./vendor/bundle/` folder.
76 the `./vendor/bundle/` folder (or your default gem installation directory if you
77 didn't set a custom path).
6078
6179 ```sh
6280 bundle add jekyll
89107 `./vendor/` and `./.bundle/` folders since they contain user- or
90108 platform-specific information. New users will be able to install the correct
91109 dependencies based on `Gemfile` and `Gemfile.lock`, which should both be checked
92 in. You can use this `.gitigonre` to get started, if you want.
110 in. You can use this `.gitignore` to get started, if you want.
93111
94112 **.gitignore**
95113
96114 ```
115 # Ignore metadata generated by Jekyll
116 _site/
117 .sass-cache/
118 .jekyll-cache/
119 .jekyll-metadata
120
97121 # Ignore folders generated by Bundler
98 vendor
99 .bundle
100
101 # Ignore folders generated by Jekyll
102 .sass-cache
103 _site
122 .bundle/
123 vendor/
104124 ```
105
00 ---
1 layout: tutorials
2 permalink: /tutorials/video-walkthroughs/
31 title: Video Walkthroughs
2 author: giraffeacademy
3 date: 2017-10-02 15:20:08 -0400
44 ---
55
66 [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.
2929 16. [Conditionals](https://www.youtube.com/watch?v=iNZBEki_x6o&list=PLLAZ4kZ9dFpOPV5C5Ay0pHaa0RJFhcmcB&index=16)
3030 17. [Data Files](https://www.youtube.com/watch?v=M6b0KmLB-pM&list=PLLAZ4kZ9dFpOPV5C5Ay0pHaa0RJFhcmcB&index=17)
3131 18. [Static Files](https://www.youtube.com/watch?v=knWjmVlVpso&index=18&list=PLLAZ4kZ9dFpOPV5C5Ay0pHaa0RJFhcmcB)
32 19. [Hosting on Github Pages](https://www.youtube.com/watch?v=fqFjuX4VZmU&list=PLLAZ4kZ9dFpOPV5C5Ay0pHaa0RJFhcmcB&index=19)
33
34
32 19. [Hosting on GitHub Pages](https://www.youtube.com/watch?v=fqFjuX4VZmU&list=PLLAZ4kZ9dFpOPV5C5Ay0pHaa0RJFhcmcB&index=19)
+0
-26
docs/community/index.md less more
0 ---
1 layout: page
2 title: JekyllConf
3 redirect_from: /community/index.html
4 permalink: /jekyllconf/
5 ---
6
7 [JekyllConf](http://jekyllconf.com) is a free, online conference for all things Jekyll hosted by [CloudCannon](http://cloudcannon.com). Each year members of the Jekyll community speak about interesting use cases, tricks they've learned, or meta Jekyll topics.
8
9 ## Featured
10
11 {% assign random = site.time | date: "%s%N" | modulo: site.data.jekyllconf-talks.size %}
12 {% assign featured = site.data.jekyllconf-talks[random] %}
13
14 {{ featured.topic }} - [{{ featured.speaker }}](https://twitter.com/{{ featured.twitter_handle }})
15 <div class="videoWrapper">
16 <iframe width="420" height="315" src="https://www.youtube.com/embed/{{ featured.youtube_id }}" frameborder="0" allowfullscreen></iframe>
17 </div>
18
19 {% assign talks = site.data.jekyllconf-talks | group_by: 'year' %}
20 {% for year in talks reversed %}
21 ## {{ year.name }}
22 {% for talk in year.items %}
23 * [{{ talk.topic }}](https://youtu.be/{{ talk.youtube_id }}) - [{{ talk.speaker }}](https://twitter.com/{{ talk.twitter_handle }})
24 {% endfor %}
25 {% endfor %}
55 @import "gridism";
66 @import "pygments";
77 @import "font-awesome";
8 @import "fonts";
9 @import "docsearch";
810 @import "style";
9 @import "docsearch";
+0
-58
docs/help/index.md less more
0 ---
1 layout: page
2 title: Getting Help
3 ---
4
5 Need help with Jekyll? Try these resources.
6
7 ### [Documentation](/docs/home/)
8
9 Start with our official guide to Jekyll covering installation, writing, customization, deployment, and more.
10
11 ### [Jekyll Talk](https://talk.jekyllrb.com/)
12
13 Our official Discourse forum. Here, users and contributors
14 can ask questions and discuss all aspects of Jekyll.
15 Also the place to [showcase your jekyll sites](https://talk.jekyllrb.com/t/jekyll-showcase-share-your-sites-built-with-jekyll/44/80) and [share your jekyll themes](https://talk.jekyllrb.com/t/jekyll-theme-showcase-share-your-jekyll-themes/1382/2).
16
17 ### [Jekyll on StackOverflow](https://stackoverflow.com/questions/tagged/jekyll)
18
19 StackOverflow is a staple of any developer's diet. Check out the Jekyll tag
20 on StackOverflow for an answer to your question. Not there? Ask a new
21 question!
22
23 ### [Jekyll IRC Channel](irc:irc.freenode.net/jekyll)
24
25 Get live support at **#jekyll** on **irc.freenode.net**, the official
26 Jekyll IRC channel.
27
28 ### View source
29
30 Learn from the source of others, you'll find plenty of [jekyll sites](https://github.com/topics/jekyll-site) and [jekyll themes](https://github.com/topics/jekyll-themes) carefully handcrafted on GitHub.
31
32 ### [Tutorials](/tutorials/home)
33
34 Similar to documentation, but more detailed scenario-based walk-throughs covering a variety of topics.
35
36 ### [Upgrading](/docs/upgrading/)
37
38 Did you recently upgrade from Jekyll 1 to 2 or from Jekyll 2 to 3?
39 Known breaking changes are listed in the upgrading docs.
40
41 ### [Jekyllconf](/jekyllconf/)
42
43 Watch videos from members of the Jekyll community speak about interesting use cases, tricks they've learned or meta Jekyll topics.
44
45 ### [Google](https://www.google.com/?q=jekyll)
46
47 Add **jekyll** to almost any query, and you'll find just what you need.
48
49 ### [jekyll/jekyll](https://github.com/jekyll/jekyll/issues)
50
51 Search through the issues on the main Jekyll development. Think you've
52 found a bug? File a new issue.
53
54 ### [@jekyllrb on Twitter](https://twitter.com/jekyllrb)
55
56 The official Jekyll Twitter account. It's not checked often, so try the
57 above first.
0 <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">
1 <title>Forestry Logotype</title>
2 <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"/>
3 <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"/>
4 <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"/>
5 <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"/>
6 <line x1="28.3" y1="101.9" x2="28.3" style="fill: none;stroke: #181919;stroke-miterlimit: 10;stroke-width: 8px"/>
7 <line x1="76.4" y1="101.9" x2="76.4" style="fill: none;stroke: #181919;stroke-miterlimit: 10;stroke-width: 8px"/>
8 <g>
9 <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"/>
10 <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)"/>
11 <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)"/>
12 <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"/>
13 <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)"/>
14 <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"/>
15 <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)"/>
16 <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"/>
17 </g>
18 </svg>
Binary diff not shown
Binary diff not shown
+0
-89
docs/index.html less more
0 ---
1 layout: default
2 overview: true
3 ---
4
5 <section class="intro">
6 <div class="grid">
7 <div class="unit whole center-on-mobiles">
8 <p class="first">Transform your plain text into static&nbsp;websites and&nbsp;blogs.</p>
9 </div>
10 </div>
11 </section>
12 <section class="features">
13 <div class="grid">
14 <div class="unit one-third">
15 <h2>Simple</h2>
16 <p>
17 No more databases, comment moderation, or pesky updates to install—just <em>your content</em>.
18 </p>
19 <a href="/docs/usage/">How Jekyll works &rarr;</a>
20 </div>
21 <div class="unit one-third">
22 <h2>Static</h2>
23 <p><a href="https://daringfireball.net/projects/markdown/">Markdown</a> (or <a href="http://redcloth.org/textile">Textile</a>), <a href="https://github.com/Shopify/liquid/wiki">Liquid</a>, HTML <span class="amp">&amp;</span> CSS go in. Static sites come out ready for deployment.</p>
24 <a href="/docs/templates/">Jekyll template guide &rarr;</a>
25 </div>
26 <div class="unit one-third">
27 <h2>Blog-aware</h2>
28 <p>
29 Permalinks, categories, pages, posts, and custom layouts are all first-class citizens here.
30 </p>
31 <a href="http://import.jekyllrb.com">Migrate your blog &rarr;</a>
32 </div>
33 <div class="clear"></div>
34 </div>
35 </section>
36 <section class="quickstart">
37 <div class="grid">
38 <div class="unit golden-small center-on-mobiles">
39 <h3>Get up and running <em>in&nbsp;seconds</em>.</h3>
40 </div>
41 <div class="unit golden-large code">
42 <p class="title">Quick-start Instructions</p>
43 <div class="shell">
44 <p class="line">
45 <span class="path">~</span>
46 <span class="prompt">$</span>
47 <span class="command">gem install jekyll bundler</span>
48 </p>
49 <p class="line">
50 <span class="path">~</span>
51 <span class="prompt">$</span>
52 <span class="command">jekyll new my-awesome-site</span>
53 </p>
54 <p class="line">
55 <span class="path">~</span>
56 <span class="prompt">$</span>
57 <span class="command">cd my-awesome-site</span>
58 </p>
59 <p class="line">
60 <span class="path">~/my-awesome-site</span>
61 <span class="prompt">$</span>
62 <span class="command">bundle exec jekyll serve</span>
63 </p>
64 <p class="line">
65 <span class="output"># => Now browse to http://localhost:4000</span>
66 </p>
67 </div>
68 </div>
69 <div class="clear"></div>
70 </div>
71 </section>
72 <section class="free-hosting">
73 <div class="grid">
74 <div class="unit whole">
75 <div class="grid pane">
76 <div class="unit whole center-on-mobiles">
77 <img src="img/octojekyll.png" width="300" height="251" alt="Free Jekyll hosting on GitHub Pages">
78 <div class="pane-content">
79 <h2 class="center-on-mobiles"><strong>Free hosting</strong> with GitHub Pages</h2>
80 <p>Sick of dealing with hosting companies? <a href="https://pages.github.com/">GitHub Pages</a> are <em>powered by Jekyll</em>, so you can easily deploy your site using GitHub for free&mdash;<a href="https://help.github.com/articles/about-supported-custom-domains/">custom domain name</a> and&nbsp;all.</p>
81 <a href="https://pages.github.com/">Learn more about GitHub Pages &rarr;</a>
82 </div>
83 </div>
84 <div class="clear"></div>
85 </div>
86 </div>
87 </div>
88 </section>
00 /**
1 * @preserve HTML5 Shiv 3.7.2 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed
1 * @preserve HTML5 Shiv 3.7.3 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed
22 */
3 !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.2",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)}(this,document);
3 !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);
+0
-10
docs/news/index.html less more
0 ---
1 layout: news
2 title: News
3 permalink: /news/
4 author: all
5 ---
6
7 {% for post in site.posts %}
8 {% include news_item.html %}
9 {% endfor %}
+0
-10
docs/news/releases/index.html less more
0 ---
1 layout: news
2 title: Releases
3 permalink: /news/releases/
4 author: all
5 ---
6
7 {% for post in site.categories.release %}
8 {% include news_item.html %}
9 {% endfor %}
0 ---
1 layout: error
2 permalink: /404.html
3 sitemap: false
4 ---
5
6 <section class="intro">
7 <div class="grid">
8 <div class="unit whole align-center">
9 <p class="first">Huh. It seems that page is<br/>Hyde-ing...</p>
10 </div>
11 </div>
12 </section>
13
14 <section class="error">
15 <div class="grid">
16 <div class="unit whole align-center">
17 <p>The resource you requested was not found. Here are some links to help you find your way:</p>
18 <nav class="main-nav">
19 <ul>
20 <li><a href="{{ '/' | relative_url }}">Home</a></li>
21 <li><a href="{{ '/docs/home/' | relative_url }}">Documentation</a></li>
22 <li><a href="{{ '/news/' | relative_url }}">News</a></li>
23 <li><a href="{{ '/help/' | relative_url }}">Help</a></li>
24 </ul>
25 </nav>
26 </div>
27 </div>
28 </section>
0 ---
1 layout: default
2 overview: true
3 permalink: /
4 ---
5
6 <section class="intro">
7 <div class="grid">
8 <div class="unit whole center-on-mobiles">
9 <p class="first">Transform your plain text into static&nbsp;websites and&nbsp;blogs.</p>
10 </div>
11 </div>
12 </section>
13 <section class="features center-on-mobiles">
14 <div class="grid">
15 <div class="unit one-third">
16 <h2>Simple</h2>
17 <p>
18 No more databases, comment moderation, or pesky updates to install—just <em>your content</em>.
19 </p>
20 <a href="{{ 'docs/usage/' | relative_url }}">How Jekyll works &rarr;</a>
21 </div>
22 <div class="unit one-third">
23 <h2>Static</h2>
24 <p><a href="https://daringfireball.net/projects/markdown/">Markdown</a>, <a href="https://github.com/Shopify/liquid/wiki">Liquid</a>, HTML <span class="amp">&amp;</span> CSS go in. Static sites come out ready for deployment.</p>
25 <a href="{{ 'docs/templates/' | relative_url }}">Jekyll template guide &rarr;</a>
26 </div>
27 <div class="unit one-third">
28 <h2>Blog-aware</h2>
29 <p>
30 Permalinks, categories, pages, posts, and custom layouts are all first-class citizens here.
31 </p>
32 <a href="https://import.jekyllrb.com">Migrate your blog &rarr;</a>
33 </div>
34 <div class="clear"></div>
35 </div>
36 </section>
37 <section class="quickstart">
38 <div class="grid">
39 <div class="unit golden-small center-on-mobiles">
40 <h3>Get up and running <em>in&nbsp;seconds</em>.</h3>
41 </div>
42 <div class="unit golden-large code">
43 <p class="title">Quick-start Instructions</p>
44 <div class="shell">
45 <p class="line">
46 <span class="path">~</span>
47 <span class="prompt">$</span>
48 <span class="command">gem install bundler jekyll</span>
49 </p>
50 <p class="line">
51 <span class="path">~</span>
52 <span class="prompt">$</span>
53 <span class="command">jekyll new my-awesome-site</span>
54 </p>
55 <p class="line">
56 <span class="path">~</span>
57 <span class="prompt">$</span>
58 <span class="command">cd my-awesome-site</span>
59 </p>
60 <p class="line">
61 <span class="path">~/my-awesome-site</span>
62 <span class="prompt">$</span>
63 <span class="command">bundle exec jekyll serve</span>
64 </p>
65 <p class="line">
66 <span class="output"># => Now browse to http://localhost:4000</span>
67 </p>
68 </div>
69 </div>
70 <div class="clear"></div>
71 </div>
72 </section>
73 <section class="free-hosting">
74 <div class="grid">
75 <div class="unit whole">
76 <div class="grid pane">
77 <div class="unit whole center-on-mobiles">
78 <img src="{{ 'img/octojekyll.png' | relative_url }}" width="300" height="251" alt="Free Jekyll hosting on GitHub Pages">
79 <div class="pane-content">
80 <h2 class="center-on-mobiles"><strong>Free hosting</strong> with GitHub Pages</h2>
81 <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&mdash;<a href="https://help.github.com/articles/about-supported-custom-domains/">custom domain name</a> and&nbsp;all.</p>
82 <a href="https://pages.github.com/">Learn more about GitHub Pages &rarr;</a>
83 </div>
84 </div>
85 <div class="clear"></div>
86 </div>
87 </div>
88 </div>
89 </section>
0 ---
1 layout: page
2 title: JekyllConf
3 permalink: /jekyllconf/
4 ---
5
6 [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.
7
8 ## Featured
9
10 {% assign random = site.time | date: "%s%N" | modulo: site.data.jekyllconf-talks.size %}
11 {% assign featured = site.data.jekyllconf-talks[random] %}
12
13 **{{ featured.topic }}** - [*{{ featured.speaker }}*](https://twitter.com/{{ featured.twitter_handle }})
14 <div class="videoWrapper">
15 <iframe width="420" height="315" src="https://www.youtube.com/embed/{{ featured.youtube_id }}" frameborder="0" allowfullscreen></iframe>
16 </div>
17
18 {% assign talks = site.data.jekyllconf-talks | group_by: 'year' %}
19 {% for year in talks reversed %}
20 ## {{ year.name }}
21 {% for talk in year.items %}
22 * [**{{ talk.topic }}**](https://youtu.be/{{ talk.youtube_id }}) - [*{{ talk.speaker }}*](https://twitter.com/{{ talk.twitter_handle }})
23 {% endfor %}
24 {% endfor %}
0 ---
1 layout: news
2 title: News
3 permalink: /news/
4 author: all
5 ---
6
7 {% for post in site.posts -%}
8 {% if forloop.index == 1 -%}
9 {% include news_item.html -%}
10 {% else -%}
11 {% include news_item_archive.html -%}
12 {% endif -%}
13 {% endfor -%}
0 ---
1 layout: page
2 title: Philosophy
3 permalink: /philosophy/
4 ---
5
6 Jekyll offers a unique philosophy when approaching the problem of static
7 site generation. This core philosophy drives development and product
8 decisions. When a contributor, maintainer, or user asks herself what Jekyll
9 is about, the following principles should come to mind:
10
11 ### 1. No Magic
12
13 Jekyll is not magic. A user should be able to understand the underlying
14 processes that make up the Jekyll build without much reading. It should
15 do only what you ask it to and nothing more. When a user takes a certain
16 action, the outcome should be easily understandable and focused.
17
18 ### 2. It "Just Works"
19
20 The out-of-the-box experience should be that it "just works." Run
21 `gem install jekyll` and it should build any Jekyll site that it's given.
22 Features like auto-regeneration and settings like the markdown renderer
23 should represent sane defaults that work perfectly for the vast majority of
24 cases. The burden of initial configuration should not be placed on the user.
25
26 ### 3. Content is King
27
28 Why is Jekyll so loved by content creators? It focuses on content first and
29 foremost, making the process of publishing content on the Web easy. Users
30 should find the management of their content enjoyable and simple.
31
32 ### 4. Stability
33
34 If a user's site builds today, it should build tomorrow.
35 Backwards-compatibility should be strongly preferred over breaking changes.
36 Breaking changes should be made to support a strong practical goal, and
37 breaking changes should never be made to drive forward "purity" of the
38 codebase, or other changes purely to make the maintainers' lives easier.
39 Breaking changes provide a significant amount of friction between upgrades
40 and reduce the confidence of users in this software, and should thus be
41 avoided unless absolutely necessary.
42 Upon breaking changes, provide a clear path for users to upgrade.
43
44 ### 5. Small & Extensible
45
46 The core of Jekyll should be simple and small, and extensibility should be
47 a first-class feature to provide added functionality from community
48 contributors. The core should be kept to features used by at least 90% of
49 users–everything else should be provided as a plugin. New features should
50 be shipped as plugins and focus should be put on creating extensible core
51 API's to support rich plugins.
0 ---
1 permalink: /github.html
2 redirect_to: https://github.com/jekyll/jekyll
3 ---
0 ---
1 permalink: /issues.html
2 redirect_to: https://github.com/jekyll/jekyll/issues
3 ---
0 ---
1 layout: news
2 title: Releases
3 permalink: /news/releases/
4 author: all
5 ---
6
7 {% for post in site.categories.release -%}
8 {% if forloop.index == 1 -%}
9 {% include news_item.html -%}
10 {% else -%}
11 {% include news_item_archive.html -%}
12 {% endif -%}
13 {% endfor -%}
0 ---
1 layout: page
2 title: Resources
3 permalink: /resources/
4 redirect_from:
5 - /docs/resources/
6 ---
7 Jekyll's growing community produces wide variety of themes, plugins, tutorials
8 and other resources that can be helpful. Below is a collection of links to
9 some of the most popular Jekyll resources.
10
11 ## Themes
12 - [GitHub.com #jekyll-theme repos](https://github.com/topics/jekyll-theme)
13 - [jamstackthemes.dev](https://jamstackthemes.dev/ssg/jekyll/)
14 - [jekyllthemes.org](http://jekyllthemes.org/)
15 - [jekyllthemes.io](https://jekyllthemes.io/)
16
17 See also: [docs/themes](/docs/themes/).
18
19 ## Plugins
20 - [jekyll-plugin topic on GitHub](https://github.com/topics/jekyll-plugin)
21 - [Planet Jekyll](https://github.com/planetjekyll/awesome-jekyll-plugins)
22
23 ## Guides
24
25 - [Community tutorials]({{ '/tutorials/home/' | relative_url }})
26 - [Deploy Jekyll 4 on GitHub Pages]({{ '/docs/continuous-integration/github-actions/' | relative_url }})
27 - [Deploy Jekyll on Vercel](https://github.com/vercel/vercel/tree/master/examples/jekyll)
28 - [Deploy Jekyll 4 on Netlify](https://www.netlify.com/blog/2020/04/02/a-step-by-step-guide-jekyll-4.0-on-netlify/)
29 - [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.
30 - [Jekyll Cheatsheet](https://learn.cloudcannon.com/jekyll-cheat-sheet/) is a single-page resource for Jekyll filters, variables, and the like.
31
32 ## Integrations
33
34 Use a SaaS service as a backend for functionality on your Jekyll site
35
36 ### Comments
37 - [Staticman](https://staticman.net): Add user-generated content to a Jekyll site (free and open source)
38 - [Talkyard](https://www.talkyard.io/blog-comments): Embedded comments for Jekyll and others (free and open source, or hosted serverless)
39
40 ### Content Management
41 - [CloudCannon](https://cloudcannon.com/): The Cloud CMS for Jekyll
42 - [Contentful](https://github.com/contentful/jekyll-contentful-data-import): Content infrastructure for digital teams
43 - [Forestry.io](https://forestry.io/): A free Git-based responsive CMS, with content modeling and instant previews.
44 - [Netlify CMS](https://www.netlifycms.org/): Open source content management for your Git workflow
45 - [Siteleaf](https://www.siteleaf.com/): Built for developers, Loved by everyone
46 - [Kentico Kontent](https://github.com/Kentico/kontent-jekyll): A headless CMS with full control over content presentation
47
48 ### E-commerce
49 - [MemberSpace](https://www.memberspace.com/integrations/jekyll-membership/): Add memberships and paywall functionality to a Jekyll site
50 - [Snipcart](https://snipcart.com/blog/static-site-e-commerce-part-2-integrating-snipcart-with-jekyll): Add a shopping cart to a Jekyll site
51
52 ### Forms
53 - [Arengu](https://www.arengu.com)
54 - [Getform](https://getform.io)
55 - [99Inbound](https://www.99inbound.com)
56 - [Formcake](https://formcake.com)
57 - [Formcarry](https://formcarry.com)
58 - [Formingo](https://www.formingo.co/guides/jekyll?utm_source=github&utm_medium=jekyll-docs&utm_campaign=Jekyll%20Documentation)
59 - [FormKeep](https://formkeep.com/guides/contact-form-jekyll?utm_source=github&utm_medium=jekyll-docs&utm_campaign=contact-form-jekyll)
60 - [Formspark](https://formspark.io/)
61 - [Formspree (open source)](https://formspree.io/)
62 - [formX](https://formx.stream)
63 - [Simple Form](https://getsimpleform.com/)
64 - [SmartForms](https://smartforms.dev/)
65 - [Typeform](https://www.typeform.com/templates/c/forms/)
66
67 ### Search
68 - [Algolia](https://blog.algolia.com/instant-search-blog-documentation-jekyll-plugin/): Add a powerful instant search to your Jekyll site
69 - [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
70 - [Bonsai Search](https://docs.bonsai.io/article/217-jekyll): The easiest way to use Elasticsearch for your Jekyll site
71 - [CloudSh](https://cloudsh.com/generators/how-to-setup-search-on-jekyll/): Website search with a few lines of JavaScript
72
73 ## Editors plugins
74
75 - 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).
76 - [jekyll-atom](https://atom.io/packages/jekyll): A collection of snippets and tools for Jekyll in Atom
77 - [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.
78 - [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).
79 - [vim-jekyll](https://github.com/parkr/vim-jekyll): A vim plugin to generate new posts and run `jekyll build` all without leaving vim.
80 - [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.
81
82 ## Posts
83
84 - [How I'm using Jekyll in 2016](https://mademistakes.com/articles/using-jekyll-2016/)
85 - [Talkyard comments instructions for Jekyll](https://jekyll-demo.talkyard.io/2018/01/09/installation-instructions.html)
86 - [Static Comments with Jekyll & Staticman](https://mademistakes.com/articles/improving-jekyll-static-comments/)
87 - [Adding Ajax pagination to Jekyll](https://eduardoboucas.com/blog/2014/11/05/adding-ajax-pagination-to-jekyll.html)
88 - ['About this Website', by Carter Allen](http://cartera.me/2010/08/12/about-this-website/)
89
90 > "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."
91
92 - 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.
93 - [Using your Rails layouts in Jekyll](https://numbers.brighterplanet.com/2010/08/09/sharing-rails-views-with-jekyll)
94
95 ## Forks
96
97 - [Time to Visit Bridgetown](https://www.bridgetownrb.com/news/time-to-visit-bridgetown/)
98 - [Creating a Faster Jekyll](https://sigpipe.macromates.com/2018/creating-a-faster-jekyll/)
0 ---
1 layout: page
2 title: Showcase
3 permalink: /showcase/
4 redirect_from:
5 - /docs/sites/
6 ---
7
8 <p>Jekyll powers many company websites, here a few nice ones:</p>
9
10 <ul class="showcase" id="showcase">
11 {% for entry in site.data.showcase reversed -%}
12 <li>
13 <a href="{{ entry.url }}" target="_blank">
14 <figure>
15 <img loading="lazy" src="{{ site.cloudinary_url }}/showcase/{{ entry.image }}" alt="{{ entry.name }}" width="404" height="253">
16 <figcaption>{{ entry.name }}</figcaption>
17 </figure>
18 </a>
19 </li>
20 {% endfor -%}
21 <li class="spacer"></li>
22 </ul>
0 ---
1 layout: page
2 title: The Jekyll Team
3 permalink: /team/
4 ---
5
6 ## Core Team
7
8 _The Jekyll Core Team's responsibility is to ensure the development and
9 community around the Jekyll ecosystem thrive._
10
11 * Ashwin (@ashmaroli)
12 * Matt (@mattr-)
13
14 ## Security Team
15
16 _The Jekyll Security Team's responsibility is to triage, validate, and
17 patch security vulnerabilities reported to them._
18
19 * Parker (@parkr)
20 * Ashwin (@ashmaroli)
21 * Matt (@mattr-)
22
23 ## Emeritus Core Team Members
24
25 _Emeritus Core Team Members were once members of Jekyll's Core Team._
26
27 * Alfred (@alfredxing)
28 * Frank (@DirtyF)
29 * Nick (@qrush)
30 * Parker (@parkr)
31 * Tom (@mojombo)
+0
-50
docs/philosophy.md less more
0 ---
1 title: Philosophy
2 ---
3
4 Jekyll offers a unique philosophy when approaching the problem of static
5 site generation. This core philosophy drives development and product
6 decisions. When a contributor, maintainer, or user asks herself what Jekyll
7 is about, the following principles should come to mind:
8
9 ### 1. No Magic
10
11 Jekyll is not magic. A user should be able to understand the underlying
12 processes that make up the Jekyll build without much reading. It should
13 do only what you ask it to and nothing more. When a user takes a certain
14 action, the outcome should be easily understandable and focused.
15
16 ### 2. It "Just Works"
17
18 The out-of-the-box experience should be that it "just works." Run
19 `gem install jekyll` and it should build any Jekyll site that it's given.
20 Features like auto-regeneration and settings like the markdown renderer
21 should represent sane defaults that work perfectly for the vast majority of
22 cases. The burden of initial configuration should not be placed on the user.
23
24 ### 3. Content is King
25
26 Why is Jekyll so loved by content creators? It focuses on content first and
27 foremost, making the process of publishing content on the Web easy. Users
28 should find the management of their content enjoyable and simple.
29
30 ### 4. Stability
31
32 If a user's site builds today, it should build tomorrow.
33 Backwards-compatibility should be strongly preferred over breaking changes.
34 Breaking changes should be made to support a strong practical goal, and
35 breaking changes should never be made to drive forward "purity" of the
36 codebase, or other changes purely to make the maintainers' lives easier.
37 Breaking changes provide a significant amount of friction between upgrades
38 and reduce the confidence of users in this software, and should thus be
39 avoided unless absolutely necessary.
40 Upon breaking changes, provide a clear path for users to upgrade.
41
42 ### 5. Small & Extensible
43
44 The core of Jekyll should be simple and small, and extensibility should be
45 a first-class feature to provide added functionality from community
46 contributors. The core should be kept to features used by at least 90% of
47 users–everything else should be provided as a plugin. New features should
48 be shipped as plugins and focus should be put on creating extensible core
49 API's to support rich plugins.
+0
-4
docs/redirects/github.html less more
0 ---
1 permalink: /github.html
2 redirect_to: https://github.com/jekyll/jekyll
3 ---
+0
-4
docs/redirects/issues.html less more
0 ---
1 permalink: /issues.html
2 redirect_to: https://github.com/jekyll/jekyll/issues
3 ---
+0
-47
docs/team/index.md less more
0 ---
1 layout: page
2 title: The Jekyll Team
3 ---
4
5 ## Core Team
6
7 *The Jekyll Core Team's responsibility is to ensure the development and
8 community around the Jekyll ecosystem thrive.*
9
10 1. Olivia (@oe, Lead Developer)
11 2. Frank (@DirtyF, Documentation)
12 3. Pat (@pathawks)
13 4. Matt (@mattr-)
14
15 ## Plugin Core
16
17 *The Jekyll Plugin Core Team's responsibility is to ensure the development and
18 community around the core plugins thrive. They also provide guidance in
19 conversations about extensibility of Core Jekyll.*
20
21 1. Ashwin (@ashmaroli)
22 2. Florian (@Crunch09)
23 3. Mert (@mertkahyaoglu, jekyll-admin)
24 4. Alfred Xing (@alfredxing)
25
26 ## Affinity Team Captains
27
28 *The Affinity Team Captains lead [Jekyll's Affinity
29 Teams](https://teams.jekyllrb.com/). Each team is tasked with maintaining
30 and addressing issues for a specific aspect of Jekyll.*
31
32 1. [Build](https://github.com/orgs/jekyll/teams/build): @mattr-
33 2. [Documentation](https://github.com/orgs/jekyll/teams/documentation): @DirtyF, @mattr-
34 3. [Ecosystem](https://github.com/orgs/jekyll/teams/ecosystem): @pathawks
35 4. [Performance](https://github.com/orgs/jekyll/teams/performance): @mattr-, @parkr
36 5. [Stability](https://github.com/orgs/jekyll/teams/stability): @oe, @parkr
37 6. [Windows](https://github.com/orgs/jekyll/teams/windows): @XhmikosR
38
39 ## Emeritus Core Team Members
40
41 *Emeritus Core Team Members were once members of Jekyll's Core Team.*
42
43 1. Parker (@parkr)
44 2. Tom (@mojombo)
45 3. Nick (@qrush)
46 4. Alfred (@alfredxing)
0 Feature: Cache
1 As a developer who likes to create plugins
2 I want to be able to cache certain aspects across multiple builds
3 And retrieve the cached aspects when needed
4
5 Scenario: Default Cache directory
6 Given I have an "index.md" page that contains "{{ site.title }}"
7 And I have a configuration file with "title" set to "Hello World"
8 When I run jekyll build
9 Then I should get a zero exit status
10 And the .jekyll-cache directory should exist
11 And the .jekyll-cache/Jekyll/Cache/Jekyll--Cache directory should exist
12 And the _site directory should exist
13 And I should see "<p>Hello World</p>" in "_site/index.html"
14
15 Scenario: Custom Cache directory
16 Given I have an "index.md" page that contains "{{ site.title }}"
17 And I have a configuration file with:
18 | key | value |
19 | title | Hello World |
20 | cache_dir | .foo-cache |
21 When I run jekyll build
22 Then I should get a zero exit status
23 And the .foo-cache directory should exist
24 And the .foo-cache/Jekyll/Cache/Jekyll--Cache directory should exist
25 But the .jekyll-cache directory should not exist
26 And the _site directory should exist
27 And I should see "<p>Hello World</p>" in "_site/index.html"
28
29 Scenario: Disk usage in safe mode
30 Given I have an "index.md" page that contains "{{ site.title }}"
31 And I have a configuration file with "title" set to "Hello World"
32 When I run jekyll build --safe
33 Then I should get a zero exit status
34 But the .jekyll-cache directory should not exist
35 And the _site directory should exist
36 And I should see "<p>Hello World</p>" in "_site/index.html"
37
38 Scenario: Disabling disk usage in non-safe mode
39 Given I have an "index.md" page that contains "{{ site.title }}"
40 And I have a configuration file with "title" set to "Hello World"
41 When I run jekyll build --disable-disk-cache
42 Then I should get a zero exit status
43 And the _site directory should exist
44 And I should see "<p>Hello World</p>" in "_site/index.html"
45 But the .jekyll-cache directory should not exist
55 Scenario: Unrendered collection
66 Given I have an "index.html" page that contains "Collections: {{ site.methods }}"
77 And I have fixture collections
8 And I have a "_methods/static-file.txt" file that contains "Static Content {{ site.title }}"
89 And I have a configuration file with "collections" set to "['methods']"
910 When I run jekyll build
1011 Then I should get a zero exit status
1112 And the _site directory should exist
13 But the _site/methods directory should not exist
1214 And the "_site/methods/configuration.html" file should not exist
15 And the "_site/methods/static-file.txt" file should not exist
1316
1417 Scenario: Rendered collection
1518 Given I have an "index.html" page that contains "Collections: output => {{ site.collections[0].output }} label => {{ site.collections[0].label }}"
1619 And I have an "collection_metadata.html" page that contains "Methods metadata: {{ site.collections[0].foo }} {{ site.collections[0] }}"
1720 And I have fixture collections
21 And I have a "_methods/static-file.txt" file that contains "Static Content {{ site.title }}"
1822 And I have a "_config.yml" file with content:
1923 """
2024 collections:
2933 And I should see "label => methods" in "_site/index.html"
3034 And I should see "Methods metadata: bar" in "_site/collection_metadata.html"
3135 And I should see "<p>Whatever: foo.bar</p>" in "_site/methods/configuration.html"
36 And I should see "Static Content {{ site.title }}" in "_site/methods/static-file.txt"
3237
3338 Scenario: Rendered collection at a custom URL
3439 Given I have an "index.html" page that contains "Collections: {{ site.collections }}"
7681 When I run jekyll build
7782 Then I should get a zero exit status
7883 Then the _site directory should exist
79 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/um_hi.md" in "_site/index.html" unless Windows
80 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/yaml_with_dots.md" in "_site/index.html" if on Windows
84 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
85 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
8186
8287 Scenario: Collections specified as an hash
8388 Given I have an "index.html" page that contains "Collections: {% for method in site.methods %}{{ method.relative_path }} {% endfor %}"
9095 When I run jekyll build
9196 Then I should get a zero exit status
9297 Then the _site directory should exist
93 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/um_hi.md" in "_site/index.html" unless Windows
94 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/yaml_with_dots.md" in "_site/index.html" if on Windows
98 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
99 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
95100
96101 Scenario: Rendered collection with document with future date
97102 Given I have a _puppies directory
371376 When I run jekyll build
372377 Then I should get a zero exit status
373378 Then the _site directory should exist
374 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/um_hi.md" in "_site/index.html" unless Windows
375 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/yaml_with_dots.md" in "_site/index.html" if on Windows
379 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
380 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
376381
377382 Scenario: Documents have an output attribute, which is the converted HTML
378383 Given I have an "index.html" page that contains "Second document's output: {{ site.documents[2].output }}"
397402 collections:
398403 - methods
399404 """
400 And I'm using kramdown v2
401405 When I run jekyll build
402406 Then I should get a zero exit status
403407 Then the _site directory should exist
417421 And I should see "Item count: 2" in "_site/index.html"
418422
419423 Scenario: Sort by title
420 Given I have an "index.html" page that contains "{% assign items = site.methods | sort: 'title' %}2. of {{ items.size }}: {{ items[1].output }}"
421 And I have fixture collections
422 And I have a "_config.yml" file with content:
423 """
424 collections:
425 - methods
426 """
427 When I run jekyll build
428 Then I should get a zero exit status
429 And the _site directory should exist
430 And I should see "2. of 9: <p>Page without title.</p>" in "_site/index.html" unless Windows
431 And I should see "2. of 8: <p>Page without title.</p>" in "_site/index.html" if on Windows
424 Given I have an "index.html" page that contains "{% assign items = site.methods | sort: 'title' %}2. of {{ items.size }}: {{ items[2].output }}"
425 And I have fixture collections
426 And I have a "_config.yml" file with content:
427 """
428 collections:
429 - methods
430 """
431 When I run jekyll build
432 Then I should get a zero exit status
433 And the _site directory should exist
434 And I should see "2. of 10: <p>Page without title.</p>" in "_site/index.html" unless Windows
435 And I should see "2. of 9: <p>Page without title.</p>" in "_site/index.html" if on Windows
432436
433437 Scenario: Sort by relative_path
434438 Given I have an "index.html" page that contains "Collections: {% assign methods = site.methods | sort: 'relative_path' %}{{ methods | map:"title" | join: ", " }}"
441445 When I run jekyll build
442446 Then I should get a zero exit status
443447 Then the _site directory should exist
444 And I should see "Collections: this is a test!, Collection#entries, Jekyll.configuration, Jekyll.escape, Jekyll.sanitized_path, Site#generate, Initialize, Site#generate, YAML with Dots" in "_site/index.html" unless Windows
445 And I should see "Collections: this is a test!, Collection#entries, Jekyll.configuration, Jekyll.escape, Jekyll.sanitized_path, Site#generate, Initialize, YAML with Dots" in "_site/index.html" if on Windows
448 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
449 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
450
451 Scenario: Sort all entries by a Front Matter key defined in all entries
452 Given I have an "index.html" page that contains "Collections: {{ site.tutorials | map: 'title' | join: ', ' }}"
453 And I have fixture collections
454 And I have a _layouts directory
455 And I have a "_layouts/tutorial.html" file with content:
456 """
457 {% if page.previous %}Previous: {{ page.previous.title }}{% endif %}
458
459 {% if page.next %}Next: {{ page.next.title }}{% endif %}
460 """
461 And I have a "_config.yml" file with content:
462 """
463 collections:
464 tutorials:
465 output: true
466 sort_by: lesson
467
468 defaults:
469 - scope:
470 path: ""
471 type: tutorials
472 values:
473 layout: tutorial
474
475 """
476 When I run jekyll build
477 Then I should get a zero exit status
478 Then the _site directory should exist
479 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"
480 And I should not see "Previous: Graduation Day" in "_site/tutorials/lets-roll.html"
481 And I should not see "Next: Tip of the Iceberg" in "_site/tutorials/lets-roll.html"
482 But I should see "Previous: Getting Started" in "_site/tutorials/lets-roll.html"
483 And I should see "Next: Dive-In and Publish Already!" in "_site/tutorials/lets-roll.html"
484
485 Scenario: Sort all entries by a Front Matter key defined in only some entries
486 Given I have an "index.html" page that contains "Collections: {{ site.tutorials | map: 'title' | join: ', ' }}"
487 And I have fixture collections
488 And I have a _layouts directory
489 And I have a "_layouts/tutorial.html" file with content:
490 """
491 {% if page.previous %}Previous: {{ page.previous.title }}{% endif %}
492
493 {% if page.next %}Next: {{ page.next.title }}{% endif %}
494 """
495 And I have a "_config.yml" file with content:
496 """
497 collections:
498 tutorials:
499 output: true
500 sort_by: approx_time
501
502 defaults:
503 - scope:
504 path: ""
505 type: tutorials
506 values:
507 layout: tutorial
508
509 """
510 When I run jekyll build
511 Then I should get a zero exit status
512 Then the _site directory should exist
513 And I should see "'approx_time' not defined" in the build output
514 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"
515 And I should see "Previous: Getting Started" in "_site/tutorials/graduation-day.html"
516 And I should see "Next: Dive-In and Publish Already!" in "_site/tutorials/graduation-day.html"
517
518 Scenario: Manually sort entries
519 Given I have an "index.html" page that contains "Collections: {{ site.tutorials | map: 'title' | join: ', ' }}"
520 And I have fixture collections
521 And I have a _layouts directory
522 And I have a "_layouts/tutorial.html" file with content:
523 """
524 {% if page.previous %}Previous: {{ page.previous.title }}{% endif %}
525
526 {% if page.next %}Next: {{ page.next.title }}{% endif %}
527 """
528 And I have a "_config.yml" file with content:
529 """
530 collections:
531 tutorials:
532 output: true
533 order:
534 - getting-started.md
535 - tip-of-the-iceberg.md
536 - lets-roll.md
537 - dive-in-and-publish-already.md
538 - graduation-day.md
539 - random-plugins.md
540
541 defaults:
542 - scope:
543 path: ""
544 type: tutorials
545 values:
546 layout: tutorial
547
548 """
549 When I run jekyll build
550 Then I should get a zero exit status
551 Then the _site directory should exist
552 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"
553 And I should not see "Previous: Graduation Day" in "_site/tutorials/lets-roll.html"
554 And I should not see "Next: Tip of the Iceberg" in "_site/tutorials/lets-roll.html"
555 But I should see "Previous: Tip of the Iceberg" in "_site/tutorials/lets-roll.html"
556 And I should see "Next: Dive-In and Publish Already!" in "_site/tutorials/lets-roll.html"
557
558 Scenario: Manually sort some entries
559 Given I have an "index.html" page that contains "Collections: {{ site.tutorials | map: 'title' | join: ', ' }}"
560 And I have fixture collections
561 And I have a _layouts directory
562 And I have a "_layouts/tutorial.html" file with content:
563 """
564 {% if page.previous %}Previous: {{ page.previous.title }}{% endif %}
565
566 {% if page.next %}Next: {{ page.next.title }}{% endif %}
567 """
568 And I have a "_config.yml" file with content:
569 """
570 collections:
571 tutorials:
572 output: true
573 order:
574 - getting-started.md
575 - lets-roll.md
576 - dive-in-and-publish-already.md
577 - graduation-day.md
578
579 defaults:
580 - scope:
581 path: ""
582 type: tutorials
583 values:
584 layout: tutorial
585
586 """
587 When I run jekyll build
588 Then I should get a zero exit status
589 Then the _site directory should exist
590 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"
591 And I should not see "Previous: Graduation Day" in "_site/tutorials/lets-roll.html"
592 And I should not see "Previous: Tip of the Iceberg" in "_site/tutorials/lets-roll.html"
593 And I should not see "Next: Tip of the Iceberg" in "_site/tutorials/lets-roll.html"
594 But I should see "Previous: Getting Started" in "_site/tutorials/lets-roll.html"
595 And I should see "Next: Dive-In and Publish Already!" in "_site/tutorials/lets-roll.html"
446596
447597 Scenario: Rendered collection with date/dateless filename
448598 Given I have an "index.html" page that contains "Collections: {% for method in site.thanksgiving %}{{ method.title }} {% endfor %}"
459609 And I should see "Thanksgiving Black Friday" in "_site/index.html"
460610 And I should see "Happy Thanksgiving" in "_site/thanksgiving/2015-11-26-thanksgiving.html"
461611 And I should see "Black Friday" in "_site/thanksgiving/black-friday.html"
612
613 Scenario: Rendered collection with custom permalinks and static file contents
614 Given I have fixture collections
615 And I have a "_config.yml" file with content:
616 """
617 collections:
618 methods:
619 output: true
620 permalink: /:collection/:name
621 """
622 When I run jekyll build
623 Then I should get a zero exit status
624 And the _site directory should exist
625 And I should see "I have no front matter." in "_site/methods/extensionless_static_file"
626
627 Scenario: Rendered collection with an extensionless document
628 Given I have fixture collections
629 And I have a "_config.yml" file with content:
630 """
631 collections:
632 methods:
633 output: true
634 """
635 When I run jekyll build
636 Then I should get a zero exit status
637 And the _site directory should exist
638 And I should see "I have no file extension but I should still be a part of the collection." in "_site/methods/collection/entries"
639
640 Scenario: Rendered collection with an extensionless document in a strict site
641 Given I have fixture collections
642 And I have a _posts directory
643 And I have an "_posts/2019-12-26-extensioned.md" file that contains "Hello!"
644 And I have an "_posts/2019-12-26-extensionless" file that contains "Aloha!"
645 And I have an "index.md" page that contains "{{ site.posts | map: 'title' }}"
646 And I have a "_config.yml" file with content:
647 """
648 strict_front_matter: true
649 collections:
650 methods:
651 output: true
652 """
653 When I run jekyll build
654 Then I should get a zero exit status
655 And the _site directory should exist
656 And I should see "I have no file extension but I should still be a part of the collection." in "_site/methods/collection/entries"
657 And I should see "Extensioned" in "_site/index.html"
658 But I should not see "Extensionless" in "_site/index.html"
282282 And I should see "<p>Loki: Manager: false</p>" in "_site/index.html"
283283 And I should see "<p>Loki: Recruit: false</p>" in "_site/index.html"
284284 And I should see "<p>Loki: Villain: false</p>" in "_site/index.html"
285
286 Scenario: Sort all entries by a Front Matter key defined in all entries
287 Given I have an "index.html" page that contains "Collections: {{ site.tutorials | map: 'title' | join: ', ' }}"
288 And I have fixture collections in "gathering" directory
289 And I have a _layouts directory
290 And I have a "_layouts/tutorial.html" file with content:
291 """
292 {% if page.previous %}Previous: {{ page.previous.title }}{% endif %}
293
294 {% if page.next %}Next: {{ page.next.title }}{% endif %}
295 """
296 And I have a "_config.yml" file with content:
297 """
298 collections_dir: gathering
299 collections:
300 tutorials:
301 output: true
302 sort_by: lesson
303
304 defaults:
305 - scope:
306 path: ""
307 type: tutorials
308 values:
309 layout: tutorial
310
311 """
312 When I run jekyll build
313 Then I should get a zero exit status
314 Then the _site directory should exist
315 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"
316 And I should not see "Previous: Graduation Day" in "_site/tutorials/lets-roll.html"
317 And I should not see "Next: Tip of the Iceberg" in "_site/tutorials/lets-roll.html"
318 But I should see "Previous: Getting Started" in "_site/tutorials/lets-roll.html"
319 And I should see "Next: Dive-In and Publish Already!" in "_site/tutorials/lets-roll.html"
320
321 Scenario: Sort all entries by a Front Matter key defined in only some entries
322 Given I have an "index.html" page that contains "Collections: {{ site.tutorials | map: 'title' | join: ', ' }}"
323 And I have fixture collections in "gathering" directory
324 And I have a _layouts directory
325 And I have a "_layouts/tutorial.html" file with content:
326 """
327 {% if page.previous %}Previous: {{ page.previous.title }}{% endif %}
328
329 {% if page.next %}Next: {{ page.next.title }}{% endif %}
330 """
331 And I have a "_config.yml" file with content:
332 """
333 collections_dir: gathering
334 collections:
335 tutorials:
336 output: true
337 sort_by: approx_time
338
339 defaults:
340 - scope:
341 path: ""
342 type: tutorials
343 values:
344 layout: tutorial
345
346 """
347 When I run jekyll build
348 Then I should get a zero exit status
349 Then the _site directory should exist
350 And I should see "'approx_time' not defined" in the build output
351 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"
352 And I should see "Previous: Getting Started" in "_site/tutorials/graduation-day.html"
353 And I should see "Next: Dive-In and Publish Already!" in "_site/tutorials/graduation-day.html"
354
355 Scenario: Manually sort entries
356 Given I have an "index.html" page that contains "Collections: {{ site.tutorials | map: 'title' | join: ', ' }}"
357 And I have fixture collections in "gathering" directory
358 And I have a _layouts directory
359 And I have a "_layouts/tutorial.html" file with content:
360 """
361 {% if page.previous %}Previous: {{ page.previous.title }}{% endif %}
362
363 {% if page.next %}Next: {{ page.next.title }}{% endif %}
364 """
365 And I have a "_config.yml" file with content:
366 """
367 collections_dir: gathering
368 collections:
369 tutorials:
370 output: true
371 order:
372 - getting-started.md
373 - tip-of-the-iceberg.md
374 - lets-roll.md
375 - dive-in-and-publish-already.md
376 - graduation-day.md
377 - random-plugins.md
378
379 defaults:
380 - scope:
381 path: ""
382 type: tutorials
383 values:
384 layout: tutorial
385
386 """
387 When I run jekyll build
388 Then I should get a zero exit status
389 Then the _site directory should exist
390 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"
391 And I should not see "Previous: Graduation Day" in "_site/tutorials/lets-roll.html"
392 And I should not see "Next: Tip of the Iceberg" in "_site/tutorials/lets-roll.html"
393 But I should see "Previous: Tip of the Iceberg" in "_site/tutorials/lets-roll.html"
394 And I should see "Next: Dive-In and Publish Already!" in "_site/tutorials/lets-roll.html"
395
396 Scenario: Manually sort some entries
397 Given I have an "index.html" page that contains "Collections: {{ site.tutorials | map: 'title' | join: ', ' }}"
398 And I have fixture collections in "gathering" directory
399 And I have a _layouts directory
400 And I have a "_layouts/tutorial.html" file with content:
401 """
402 {% if page.previous %}Previous: {{ page.previous.title }}{% endif %}
403
404 {% if page.next %}Next: {{ page.next.title }}{% endif %}
405 """
406 And I have a "_config.yml" file with content:
407 """
408 collections_dir: gathering
409 collections:
410 tutorials:
411 output: true
412 order:
413 - getting-started.md
414 - lets-roll.md
415 - dive-in-and-publish-already.md
416 - graduation-day.md
417
418 defaults:
419 - scope:
420 path: ""
421 type: tutorials
422 values:
423 layout: tutorial
424
425 """
426 When I run jekyll build
427 Then I should get a zero exit status
428 Then the _site directory should exist
429 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"
430 And I should not see "Previous: Graduation Day" in "_site/tutorials/lets-roll.html"
431 And I should not see "Previous: Tip of the Iceberg" in "_site/tutorials/lets-roll.html"
432 And I should not see "Next: Tip of the Iceberg" in "_site/tutorials/lets-roll.html"
433 But I should see "Previous: Getting Started" in "_site/tutorials/lets-roll.html"
434 And I should see "Next: Dive-In and Publish Already!" in "_site/tutorials/lets-roll.html"
55 Scenario: Blank site
66 Given I do not have a "test_blank" directory
77 When I run jekyll new test_blank --blank
8 Then the test_blank/_layouts directory should exist
8 Then the test_blank/_data directory should exist
9 And the test_blank/_drafts directory should exist
10 And the test_blank/_includes directory should exist
11 And the test_blank/_layouts directory should exist
912 And the test_blank/_posts directory should exist
10 And the "test_blank/index.html" file should exist
13 And the test_blank/_sass directory should exist
14 And the test_blank/assets/css directory should exist
15 And the "test_blank/_layouts/default.html" file should exist
16 And the "test_blank/_sass/main.scss" file should exist
17 And the "test_blank/assets/css/main.scss" file should exist
18 And the "test_blank/_config.yml" file should exist
19 And the "test_blank/index.md" file should exist
1120
1221 Scenario: Basic site
1322 Given I have an "index.html" file that contains "Basic Site"
174183 Given I have a _posts directory
175184 And I have the following post:
176185 | title | date | layout | content |
177 | entry1 | 2020-12-31 | post | content for entry1. |
186 | entry1 | 2040-12-31 | post | content for entry1. |
178187 | entry2 | 2007-12-31 | post | content for entry2. |
179188 When I run jekyll build
180189 Then I should get a zero exit status
181190 And the _site directory should exist
182191 And I should see "content for entry2" in "_site/2007/12/31/entry2.html"
183 And the "_site/2020/12/31/entry1.html" file should not exist
192 And the "_site/2040/12/31/entry1.html" file should not exist
184193 When I run jekyll build --future
185194 Then I should get a zero exit status
186195 And the _site directory should exist
187 And the "_site/2020/12/31/entry1.html" file should exist
196 And the "_site/2040/12/31/entry1.html" file should exist
188197
189198 Scenario: Basic site with layouts, posts and related posts
190199 Given I have a _layouts directory
106106 Then I should get a zero exit status
107107 And the _site directory should exist
108108 And I should see exactly "The rule of 3: Fly, Run, Jump," in "_site/bird.html"
109
110 Scenario: Filter posts by given property and value
111 Given I have a _posts directory
112 And I have the following posts:
113 | title | date | content | property |
114 | Bird | 2019-03-13 | Chirp | [nature, sounds] |
115 | Cat | 2019-03-14 | Meow | [sounds] |
116 | Dog | 2019-03-15 | Bark | |
117 | Elephant | 2019-03-16 | Asiatic | wildlife |
118 | Goat | 2019-03-17 | Mountains | "" |
119 | Horse | 2019-03-18 | Mustang | [] |
120 | Iguana | 2019-03-19 | Reptile | {} |
121 | Jaguar | 2019-03-20 | Reptile | {foo: lorem, bar: nature} |
122 And I have a "string-value.md" page with content:
123 """
124 {% assign pool = site.posts | reverse | where: 'property', 'wildlife' %}
125 {{ pool | map: 'title' | join: ', ' }}
126 """
127 And I have a "string-value-array.md" page with content:
128 """
129 {% assign pool = site.posts | reverse | where: 'property', 'sounds' %}
130 {{ pool | map: 'title' | join: ', ' }}
131 """
132 And I have a "string-value-hash.md" page with content:
133 """
134 {% assign pool = site.posts | reverse | where: 'property', 'nature' %}
135 {{ pool | map: 'title' | join: ', ' }}
136 """
137 And I have a "nil-value.md" page with content:
138 """
139 {% assign pool = site.posts | reverse | where: 'property', nil %}
140 {{ pool | map: 'title' | join: ', ' }}
141 """
142 And I have an "empty-liquid-literal.md" page with content:
143 """
144 {% assign pool = site.posts | reverse | where: 'property', empty %}
145 {{ pool | map: 'title' | join: ', ' }}
146 """
147 And I have a "blank-liquid-literal.md" page with content:
148 """
149 {% assign pool = site.posts | reverse | where: 'property', blank %}
150 {{ pool | map: 'title' | join: ', ' }}
151 """
152 When I run jekyll build
153 Then I should get a zero exit status
154 And the _site directory should exist
155 And I should see exactly "<p>Elephant</p>" in "_site/string-value.html"
156 And I should see exactly "<p>Bird, Cat</p>" in "_site/string-value-array.html"
157 And I should see exactly "<p>Bird</p>" in "_site/string-value-hash.html"
158 And I should see exactly "<p>Dog</p>" in "_site/nil-value.html"
159 And I should see exactly "<p>Dog, Goat, Horse, Iguana</p>" in "_site/empty-liquid-literal.html"
160 And I should see exactly "<p>Dog, Goat, Horse, Iguana</p>" in "_site/blank-liquid-literal.html"
102102 Then I should see "special" in "_site/page1.html"
103103 And I should not see "special" in "_site/page2.html"
104104
105 Scenario: Modify the converted HTML content of a page before rendering layout
106 Given I have a _layouts directory
107 And I have a "_layouts/page.html" file with content:
108 """
109 <h3>Page heading</h3>
110 {{ content }}
111 """
112 And I have a "page.md" page with layout "page" that contains "### Heading"
113 And I have a _plugins directory
114 And I have a "_plugins/ext.rb" file with content:
115 """
116 Jekyll::Hooks.register :pages, :post_convert do |page|
117 page.content = page.content.gsub('h3', 'h4')
118 end
119 """
120 When I run jekyll build
121 Then I should get a zero exit status
122 And the _site directory should exist
123 And I should see "<h3>Page heading</h3>" in "_site/page.html"
124 And I should see "<h4 id=\"heading\">Heading</h4>" in "_site/page.html"
125
105126 Scenario: Modify page contents before writing to disk
106127 Given I have a _plugins directory
107128 And I have a "index.html" page that contains "WRAP ME"
169190 Then I should see "old post" in "_site/2015/03/14/entry1.html"
170191 And I should see "new post" in "_site/2015/03/15/entry2.html"
171192
193 Scenario: Modify the converted HTML content of a post before rendering layout
194 Given I have a _layouts directory
195 And I have a "_layouts/post.html" file with content:
196 """
197 <h3>Page heading</h3>
198 {{ content }}
199 """
200 And I have a _posts directory
201 And I have a "_posts/2016-01-01-example.md" file with content:
202 """
203 ---
204 layout: post
205 ---
206 ### Heading
207 """
208 And I have a _plugins directory
209 And I have a "_plugins/ext.rb" file with content:
210 """
211 Jekyll::Hooks.register :posts, :post_convert do |post|
212 post.content = post.content.gsub('h3', 'h4')
213 end
214 """
215 When I run jekyll build
216 Then I should get a zero exit status
217 And the _site directory should exist
218 And I should see "<h3>Page heading</h3>" in "_site/2016/01/01/example.html"
219 And I should see "<h4 id=\"heading\">Heading</h4>" in "_site/2016/01/01/example.html"
220
172221 Scenario: Modify post contents before writing to disk
173222 Given I have a _plugins directory
174223 And I have a "_plugins/ext.rb" file with content:
242291 owner.output = "3 #{owner.output.chomp}"
243292 end
244293 Jekyll::Hooks.register :pages, :post_render, priority: :low do |owner|
245 # low runs last
294 # low runs last
246295 owner.output = "4 #{owner.output.chomp}"
247296 end
248297 """
276325 Then I should get a zero exit status
277326 And the _site directory should exist
278327 And I should see "all your base are belong to us" in "_site/index.html"
328
329 Scenario: Modify the converted HTML content of a document before rendering layout
330 Given I have a _layouts directory
331 And I have a "_layouts/meme.html" file with content:
332 """
333 <h3>Page heading</h3>
334 {{ content }}
335 """
336 And I have a "_config.yml" file with content:
337 """
338 collections:
339 memes:
340 output: true
341 """
342 And I have a _memes directory
343 And I have a "_memes/doc1.md" file with content:
344 """
345 ---
346 layout: meme
347 text: all your base
348 ---
349 ### {{ page.text }}
350 """
351 And I have a _plugins directory
352 And I have a "_plugins/ext.rb" file with content:
353 """
354 Jekyll::Hooks.register :documents, :post_convert do |document|
355 document.content = document.content.gsub('h3', 'h4')
356 end
357 """
358 When I run jekyll build
359 Then I should get a zero exit status
360 And the _site directory should exist
361 And I should see "<h3>Page heading</h3>" in "_site/memes/doc1.html"
362 And I should see "<h4 id=\"all-your-base\">all your base</h4>" in "_site/memes/doc1.html"
363
364 Scenario: Modify the converted HTML content of document of a particular collection before rendering layout
365 Given I have a _layouts directory
366 And I have a "_layouts/meme.html" file with content:
367 """
368 <h3>Page heading</h3>
369 {{ content }}
370 """
371 And I have a "_config.yml" file with content:
372 """
373 collections:
374 memes:
375 output: true
376 """
377 And I have a _memes directory
378 And I have a "_memes/doc1.md" file with content:
379 """
380 ---
381 layout: meme
382 text: all your base
383 ---
384 ### {{ page.text }}
385 """
386 And I have a _posts directory
387 And I have a "_posts/2016-01-01-example.md" file with content:
388 """
389 ---
390 layout: meme
391 text: all your base
392 ---
393 ### {{ page.text }}
394 """
395 And I have a _plugins directory
396 And I have a "_plugins/ext.rb" file with content:
397 """
398 Jekyll::Hooks.register :memes, :post_convert do |document|
399 document.content = document.content.gsub('h3', 'h4')
400 end
401 """
402 When I run jekyll build
403 Then I should get a zero exit status
404 And the _site directory should exist
405 And I should see "<h3>Page heading</h3>" in "_site/memes/doc1.html"
406 And I should see "<h4 id=\"all-your-base\">all your base</h4>" in "_site/memes/doc1.html"
407 But I should see "<h3 id=\"all-your-base\">all your base</h3>" in "_site/2016/01/01/example.html"
279408
280409 Scenario: Update a document after rendering it, but before writing it to disk
281410 Given I have a _plugins directory
6666 And the _site directory should exist
6767 And I should see "Basic Site with include tag: Regenerated by Jekyll" in "_site/index.html"
6868
69 Scenario: Rebuild when a dependency of document in custom collection_dir is changed
70 Given I have a _includes directory
71 And I have a configuration file with "collections_dir" set to "collections"
72 And I have a collections/_posts directory
73 And I have the following post within the "collections" directory:
74 | title | date | layout | content |
75 | Wargames | 2009-03-27 | default | Basic Site with include tag: {% include about.html %} |
76 And I have an "_includes/about.html" file that contains "Generated by Jekyll"
77 When I run jekyll build -I
78 Then I should get a zero exit status
79 And the _site directory should exist
80 And I should see "Basic Site with include tag: Generated by Jekyll" in "_site/2009/03/27/wargames.html"
81 When I wait 1 second
82 Then I have an "_includes/about.html" file that contains "Regenerated by Jekyll"
83 When I run jekyll build -I
84 Then I should get a zero exit status
85 And the _site directory should exist
86 And I should see "Basic Site with include tag: Regenerated by Jekyll" in "_site/2009/03/27/wargames.html"
87
6988 Scenario: A themed-site and incremental regeneration
7089 Given I have a configuration file with "theme" set to "test-theme"
7190 And I have an "index.md" page that contains "Themed site"
3737 When I run jekyll build
3838 Then I should get a zero exit status
3939 And the _site directory should exist
40 And I should see "<p><a href=\"/about.html\">About my projects</a></p>" in "_site/index.html"
41 And I should see "<p><a href=\"/\">Home</a></p>" in "_site/about.html"
40 And I should see "<p><a href=\"/blog/about.html\">About my projects</a></p>" in "_site/index.html"
41 And I should see "<p><a href=\"/blog/\">Home</a></p>" in "_site/about.html"
4242
4343 Scenario: Basic site with two pages and custom baseurl and permalinks
4444 Given I have an "index.md" page that contains "[About my projects]({% link about.md %})"
5151 When I run jekyll build
5252 Then I should get a zero exit status
5353 And the _site directory should exist
54 And I should see "<p><a href=\"/about/\">About my projects</a></p>" in "_site/index.html"
55 And I should see "<p><a href=\"/\">Home</a></p>" in "_site/about/index.html"
54 And I should see "<p><a href=\"/blog/about/\">About my projects</a></p>" in "_site/index.html"
55 And I should see "<p><a href=\"/blog/\">Home</a></p>" in "_site/about/index.html"
5656
5757 Scenario: Linking to a ghost file
5858 Given I have an "index.md" page that contains "[About my projects]({% link about.md %})"
2020 Given I have a configuration file with:
2121 | key | value |
2222 | paginate | 5 |
23 | gems | [jekyll-paginate] |
23 | plugins | [jekyll-paginate] |
2424 And I have an "index.html" page that contains "Index - {% for post in paginator.posts %} {{ post.content }} {% endfor %}"
2525 And I have a _posts directory
2626 And I have the following post:
66 Given I have a configuration file with:
77 | key | value |
88 | paginate | <num> |
9 | gems | [jekyll-paginate] |
9 | plugins | [jekyll-paginate] |
1010 And I have a _layouts directory
1111 And I have an "index.html" page that contains "{{ paginator.posts.size }}"
1212 And I have a _posts directory
3434 | paginate | 1 |
3535 | paginate_path | /blog/page-:num |
3636 | permalink | /blog/:year/:month/:day/:title |
37 | gems | [jekyll-paginate] |
37 | plugins | [jekyll-paginate] |
3838 And I have a blog directory
3939 And I have an "blog/index.html" page that contains "{{ paginator.posts.size }}"
4040 And I have a _posts directory
6262 | paginate | 1 |
6363 | paginate_path | /blog/page/:num |
6464 | permalink | /blog/:year/:month/:day/:title |
65 | gems | [jekyll-paginate] |
65 | plugins | [jekyll-paginate] |
6666 And I have a blog directory
6767 And I have an "blog/index.html" page that contains "{{ paginator.posts.size }}"
6868 And I have an "index.html" page that contains "Don't pick me!"
141141 And the _site directory should exist
142142 And I should see "I am PHP" in "_site/2016/i-am-php.php"
143143 And I should see "I am also PHP" in "_site/i-am-also-php.php"
144
145 Scenario: Using the same permalink twice
146 Given I have a "cool.md" page with permalink "/amazing.html" that contains "I am cool"
147 And I have an "awesome.md" page with permalink "/amazing.html" that contains "I am also awesome"
148 And I have an "amazing.html" file with content:
149 """
150 Hello World
151 I'm a static file
152 """
153 And I have a "_config.yml" file with content:
154 """
155 collections:
156 puppies:
157 output: true
158 permalink: /:collection/:year/:month/:day/:title:output_ext
159 """
160 And I have a _puppies directory
161 And I have the following documents under the puppies collection:
162 | title | date | content |
163 | Rover | 2009-03-27 | content for Rover. |
164 And I have a _posts directory
165 And I have the following post:
166 | title | date | layout | category | content |
167 | Rover | 2009-03-27 | none | puppies | Luke, I am your father. |
168 When I run jekyll build
169 Then I should get a zero exit status
170 And the _site directory should exist
171 And I should see "Conflict: The following destination is shared by multiple files." in the build output
172 And I should see "_site/amazing.html" in the build output
173 And I should see "awesome.md" in the build output
174 And I should see "cool.md" in the build output
175 And I should see "amazing.html" in the build output
176 And I should see "_site/puppies/2009/03/27/rover.html" in the build output
177 And I should see "_posts/2009-03-27-rover.markdown" in the build output
178 And I should see "_puppies/rover.md" in the build output
179
180 Scenario: Redirecting from an existing permalink
181 Given I have a configuration file with "plugins" set to "[jekyll-redirect-from]"
182 And I have a "deals.html" file with content:
183 """
184 ---
185 permalink: /deals/
186 redirect_from:
187 - /offers/
188 ---
189 """
190 And I have a "offers.html" page with permalink "/offers/" that contains "Hurry! Limited time only!"
191 When I run jekyll build
192 Then I should get a zero exit status
193 And the _site directory should exist
194 And I should not see "Conflict: The following destination is shared by multiple files." in the build output
195 And I should not see "_site/offers/index.html" in the build output
196 And I should not see "offers.html" in the build output
197 And I should not see "redirect.html" in the build output
33
44 Scenario: Add a gem-based plugin
55 Given I have an "index.html" file that contains "Whatever"
6 And I have a configuration file with "gems" set to "[jekyll_test_plugin]"
6 And I have a configuration file with "plugins" set to "[jekyll_test_plugin]"
77 When I run jekyll build
88 Then I should get a zero exit status
99 And the _site directory should exist
1414 Given I have an "index.html" file that contains "Whatever"
1515 And I have a configuration file with:
1616 | key | value |
17 | gems | [jekyll_test_plugin] |
17 | plugins | [jekyll_test_plugin] |
1818 | whitelist | [] |
1919 When I run jekyll build --safe
2020 Then I should get a zero exit status
2626 Given I have an "index.html" file that contains "Whatever"
2727 And I have a configuration file with:
2828 | key | value |
29 | gems | [jekyll_test_plugin, jekyll_test_plugin_malicious] |
29 | plugins | [jekyll_test_plugin, jekyll_test_plugin_malicious] |
3030 | whitelist | [jekyll_test_plugin] |
3131 When I run jekyll build --safe
3232 Then I should get a zero exit status
2626 And the _site directory should exist
2727 And I should see "Post url: /2009/03/27/star-wars.html" in "_site/2009/03/27/star-wars.html"
2828
29 Scenario: Use page.name variable
30 Given I have a _posts directory
31 And I have a _layouts directory
32 And I have the following post:
33 | title | date | layout | content |
34 | Star Wars | 2009-03-27 | simple | Luke, I am your father. |
35 And I have a simple layout that contains "Page name: {{ page.name }}"
36 When I run jekyll build
37 Then I should get a zero exit status
38 And the _site directory should exist
39 And I should see "Page name: 2009-03-27-star-wars.markdown" in "_site/2009/03/27/star-wars.html"
40
2941 Scenario: Use post.date variable
3042 Given I have a _posts directory
3143 And I have a _layouts directory
127139 Then I should get a zero exit status
128140 And the _site directory should exist
129141 And I should see "Post category: movies" in "_site/movies/2009/03/27/star-wars.html"
142
143 Scenario: Use post.categories when category is a composite of multiple words
144 Given I have a Sci-Fi Movi3s directory
145 And I have a Sci-Fi Movi3s/_posts directory
146 And I have a _layouts directory
147 And I have the following post in "Sci-Fi Movi3s":
148 | title | date | layout | category | content |
149 | Star Wars | 2020-04-03 | simple | vintage | Luke, I am your father. |
150 And I have a "_layouts/simple.html" file with content:
151 """
152 Post categories: {{ page.categories | join: ', ' }}
153 Post URL: {{ page.url }}
154 """
155 When I run jekyll build
156 Then I should get a zero exit status
157 And the _site directory should exist
158 And I should see "Post categories: Sci-Fi Movi3s, vintage" in "_site/sci-fi movi3s/vintage/2020/04/03/star-wars.html"
159 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"
160
161 Scenario: Use post.slugified_categories to generate URL when category is a composite of multiple words
162 Given I have a Sci-Fi Movi3s directory
163 And I have a Sci-Fi Movi3s/_posts directory
164 And I have a _layouts directory
165 And I have the following post in "Sci-Fi Movi3s":
166 | title | date | layout | category | content |
167 | Star Wars | 2020-04-03 | simple | vintage | Luke, I am your father. |
168 And I have a "_layouts/simple.html" file with content:
169 """
170 Post categories: {{ page.categories | join: ', ' }}
171 Post URL: {{ page.url }}
172 """
173 And I have a "_config.yml" file with content:
174 """
175 collections:
176 posts:
177 permalink: "/:slugified_categories/:year/:month/:day/:title:output_ext"
178 """
179 When I run jekyll build
180 Then I should get a zero exit status
181 And the _site directory should exist
182 And I should see "Post categories: Sci-Fi Movi3s, vintage" in "_site/sci-fi-movi3s/vintage/2020/04/03/star-wars.html"
183 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"
130184
131185 Scenario: Use post.tags variable
132186 Given I have a _posts directory
249303 And the _site directory should exist
250304 And I should see "Post categories: scifi and Movies" in "_site/scifi/movies/2009/03/27/star-wars.html"
251305 And I should see "Post categories: SciFi and movies" in "_site/scifi/movies/2013/03/17/star-trek.html"
306
307 Scenario: Use page.render_with_liquid variable
308 Given I have a _posts directory
309 And I have the following posts:
310 | title | render_with_liquid | date | content |
311 | Unrendered Post | false | 2017-07-06 | Hello {{ page.title }} |
312 | Rendered Post | true | 2017-07-06 | Hello {{ page.title }} |
313 When I run jekyll build
314 Then I should get a zero exit status
315 And the _site directory should exist
316 And I should not see "Hello Unrendered Post" in "_site/2017/07/06/unrendered-post.html"
317 But I should see "Hello {{ page.title }}" in "_site/2017/07/06/unrendered-post.html"
318 And I should see "Hello Rendered Post" in "_site/2017/07/06/rendered-post.html"
252319
253320 Scenario Outline: Use page.path variable
254321 Given I have a <dir>/_posts directory
326393 And the _site directory should exist
327394 And I should see "next post: Some like it hot" in "_site/2009/03/27/star-wars.html"
328395 And I should see "Previous post: Some like it hot" in "_site/2009/05/27/terminator.html"
396
397 Scenario: Deprecate calling data keys directly via Ruby
398 Given I have a _posts directory
399 And I have a _plugins directory
400 And I have the following post:
401 | title | date | content |
402 | My post | 2016-01-21 | Luke, I am your father. |
403 And I have a "_plugins/foo.rb" file with content:
404 """
405 Jekyll::Hooks.register :documents, :pre_render do |doc|
406 doc.title
407 end
408 """
409 And I have a "_plugins/bar.rb" file with content:
410 """
411 module FooBar
412 def self.dummy?(doc)
413 doc.title == "Dummy Document"
414 end
415 end
416
417 Jekyll::Hooks.register :documents, :post_render do |doc|
418 FooBar.dummy?(doc)
419 end
420 """
421 When I run jekyll build
422 Then I should get a zero exit status
423 And the _site directory should exist
424 And I should see "Deprecation: Document#title" in the build output
425 And I should see "_plugins/foo.rb:2" in the build output
426 And I should see "_plugins/bar.rb:3" in the build output
427 But I should not see "lib/jekyll/document.rb" in the build output
6969 And the "_site/2007/12/31/entry1.html" file should exist
7070 And I should see "<p>content for entry1.</p>" in "_site/index.html"
7171 And I should see "<html><head></head><body><p>content for entry1.</p>\n</body></html>" in "_site/2007/12/31/entry1.html"
72
73 Scenario: Excerpts from posts having 'render_with_liquid' in their front matter
74 Given I have an "index.html" page that contains "{% for post in site.posts %}{{ post.excerpt }}{% endfor %}"
75 And I have a _posts directory
76 And I have a _layouts directory
77 And I have a post layout that contains "{{ page.excerpt }}"
78 And I have the following posts:
79 | title | layout | render_with_liquid | date | content |
80 | Unrendered Post | post | false | 2017-07-06 | Liquid is not rendered at {{ page.url }} |
81 | Rendered Post | post | true | 2017-07-06 | Liquid is rendered at {{ page.url }} |
82 When I run jekyll build
83 Then I should get a zero exit status
84 And the _site/2017/07/06 directory should exist
85 And the "_site/2017/07/06/unrendered-post.html" file should exist
86 And the "_site/2017/07/06/rendered-post.html" file should exist
87 And I should see "Liquid is not rendered at {{ page.url }}" in "_site/2017/07/06/unrendered-post.html"
88 But I should see "<p>Liquid is rendered at /2017/07/06/rendered-post.html</p>" in "_site/2017/07/06/rendered-post.html"
89 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"
90
91 Scenario: Excerpts from posts with reference-style Markdown links
92 Given I have a configuration file with:
93 | key | value |
94 | permalink | "/:title:output_ext" |
95 | kramdown | { show_warnings: true } |
96 And I have an "index.html" page that contains "{% for post in site.posts %}{{ post.excerpt }}{% endfor %}"
97 And I have a _layouts directory
98 And I have a post layout that contains "{{ page.excerpt }}"
99 And I have a _posts directory
100 And I have the following posts:
101 | title | layout | date | content |
102 | Just Text Excerpt | post | 2019-03-06 | Install Jekyll\n\nNext Para [^1]\n\n[^1]: Lorem ipsum |
103 | Text and Footnote | post | 2019-03-07 | Alpha [^1]\n\nNext Para\n\n[^1]: Omega sigma |
104 | Text and Reference Link | post | 2019-03-08 | Read [docs][link]\n\nNext Para\n\n[link]: docs.jekyll.com |
105 | Text and Self-referencing Link | post | 2019-03-09 | Check out [jekyll]\n\nNext Para\n\n[jekyll]: jekyllrb.com |
106 When I run jekyll build
107 Then I should get a zero exit status
108 And I should not see "Kramdown warning" in the build output
109 But I should see exactly "<p>Install Jekyll</p>" in "_site/just-text-excerpt.html"
110 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"
111 And I should see "<p>Omega sigma <a href=\"#fnref:1\" class=\"reversefootnote\" role=\"doc-backlink\">&#8617;</a></p>" in "_site/text-and-footnote.html"
112 And I should see "<p>Read <a href=\"docs.jekyll.com\">docs</a></p>" in "_site/text-and-reference-link.html"
113 And I should see "<p>Check out <a href=\"jekyllrb.com\">jekyll</a></p>" in "_site/text-and-self-referencing-link.html"
0 Feature: PostUrl Tag
1 As a blogger who likes to write a variety of content
2 I want to be able to link to posts easily
3 And render them without much hassle
4
5 Scenario: A site that is using the defaults for permalink
6 Given I have a _posts directory
7 And I have the following post:
8 | title | date | content |
9 | Hello World | 2019-02-04 | Lorem ipsum dolor |
10 And I have an "index.md" page that contains "[Welcome]({% post_url 2019-02-04-hello-world %})"
11 When I run jekyll build
12 Then I should get a zero exit status
13 And the _site directory should exist
14 And I should see "<p><a href=\"/2019/02/04/hello-world.html\">Welcome</a></p>" in "_site/index.html"
15
16 Scenario: Site with site-wide custom permalink setting
17 Given I have a _posts directory
18 And I have the following posts:
19 | title | date | content |
20 | Hello World | 2019-02-04 | Lorem ipsum dolor |
21 | We Meet Again | 2019-02-05 | Alpha beta gamma |
22 And I have a configuration file with "permalink" set to "/:title:output_ext"
23 And I have an "index.md" page that contains "[Welcome]({% post_url 2019-02-04-hello-world %})"
24 When I run jekyll build
25 Then I should get a zero exit status
26 And the _site directory should exist
27 And I should see "<p><a href=\"/hello-world.html\">Welcome</a></p>" in "_site/index.html"
28
29 Scenario: Site with custom permalink settings on each post
30 Given I have a _posts directory
31 And I have the following posts:
32 | title | date | permalink | content |
33 | Hello World | 2019-02-04 | "/2019/hello-world/" | Lorem ipsum dolor |
34 | We Meet Again | 2019-02-05 | "/2019/second-meeting/" | Alpha beta gamma |
35 And I have a configuration file with "permalink" set to "/:title:output_ext"
36 And I have an "index.md" page that contains "[Welcome]({% post_url 2019-02-04-hello-world %})"
37 When I run jekyll build
38 Then I should get a zero exit status
39 And the _site directory should exist
40 And I should see "<p><a href=\"/2019/hello-world/\">Welcome</a></p>" in "_site/index.html"
41
42 Scenario: Site with no posts
43 Given I have an "index.md" page that contains "[Welcome]({% post_url 2019-02-04-hello-world %})"
44 When I run jekyll build
45 Then I should get a non-zero exit status
46 And the _site directory should not exist
47 But I should see "Could not find post \"2019-02-04-hello-world\" in tag 'post_url'." in the build output
48
49 Scenario: Site with a future-dated post
50 Given I have a _posts directory
51 And I have the following posts:
52 | title | date | content |
53 | Hello World | 2019-02-04 | Lorem ipsum dolor |
54 | We Meet Again | 2119-02-04 | Alpha beta gamma |
55 And I have a configuration file with "permalink" set to "/:title:output_ext"
56 And I have an "index.md" page that contains "[Welcome Again]({% post_url 2119-02-04-we-meet-again %})"
57 When I run jekyll build --future
58 Then I should get a zero exit status
59 And the _site directory should exist
60 And I should see "<p><a href=\"/we-meet-again.html\">Welcome Again</a></p>" in "_site/index.html"
61
62 Scenario: Site with configured baseurl
63 Given I have a _posts directory
64 And I have the following posts:
65 | title | date | content |
66 | Hello World | 2019-02-04 | Lorem ipsum dolor |
67 | We Meet Again | 2019-02-05 | Alpha beta gamma |
68 And I have a configuration file with "baseurl" set to "blog"
69 And I have an "index.md" page that contains "[Welcome]({% post_url 2019-02-04-hello-world %})"
70 When I run jekyll build
71 Then I should get a zero exit status
72 And the _site directory should exist
73 And I should see "<p><a href=\"/blog/2019/02/04/hello-world.html\">Welcome</a></p>" in "_site/index.html"
74
75 Scenario: Posts with categories
76 Given I have a _posts directory
77 And I have the following post:
78 | title | date | content |
79 | Hello World | 2019-02-04 | Lorem ipsum dolor |
80 And I have a movies/_posts directory
81 And I have the following post in "movies":
82 | title | date | content |
83 | Hello Movies | 2019-02-05 | Lorem ipsum dolor |
84 And I have the following post in "movies":
85 | title | date | category | content |
86 | Star Wars | 2019-02-06 | film | Luke, I am your father |
87 And I have an "index.md" page with content:
88 """
89 [Welcome]({% post_url 2019-02-04-hello-world %})
90
91 [Movies]({% post_url movies/2019-02-05-hello-movies %})
92
93 [Film]({% post_url movies/2019-02-06-star-wars %})
94 """
95 When I run jekyll build
96 Then I should get a zero exit status
97 And the _site directory should exist
98 And I should see "<p><a href=\"/2019/02/04/hello-world.html\">Welcome</a></p>" in "_site/index.html"
99 And I should see "<p><a href=\"/movies/2019/02/05/hello-movies.html\">Movies</a></p>" in "_site/index.html"
100 And I should see "<p><a href=\"/movies/film/2019/02/06/star-wars.html\">Film</a></p>" in "_site/index.html"
101
102 Scenario: Duplicate posts with categories
103 Given I have a _posts directory
104 And I have the following post:
105 | title | date | content |
106 | Hello World | 2019-02-04 | Lorem ipsum dolor |
107 And I have a movies/_posts directory
108 And I have the following post in "movies":
109 | title | date | content |
110 | Hello World | 2019-02-04 | Lorem ipsum dolor |
111 And I have an "index.md" page with content:
112 """
113 [Welcome]({% post_url 2019-02-04-hello-world %})
114
115 [Movies]({% post_url movies/2019-02-04-hello-world %})
116 """
117 When I run jekyll build
118 Then I should get a zero exit status
119 And the _site directory should exist
120 And I should see "<p><a href=\"/2019/02/04/hello-world.html\">Welcome</a></p>" in "_site/index.html"
121 And I should see "<p><a href=\"/movies/2019/02/04/hello-world.html\">Movies</a></p>" in "_site/index.html"
122
123 Scenario: Deprecated usage to link nested post
124 Given I have a movies/_posts directory
125 And I have the following post in "movies":
126 | title | date | content |
127 | Hello World | 2019-02-04 | Lorem ipsum dolor |
128 And I have an "index.md" page that contains "[Movies]({% post_url 2019-02-04-hello-world %})"
129 When I run jekyll build
130 Then I should get a zero exit status
131 And I should see "Deprecation: A call to '{% post_url 2019-02-04-hello-world %}' did not match a post" in the build output
132 But the _site directory should exist
133 And I should see "<p><a href=\"/movies/2019/02/04/hello-world.html\">Movies</a></p>" in "_site/index.html"
134
135 Scenario: Nested posts in a directory with name containing spaces
136 Given I have a Cats and Dogs/_posts directory
137 And I have the following post in "Cats and Dogs":
138 | title | date | content |
139 | Hello World | 2019-02-04 | Lorem ipsum dolor |
140 And I have a _posts/Salt and Pepper directory
141 And I have the following post under "Salt and Pepper":
142 | title | date | content |
143 | Hello Again | 2019-02-05 | Lorem ipsum dolor |
144 And I have an "index.md" file with content:
145 """
146 ---
147 ---
148
149 [Post 1]({% post_url Cats and Dogs/2019-02-04-hello-world %})
150
151 [Post 2]({% post_url Salt and Pepper/2019-02-05-hello-again %})
152 """
153 When I run jekyll build
154 Then I should get a zero exit status
155 And I should not see "Deprecation: A call to '{% post_url" in the build output
156 But the _site directory should exist
157 And I should see "<p><a href=\"/cats%20and%20dogs/2019/02/04/hello-world.html\">Post 1</a></p>" in "_site/index.html"
158 And I should see "<p><a href=\"/2019/02/05/hello-again.html\">Post 2</a></p>" in "_site/index.html"
4848 Then I should get a zero exit-status
4949 And I should not see "Liquid Exception:" in the build output
5050
51 Scenario: Rendering a default site containing a file with a non-existent Liquid variable
52 Given I have a "index.html" file with content:
53 """
54 ---
55 title: Simple Test
56 ---
57 {{ site.lorem.ipsum }}
58 {{ site.title }}
59 """
60 And I have a configuration file with "title" set to "Hello World"
61 When I run jekyll build
62 Then I should get a zero exit-status
63 And the _site directory should exist
64
5165 Scenario: Rendering a custom site containing a file with a non-existent Liquid variable
5266 Given I have a "index.html" file with content:
5367 """
97111 Scenario: Don't place asset files in layout
98112 Given I have an "index.scss" page with layout "simple" that contains ".foo-bar { color:black; }"
99113 And I have an "index.coffee" page with layout "simple" that contains "whatever()"
100 And I have a configuration file with "gems" set to "[jekyll-coffeescript]"
114 And I have a configuration file with "plugins" set to "[jekyll-coffeescript]"
101115 And I have a simple layout that contains "{{ content }}Ahoy, indeed!"
102116 When I run jekyll build
103117 Then I should get a zero exit status
153167 When I run jekyll build
154168 Then I should get a zero exit status
155169 And the _site directory should exist
156 And I should see ".foo-bar {\n color: red; }" in "_site/index.css"
170 And I should see ".foo-bar { color: red; }\n\n\/\*# sourceMappingURL=index.css.map \*\/" in "_site/index.css"
157171
158172 Scenario: Not render liquid in CoffeeScript without explicitly including jekyll-coffeescript
159173 Given I have an "index.coffee" page with animal "cicada" that contains "hey='for {{page.animal}}'"
164178
165179 Scenario: Render liquid in CoffeeScript with jekyll-coffeescript enabled
166180 Given I have an "index.coffee" page with animal "cicada" that contains "hey='for {{page.animal}}'"
167 And I have a configuration file with "gems" set to "[jekyll-coffeescript]"
181 And I have a configuration file with "plugins" set to "[jekyll-coffeescript]"
168182 When I run jekyll build
169183 Then I should get a zero exit status
170184 And the _site directory should exist
171185 And I should see "hey = 'for cicada';" in "_site/index.js"
186
187 Scenario: Rendering Liquid expressions that return strings containing Liquid expressions
188 Given I have an "index.md" file with content:
189 """
190 ---
191 prequel: "{% link series/first-part.md %}"
192 sequel: "{% link series/last-part.md %}"
193 ---
194
195 This is the second-part of the series named {{ site.novel }}.
196 The first part is at {{ page.prequel }}.
197
198 Lorem ipsum
199
200 {% capture sequel_link %}{{ page.sequel }}{% endcapture %}
201 The last part of the series can be read at {{ sequel_link }}
202 """
203 And I have a configuration file with "novel" set to "'{{ site.title }}'"
204 When I run jekyll build
205 Then I should get a zero exit status
206 And I should see "series named {{ site.title }}" in "_site/index.html"
207 And I should see "{% link series/first-part.md %}" in "_site/index.html"
208 And I should see "{% link series/last-part.md %}" in "_site/index.html"
209
210 Scenario: Render content of another page
211 Given I have an "index.md" page that contains "__Hello World__"
212 And I have an "about.md" page that contains "{{ page.name }}"
213 And I have a "test.json" file with content:
214 """
215 ---
216 ---
217
218 {
219 "hpages": [
220 {%- for page in site.html_pages %}
221 {
222 "url" : {{ page.url | jsonify }},
223 "name" : {{ page.name | jsonify }},
224 "path" : {{ page.path | jsonify }},
225 "title" : {{ page.title | jsonify }},
226 "layout" : {{ page.layout | jsonify }},
227 "content": {{ page.content | jsonify }},
228 "excerpt": {{ page.excerpt | jsonify }}
229 }{% unless forloop.last %},{% endunless -%}
230 {% endfor %}
231 ]
232 }
233 """
234 When I run jekyll build
235 Then I should get a zero exit status
236 And the _site directory should exist
237 But I should not see "content\": \"{{ page.name }}" in "_site/test.json"
238 And I should not see "content\": \"__Hello World__" in "_site/test.json"
239 But I should see "content\": \"<p>about.md</p>" in "_site/test.json"
240 And I should see "content\": \"<p><strong>Hello World</strong></p>" in "_site/test.json"
4747 And I have a configuration file with "exclude" set to "['Rakefile', 'README']"
4848 When I run jekyll build
4949 Then I should see "I want to be included" in "_site/index.html"
50 And the "_site/Gemfile" file should exist
50 And the "_site/Gemfile" file should not exist
5151 And the "_site/Rakefile" file should not exist
5252 And the "_site/README" file should not exist
5353
5555 Given I have an "Rakefile" file that contains "I want to be excluded"
5656 And I have an "README" file that contains "I want to be excluded"
5757 And I have an "index.html" file that contains "I want to be included"
58 And I have a "Gemfile" file that contains "gem 'include-me'"
5859 And I have a configuration file with "exclude" set to:
5960 | value |
6061 | README |
6364 Then I should see "I want to be included" in "_site/index.html"
6465 And the "_site/Rakefile" file should not exist
6566 And the "_site/README" file should not exist
66
67 Scenario: Use RDiscount for markup
68 Given I have an "index.markdown" page that contains "[Google](https://www.google.com)"
69 And I have a configuration file with "markdown" set to "rdiscount"
70 When I run jekyll build
71 Then I should get a zero exit status
72 And the _site directory should exist
73 And I should see "<a href=\"https://www.google.com\">Google</a>" in "_site/index.html"
67 And the "_site/Gemfile" file should not exist
68
69 Scenario: Copy over excluded files when their directory is explicitly included
70 Given I have a ".gitignore" file that contains ".DS_Store"
71 And I have an ".htaccess" file that contains "SomeDirective"
72 And I have a "Gemfile" file that contains "gem 'include-me'"
73 And I have a node_modules directory
74 And I have a "node_modules/bazinga.js" file that contains "var c = 'Bazinga!';"
75 And I have a "node_modules/warning.js" file that contains "var w = 'Winter is coming!';"
76 And I have a configuration file with "include" set to:
77 | value |
78 | .gitignore |
79 | .foo |
80 | Gemfile |
81 | node_modules |
82 When I run jekyll build
83 Then I should get a zero exit status
84 And the _site directory should exist
85 And the "_site/.htaccess" file should not exist
86 But I should see ".DS_Store" in "_site/.gitignore"
87 And I should see "gem 'include-me'" in "_site/Gemfile"
88 And I should see "var c = 'Bazinga!';" in "_site/node_modules/bazinga.js"
89 And I should see "var w = 'Winter is coming!';" in "_site/node_modules/warning.js"
90
91 Scenario: Copy over excluded files only when they are explicitly included
92 Given I have a ".gitignore" file that contains ".DS_Store"
93 And I have an ".htaccess" file that contains "SomeDirective"
94 And I have a node_modules directory
95 And I have a "node_modules/bazinga.js" file that contains "var c = 'Bazinga!';"
96 And I have a "node_modules/warning.js" file that contains "var w = 'Winter is coming!';"
97 And I have a configuration file with "include" set to:
98 | value |
99 | .gitignore |
100 | .foo |
101 | node_modules/bazinga.js |
102 When I run jekyll build
103 Then I should get a zero exit status
104 And the _site directory should exist
105 And the "_site/.htaccess" file should not exist
106 But I should see ".DS_Store" in "_site/.gitignore"
107 And I should see "var c = 'Bazinga!';" in "_site/node_modules/bazinga.js"
108 But the "_site/node_modules/warning.js" file should not exist
109
110 Scenario: Copy over excluded wild-card files only when they are explicitly included
111 Given I have a ".gitignore" file that contains ".DS_Store"
112 And I have an ".htaccess" file that contains "SomeDirective"
113 And I have an "foo.txt" file that contains "Lorem Ipsum"
114 And I have an "index.md" page that contains "{{ site.title }}"
115 And I have an "about.md" page that contains "{{ site.author }}"
116 And I have a configuration file with:
117 | key | value |
118 | title | Barren Site |
119 | author | John Doe |
120 | exclude | ["**"] |
121 When I run jekyll build
122 Then I should get a zero exit status
123 And the _site directory should exist
124 And the "_site/.gitignore" file should not exist
125 And the "_site/foo.txt" file should not exist
126 And the "_site/index.html" file should not exist
127 And the "_site/about.html" file should not exist
128 But the "_site/.htaccess" file should exist
129 Given I have a configuration file with:
130 | key | value |
131 | title | Barren Site |
132 | author | John Doe |
133 | exclude | ["**"] |
134 | include | ["about.md"] |
135 When I run jekyll build
136 Then I should get a zero exit status
137 And the _site directory should exist
138 And the "_site/.gitignore" file should not exist
139 And the "_site/foo.txt" file should not exist
140 And the "_site/index.html" file should not exist
141 And the "_site/.htaccess" file should not exist
142 But the "_site/about.html" file should exist
143 And I should see "John Doe" in "_site/about.html"
144
145 Scenario: Process included files only once
146 Given I have a ".foobar" page that contains "dotfile with front matter"
147 And I have an ".htaccess" file that contains "SomeDirective"
148 And I have a "_redirects" file that contains "/foo/* /bar/* 301!"
149 And I have an "index.md" file with content:
150 """
151 ---
152 ---
153
154 Dotpages: {{ site.pages | where: 'path', '.foobar' | size }}
155 Dotstatics: {{ site.static_files | where: 'path', '/_redirects' | size }}
156 """
157 And I have a configuration file with "title" set to "Hello World"
158 When I run jekyll build
159 Then I should get a zero exit status
160 And the _site directory should exist
161 But the "_site/.foobar" file should not exist
162 And the "_site/_redirects" file should not exist
163 And I should see "Dotpages: 0" in "_site/index.html"
164 And I should see "Dotstatics: 0" in "_site/index.html"
165
166 When I have a configuration file with:
167 | key | value |
168 | title | Hello World |
169 | include | [.foobar, _redirects] |
170 When I run jekyll build
171 Then I should get a zero exit status
172 And I should not see "Conflict:" in the build output
173 And the _site directory should exist
174 And the "_site/.foobar" file should exist
175 And the "_site/_redirects" file should exist
176 And I should see "Dotpages: 1" in "_site/index.html"
177 And I should see "Dotstatics: 1" in "_site/index.html"
74178
75179 Scenario: Use Kramdown for markup
76180 Given I have an "index.markdown" page that contains "[Google](https://www.google.com)"
79183 Then I should get a zero exit status
80184 And the _site directory should exist
81185 And I should see "<a href=\"https://www.google.com\">Google</a>" in "_site/index.html"
82
83 Scenario: Use Redcarpet for markup
84 Given I have an "index.markdown" page that contains "[Google](https://www.google.com)"
85 And I have a configuration file with "markdown" set to "redcarpet"
86 When I run jekyll build
87 Then I should get a zero exit status
88 And the _site directory should exist
89 And I should see "<a href=\"https://www.google.com\">Google</a>" in "_site/index.html"
90
91 Scenario: Highlight code with pygments
92 Given I have an "index.html" page that contains "{% highlight ruby %} puts 'Hello world!' {% endhighlight %}"
93 When I run jekyll build
94 Then I should get a zero exit status
95 And the _site directory should exist
96 And I should see "Hello world!" in "_site/index.html"
97 And I should see "class=\"highlight\"" in "_site/index.html"
98186
99187 Scenario: Highlight code with rouge
100188 Given I have an "index.html" page that contains "{% highlight ruby %} puts 'Hello world!' {% endhighlight %}"
202290 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"
203291 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"
204292
293 Scenario: Generate proper dates with explicitly set timezone (using non-half hour offset )
294 Given I have a _layouts directory
295 And I have a page layout that contains "Page Layout: {{ site.posts.size }}"
296 And I have a post layout that contains "Post Layout: {{ content }} built at {{ page.date | date_to_xmlschema }}"
297 And I have an "index.html" page with layout "page" that contains "site index page"
298 And I have a configuration file with:
299 | key | value |
300 | timezone | Australia/Eucla |
301 And I have a _posts directory
302 And I have the following posts:
303 | title | date | layout | content |
304 | entry1 | 2013-04-09 23:22 +0400 | post | content for entry1. |
305 | entry2 | 2013-04-10 03:14 +0400 | post | content for entry2. |
306 When I run jekyll build
307 Then I should get a zero exit status
308 And the _site directory should exist
309 And I should see "Page Layout: 2" in "_site/index.html"
310 And the "_site/2013/04/10/entry1.html" file should exist
311 And the "_site/2013/04/10/entry2.html" file should exist
312 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"
313 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"
314
205315 Scenario: Limit the number of posts generated by most recent date
206316 Given I have a _posts directory
207317 And I have a configuration file with:
218328 And the "_site/2009/04/05/bananas.html" file should exist
219329 And the "_site/2009/04/01/oranges.html" file should exist
220330 And the "_site/2009/03/27/apples.html" file should not exist
221
222 Scenario: Copy over normally excluded files when they are explicitly included
223 Given I have a ".gitignore" file that contains ".DS_Store"
224 And I have an ".htaccess" file that contains "SomeDirective"
225 And I have a configuration file with "include" set to:
226 | value |
227 | .gitignore |
228 | .foo |
229 When I run jekyll build
230 Then I should get a zero exit status
231 And the _site directory should exist
232 And I should see ".DS_Store" in "_site/.gitignore"
233 And the "_site/.htaccess" file should not exist
234331
235332 Scenario: Using a different layouts directory
236333 Given I have a _theme directory
3333 #
3434
3535 Given(%r!^I have an? "(.*)" page(?: with (.*) "(.*)")? that contains "(.*)"$!) do |file, key, value, text|
36 File.write(file, Jekyll::Utils.strip_heredoc(<<-DATA))
36 File.write(file, <<~DATA)
3737 ---
3838 #{key || "layout"}: #{value || "none"}
3939 ---
6262
6363 Given(%r!^I have an? "(.*)" file with content:$!) do |file, text|
6464 File.write(file, text)
65 end
66
67 #
68
69 Given(%r!^I have an? "(.*)" page with content:$!) do |file, text|
70 File.write(file, <<~DATA)
71 ---
72 ---
73
74 #{text}
75 DATA
6576 end
6677
6778 #
151162 else
152163 {}
153164 end
154 config[key] = YAML.load(value)
165 config[key] = SafeYAML.load(value)
155166 Jekyll.set_timezone(value) if key == "timezone"
156167 File.write("_config.yml", YAML.dump(config))
157168 end
177188
178189 #
179190
180 Given(%r!^I have fixture collections$!) do
181 FileUtils.cp_r Paths.source_dir.join("test", "source", "_methods"), source_dir
182 FileUtils.cp_r Paths.source_dir.join("test", "source", "_thanksgiving"), source_dir
183 end
184
185 #
186
187 Given("I'm using kramdown v{int}") do |int|
188 skip_this_scenario unless Kramdown::VERSION.to_i == int.to_i
191 Given(%r!^I have fixture collections(?: in "(.*)" directory)?$!) do |directory|
192 collections_dir = File.join(source_dir, directory.to_s)
193 FileUtils.cp_r Paths.source_dir.join("test", "source", "_methods"), collections_dir
194 FileUtils.cp_r Paths.source_dir.join("test", "source", "_thanksgiving"), collections_dir
195 FileUtils.cp_r Paths.source_dir.join("test", "source", "_tutorials"), collections_dir
189196 end
190197
191198 #
231238
232239 When(%r!^I decide to build the theme gem$!) do
233240 Dir.chdir(Paths.theme_gem_dir)
234 File.new("_includes/blank.html", "w")
235 File.new("_sass/blank.scss", "w")
236 File.new("assets/blank.scss", "w")
241 [
242 "_includes/blank.html",
243 "_sass/blank.scss",
244 "assets/blank.scss",
245 "_config.yml"
246 ].each do |filename|
247 File.new(filename, "w")
248 end
237249 end
238250
239251 #
377389 Gemfile
378390 LICENSE.txt
379391 README.md
392 _config.yml
380393 _includes/blank.html
381394 _layouts/default.html
382395 _layouts/page.html
00 # frozen_string_literal: true
11
2 require "fileutils"
32 require "jekyll"
4 require "time"
5 require "safe_yaml/load"
63
74 class Paths
85 SOURCE_DIR = Pathname.new(File.expand_path("../..", __dir__))
6
97 def self.test_dir; source_dir.join("tmp", "jekyll"); end
108
119 def self.theme_gem_dir; source_dir.join("tmp", "jekyll", "my-cool-theme"); end
2321
2422 def file_content_from_hash(input_hash)
2523 matter_hash = input_hash.reject { |k, _v| k == "content" }
26 matter = matter_hash.map do |k, v|
27 "#{k}: #{v}\n"
28 end
24 matter = matter_hash.map { |k, v| "#{k}: #{v}\n" }.join
25 matter.chomp!
26 content = if input_hash["input"] && input_hash["filter"]
27 "{{ #{input_hash["input"]} | #{input_hash["filter"]} }}"
28 else
29 input_hash["content"]
30 end
2931
30 matter = matter.join.chomp
31 content = \
32 if !input_hash["input"] || !input_hash["filter"]
33 then input_hash["content"]
34 else "{{ #{input_hash["input"]} | " \
35 "#{input_hash["filter"]} }}"
36 end
32 <<~EOF
33 ---
34 #{matter}
35 ---
3736
38 Jekyll::Utils.strip_heredoc(<<-EOF)
39 ---
40 #{matter.gsub(
41 %r!\n!, "\n "
42 )}
43 ---
4437 #{content}
4538 EOF
4639 end
4841 #
4942
5043 def source_dir(*files)
51 return Paths.test_dir(*files)
44 Paths.test_dir(*files)
5245 end
5346
5447 #
6962 #
7063
7164 def jekyll_run_output
72 if Paths.output_file.file?
73 then return Paths.output_file.read
74 end
65 Paths.output_file.read if Paths.output_file.file?
7566 end
7667
7768 #
7869
7970 def jekyll_run_status
80 if Paths.status_file.file?
81 then return Paths.status_file.read
82 end
71 Paths.status_file.read if Paths.status_file.file?
8372 end
8473
8574 #
10392 end
10493
10594 #
95
10696 def run_in_shell(*args)
10797 p, output = Jekyll::Utils::Exec.run(*args)
10898
120110 #
121111
122112 def slug(title = nil)
123 if !title
124 then Time.now.strftime("%s%9N") # nanoseconds since the Epoch
125 else title.downcase.gsub(%r![^\w]!, " ").strip.gsub(%r!\s+!, "-")
113 if title
114 title.downcase.gsub(%r![^\w]!, " ").strip.gsub(%r!\s+!, "-")
115 else
116 Time.now.strftime("%s%9N") # nanoseconds since the Epoch
126117 end
127118 end
128119
135126 end
136127
137128 [before || ".",
138 after || ".",]
129 after || "",]
139130 end
140131
141132 #
142133
143134 def file_contents(path)
144 return Pathname.new(path).read
135 Pathname.new(path).read
145136 end
146137
147138 #
154145 Regexp.escape(date),
155146 "#{time}:\\d{2}",
156147 Regexp.escape(zone),
157 ] \
158 .join("\\ ")
148 ].join("\\ ")
159149 end
160150
161151 #
1515
1616 Scenario: A theme with SCSS
1717 Given I have a configuration file with "theme" set to "test-theme"
18 And I have a css directory
19 And I have a "css/main.scss" page that contains "@import 'test-theme-black';"
2018 When I run jekyll build
2119 Then I should get a zero exit status
2220 And the _site directory should exist
23 And I should see ".sample {\n color: black; }" in "_site/css/main.css"
21 And I should see ".sample { color: red; }\n\n\/\*# sourceMappingURL=style.css.map \*\/" in "_site/assets/style.css"
22
23 Scenario: Overriding a theme with SCSS
24 Given I have a configuration file with "theme" set to "test-theme"
25 And I have an assets directory
26 And I have an "assets/style.scss" page that contains "@import 'test-theme-black';"
27 When I run jekyll build
28 Then I should get a zero exit status
29 And the _site directory should exist
30 And I should see ".sample { color: black; }\n\n\/\*# sourceMappingURL=style.css.map \*\/" in "_site/assets/style.css"
2431
2532 Scenario: A theme with an include
2633 Given I have a configuration file with "theme" set to "test-theme"
3239 And the _site directory should exist
3340 And I should see "I'm in the project." in "_site/index.html"
3441 And I should see "<span class=\"sample\">include.html from test-theme</span>" in "_site/index.html"
42
43 Scenario: A theme without data
44 Given I have a configuration file with "theme" set to "test-theme-skinny"
45 And I have a _data directory
46 And I have a "_data/greetings.yml" file with content:
47 """
48 foo: "Hello! I’m foo. And who are you?"
49 """
50 And I have an "index.html" page that contains "{{ site.data.greetings.foo }}"
51 When I run jekyll build
52 Then I should get a zero exit status
53 And the _site directory should exist
54 And I should see "Hello! I’m foo. And who are you?" in "_site/index.html"
55
56 Scenario: A theme with data overridden by data in source directory
57 Given I have a configuration file with "theme" set to "test-theme"
58 And I have a _data directory
59 And I have a "_data/greetings.yml" file with content:
60 """
61 foo: "Hello! I’m foo. And who are you?"
62 """
63 And I have an "index.html" page that contains "{{ site.data.greetings.foo }}"
64 When I run jekyll build
65 Then I should get a zero exit status
66 And the _site directory should exist
67 And I should see "Hello! I’m foo. And who are you?" in "_site/index.html"
68 And I should not see "Hello! I’m bar. What’s up so far?" in "_site/index.html"
3569
3670 Scenario: A theme with a layout
3771 Given I have a configuration file with "theme" set to "test-theme"
98132 And I should see "default.html from test-theme:" in "_site/2016/04/21/entry1.html"
99133 And I should see "I am using a local layout." in "_site/2016/04/21/entry1.html"
100134 And I should see "I am a post layout!" in "_site/2016/04/21/entry1.html"
135
136 Scenario: Complicated site that puts it all together in respect to data folders
137 Given I have a configuration file with "theme" set to "test-theme"
138 And I have a _data directory
139 And I have a "_data/i18n.yml" file with content:
140 """
141 testimonials:
142 header: Kundenstimmen
143 """
144 And I have an "index.html" page that contains "{% include testimonials.html %}"
145 When I run jekyll build
146 Then I should get a zero exit status
147 And the _site directory should exist
148 And I should not see "Testimonials" in "_site/index.html"
149 And I should see "Kundenstimmen" in "_site/index.html"
150 And I should see "Design by FTC" in "_site/index.html"
0 Feature: Bundling Config file with Theme gems
1 As a web developer who likes to share my expertise
2 I want to be able to pre-configure my gemified theme
3 In order to make it easier for other Jekyllites to use my theme
4
5 Scenario: Easy onboarding with a pre-configured theme
6 Given I have a configuration file with "theme" set to "test-theme"
7 And I have an "index.md" page that contains "{{ site.test_theme.skin }}"
8 When I run jekyll build
9 Then I should get a zero exit status
10 And the _site directory should exist
11 And I should see "aero" in "_site/index.html"
12
13 Scenario: Disabling import of theme configuration entirely
14 Given I have a configuration file with:
15 | key | value |
16 | theme | test-theme |
17 | ignore_theme_config | true |
18 And I have an "index.md" page that contains "{{ site.test_theme.skin }}"
19 When I run jekyll build
20 Then I should get a zero exit status
21 And the _site directory should exist
22 And I should not see "aero" in "_site/index.html"
23
24 Scenario: A pre-configured theme with valid config file overriding Jekyll defaults
25 Given I have a configuration file with "theme" set to "test-theme"
26 And I have an "index.md" page that contains "{{ site.baseurl }}"
27 And I have a node_modules directory
28 And I have a "node_modules/alert.js" file that contains "alert('foo');"
29 When I run jekyll build
30 Then I should get a zero exit status
31 And the _site directory should exist
32 And the "_site/index.html" file should exist
33 But the "_site/node_modules/alert.js" file should not exist
34 And the "_site/extras/banner.html" file should not exist
35 And I should not see "/test-theme" in "_site/index.html"
2424 And the "my-cool-theme-0.1.0/_includes/blank.html" file should exist
2525 And the "my-cool-theme-0.1.0/_sass/blank.scss" file should exist
2626 And the "my-cool-theme-0.1.0/assets/blank.scss" file should exist
27 And the "my-cool-theme-0.1.0/_config.yml" file should exist
2728 And the my-cool-theme-0.1.0/.git directory should not exist
2829 And the "my-cool-theme-0.1.0/.gitignore" file should not exist
2930 And the "my-cool-theme-0.1.0/Gemfile" file should not exist
00 # frozen_string_literal: true
11
2 lib = File.expand_path("lib", __dir__)
3 $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4 require "jekyll/version"
2 require "English"
3 require_relative "lib/jekyll/version"
54
65 Gem::Specification.new do |s|
7 s.specification_version = 2 if s.respond_to? :specification_version=
8 s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
9 s.rubygems_version = "2.2.2"
10 s.required_ruby_version = ">= 2.1.0"
11
126 s.name = "jekyll"
137 s.version = Jekyll::VERSION
148 s.license = "MIT"
15
9 s.authors = ["Tom Preston-Werner", "Parker Moore", "Matt Rogers"]
10 s.email = ["maintainers@jekyllrb.com"]
11 s.homepage = "https://jekyllrb.com"
1612 s.summary = "A simple, blog aware, static site generator."
1713 s.description = "Jekyll is a simple, blog aware, static site generator."
1814
19 s.authors = ["Tom Preston-Werner"]
20 s.email = "tom@mojombo.com"
21 s.homepage = "https://github.com/jekyll/jekyll"
22
23 all_files = `git ls-files -z`.split("\x0")
15 all_files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
2416 s.files = all_files.grep(%r!^(exe|lib|rubocop)/|^.rubocop.yml$!)
2517 s.executables = all_files.grep(%r!^exe/!) { |f| File.basename(f) }
2618 s.bindir = "exe"
2719 s.require_paths = ["lib"]
2820
29 s.rdoc_options = ["--charset=UTF-8"]
21 s.metadata = {
22 "source_code_uri" => "https://github.com/jekyll/jekyll",
23 "bug_tracker_uri" => "https://github.com/jekyll/jekyll/issues",
24 "changelog_uri" => "https://github.com/jekyll/jekyll/releases",
25 "homepage_uri" => s.homepage,
26 }
27
28 s.rdoc_options = ["--charset=UTF-8"]
3029 s.extra_rdoc_files = %w(README.markdown LICENSE)
30
31 s.required_ruby_version = ">= 2.5.0"
32 s.required_rubygems_version = ">= 2.7.0"
3133
3234 s.add_runtime_dependency("addressable", "~> 2.4")
3335 s.add_runtime_dependency("colorator", "~> 1.0")
3436 s.add_runtime_dependency("em-websocket", "~> 0.5")
35 s.add_runtime_dependency("i18n", "~> 0.7")
36 s.add_runtime_dependency("jekyll-sass-converter", "~> 1.0")
37 s.add_runtime_dependency("i18n", "~> 1.0")
38 s.add_runtime_dependency("jekyll-sass-converter", ">= 2.0", "< 4.0")
3739 s.add_runtime_dependency("jekyll-watch", "~> 2.0")
40 s.add_runtime_dependency("kramdown", "~> 2.3", ">= 2.3.1")
41 s.add_runtime_dependency("kramdown-parser-gfm", "~> 1.0")
3842 s.add_runtime_dependency("liquid", "~> 4.0")
39 s.add_runtime_dependency("mercenary", "~> 0.3.3")
43 s.add_runtime_dependency("mercenary", ">= 0.3.6", "< 0.5")
4044 s.add_runtime_dependency("pathutil", "~> 0.9")
41 rouge_versions = ENV["ROUGE_VERSION"] ? ["~> #{ENV["ROUGE_VERSION"]}"] : [">= 1.7", "< 4"]
42 s.add_runtime_dependency("rouge", *rouge_versions)
45 s.add_runtime_dependency("rouge", ">= 3.0", "< 5.0")
4346 s.add_runtime_dependency("safe_yaml", "~> 1.0")
44
45 # Depend on kramdown. For kramdown v2, extra gems are required.
46 # https://kramdown.gettalong.org/news.html#kramdown-200-released
47 kramdown_versions = ENV["KRAMDOWN_VERSION"] ? ["~> #{ENV["KRAMDOWN_VERSION"]}"] : [">= 1.17", "< 3"]
48 s.add_runtime_dependency("kramdown", *kramdown_versions)
47 s.add_runtime_dependency("terminal-table", ">= 1.8", "< 4.0")
48 s.add_runtime_dependency("webrick", "~> 1.7")
4949 end
0 url: "" # the base hostname & protocol for your site, e.g. http://example.com
1 baseurl: "" # the subpath of your site, e.g. /blog
2 title: "" # the name of your site, e.g. ACME Corp.
0 <!DOCTYPE html>
1 <html lang="{{ site.lang | default: "en-US" }}">
2 <head>
3 <meta name="viewport" content="width=device-width, initial-scale=1">
4 <meta charset="utf-8">
5 <title>{{ page.title }} - {{ site.title }}</title>
6 <link rel="stylesheet" href="{{ "/assets/css/main.css" | relative_url }}">
7 </head>
8 <body>
9 {{ content }}
10 </body>
11 </html>
0 $backgroundColor: #ffffff;
1 $bodyColor: #000000;
2 $bodyFont: -apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";
3
4 body {
5 background: $backgroundColor;
6 color: $bodyColor;
7 font-family: $bodyFont;
8 }
0 ---
1 ---
2
3 @import "main";
0 ---
1 layout: default
2 title: "Happy Jekylling!"
3 ---
4
5 ## You're ready to go!
6
7 Start developing your Jekyll website.
0 # frozen_string_literal: true
1
2 require "digest"
3
4 module Jekyll
5 class Cache
6 # class-wide base cache
7 @base_cache = {}
8
9 # class-wide directive to write cache to disk is enabled by default
10 @disk_cache_enabled = true
11
12 class << self
13 attr_accessor :cache_dir # class-wide cache location
14
15 attr_reader :base_cache, # class-wide base cache reader
16 :disk_cache_enabled # class-wide directive to write cache to disk
17
18 # Disable Marshaling cached items to disk
19 def disable_disk_cache!
20 @disk_cache_enabled = false
21 end
22
23 # Clear all caches
24 def clear
25 delete_cache_files
26 base_cache.each_value(&:clear)
27 end
28
29 # Compare the current config to the cached config
30 # If they are different, clear all caches
31 #
32 # Returns nothing.
33 def clear_if_config_changed(config)
34 config = config.inspect
35 cache = Jekyll::Cache.new "Jekyll::Cache"
36 return if cache.key?("config") && cache["config"] == config
37
38 clear
39 cache = Jekyll::Cache.new "Jekyll::Cache"
40 cache["config"] = config
41 nil
42 end
43
44 private
45
46 # Delete all cached items from all caches
47 #
48 # Returns nothing.
49 def delete_cache_files
50 FileUtils.rm_rf(@cache_dir) if disk_cache_enabled
51 end
52 end
53
54 #
55
56 # Get an existing named cache, or create a new one if none exists
57 #
58 # name - name of the cache
59 #
60 # Returns nothing.
61 def initialize(name)
62 @cache = Jekyll::Cache.base_cache[name] ||= {}
63 @name = name.gsub(%r![^\w\s-]!, "-")
64 end
65
66 # Clear this particular cache
67 def clear
68 delete_cache_files
69 @cache.clear
70 end
71
72 # Retrieve a cached item
73 # Raises if key does not exist in cache
74 #
75 # Returns cached value
76 def [](key)
77 return @cache[key] if @cache.key?(key)
78
79 path = path_to(hash(key))
80 if disk_cache_enabled? && File.file?(path) && File.readable?(path)
81 @cache[key] = load(path)
82 else
83 raise
84 end
85 end
86
87 # Add an item to cache
88 #
89 # Returns nothing.
90 def []=(key, value)
91 @cache[key] = value
92 return unless disk_cache_enabled?
93
94 path = path_to(hash(key))
95 value = new Hash(value) if value.is_a?(Hash) && !value.default.nil?
96 dump(path, value)
97 rescue TypeError
98 Jekyll.logger.debug "Cache:", "Cannot dump object #{key}"
99 end
100
101 # If an item already exists in the cache, retrieve it.
102 # Else execute code block, and add the result to the cache, and return that result.
103 def getset(key)
104 self[key]
105 rescue StandardError
106 value = yield
107 self[key] = value
108 value
109 end
110
111 # Remove one particular item from the cache
112 #
113 # Returns nothing.
114 def delete(key)
115 @cache.delete(key)
116 File.delete(path_to(hash(key))) if disk_cache_enabled?
117 end
118
119 # Check if `key` already exists in this cache
120 #
121 # Returns true if key exists in the cache, false otherwise
122 def key?(key)
123 # First, check if item is already cached in memory
124 return true if @cache.key?(key)
125 # Otherwise, it might be cached on disk
126 # but we should not consider the disk cache if it is disabled
127 return false unless disk_cache_enabled?
128
129 path = path_to(hash(key))
130 File.file?(path) && File.readable?(path)
131 end
132
133 def disk_cache_enabled?
134 !!Jekyll::Cache.disk_cache_enabled
135 end
136
137 private
138
139 # Given a hashed key, return the path to where this item would be saved on disk.
140 def path_to(hash = nil)
141 @base_dir ||= File.join(Jekyll::Cache.cache_dir, @name)
142 return @base_dir if hash.nil?
143
144 File.join(@base_dir, hash[0..1], hash[2..-1]).freeze
145 end
146
147 # Given a key, return a SHA2 hash that can be used for caching this item to disk.
148 def hash(key)
149 Digest::SHA2.hexdigest(key).freeze
150 end
151
152 # Remove all this caches items from disk
153 #
154 # Returns nothing.
155 def delete_cache_files
156 FileUtils.rm_rf(path_to) if disk_cache_enabled?
157 end
158
159 # Load `path` from disk and return the result.
160 # This MUST NEVER be called in Safe Mode
161 # rubocop:disable Security/MarshalLoad
162 def load(path)
163 raise unless disk_cache_enabled?
164
165 cached_file = File.open(path, "rb")
166 value = Marshal.load(cached_file)
167 cached_file.close
168 value
169 end
170 # rubocop:enable Security/MarshalLoad
171
172 # Given a path and a value, save value to disk at path.
173 # This should NEVER be called in Safe Mode
174 #
175 # Returns nothing.
176 def dump(path, value)
177 return unless disk_cache_enabled?
178
179 FileUtils.mkdir_p(File.dirname(path))
180 File.open(path, "wb") do |cached_file|
181 Marshal.dump(value, cached_file)
182 end
183 end
184 end
185 end
22 module Jekyll
33 # Handles the cleanup of a site's destination before it is built.
44 class Cleaner
5 HIDDEN_FILE_REGEX = %r!\/\.{1,2}$!
5 HIDDEN_FILE_REGEX = %r!/\.{1,2}$!.freeze
66 attr_reader :site
77
88 def initialize(site)
2828
2929 # Private: The metadata file storing dependency tree and build history
3030 #
31 # Returns an Array with the metdata file as the only item
31 # Returns an Array with the metadata file as the only item
3232 def metadata_file
3333 [site.regenerator.metadata_file]
3434 end
4343 dirs = keep_dirs
4444
4545 Utils.safe_glob(site.in_dest_dir, ["**", "*"], File::FNM_DOTMATCH).each do |file|
46 next if file =~ HIDDEN_FILE_REGEX || file =~ regex || dirs.include?(file)
46 next if HIDDEN_FILE_REGEX.match?(file) || regex.match?(file) || dirs.include?(file)
47
4748 files << file
4849 end
4950
6465 #
6566 # Returns a Set with the directory paths
6667 def new_dirs
67 @new_dirs ||= new_files.map { |file| parent_dirs(file) }.flatten.to_set
68 @new_dirs ||= new_files.flat_map { |file| parent_dirs(file) }.to_set
6869 end
6970
7071 # Private: The list of parent directories of a given file
7576 if parent_dir == site.dest
7677 []
7778 else
78 [parent_dir] + parent_dirs(parent_dir)
79 parent_dirs(parent_dir).unshift(parent_dir)
7980 end
8081 end
8182
9293 #
9394 # Returns a Set with the directory paths
9495 def keep_dirs
95 site.keep_files.map { |file| parent_dirs(site.in_dest_dir(file)) }.flatten.to_set
96 site.keep_files.flat_map { |file| parent_dirs(site.in_dest_dir(file)) }.to_set
9697 end
9798
9899 # Private: Creates a regular expression from the config's keep_files array
103104 #
104105 # Returns the regular expression
105106 def keep_file_regex
106 %r!\A#{Regexp.quote(site.dest)}\/(#{Regexp.union(site.keep_files).source})!
107 %r!\A#{Regexp.quote(site.dest)}/(#{Regexp.union(site.keep_files).source})!
107108 end
108109 end
109110 end
3434 def method_missing(method, *args, &blck)
3535 if docs.respond_to?(method.to_sym)
3636 Jekyll.logger.warn "Deprecation:",
37 "#{label}.#{method} should be changed to #{label}.docs.#{method}."
37 "#{label}.#{method} should be changed to #{label}.docs.#{method}."
3838 Jekyll.logger.warn "", "Called by #{caller(0..0)}."
3939 docs.public_send(method.to_sym, *args, &blck)
4040 else
5757 filtered_entries.each do |file_path|
5858 full_path = collection_dir(file_path)
5959 next if File.directory?(full_path)
60
6061 if Utils.has_yaml_header? full_path
6162 read_document(full_path)
6263 else
6364 read_static_file(file_path, full_path)
6465 end
6566 end
66 docs.sort!
67 site.static_files.concat(files) unless files.empty?
68 sort_docs!
6769 end
6870
6971 # All the entries in this collection.
7274 # relative to the collection's directory
7375 def entries
7476 return [] unless exists?
75 @entries ||=
77
78 @entries ||= begin
79 collection_dir_slash = "#{collection_dir}/"
7680 Utils.safe_glob(collection_dir, ["**", "*"], File::FNM_DOTMATCH).map do |entry|
77 entry["#{collection_dir}/"] = ""
81 entry[collection_dir_slash] = ""
7882 entry
7983 end
84 end
8085 end
8186
8287 # Filtered version of the entries in this collection.
8590 # Returns a list of filtered entry paths.
8691 def filtered_entries
8792 return [] unless exists?
93
8894 @filtered_entries ||=
8995 Dir.chdir(directory) do
9096 entry_filter.filter(entries).reject do |f|
123129 # is stored on the filesystem.
124130 def collection_dir(*files)
125131 return directory if files.empty?
132
126133 site.in_source_dir(container, relative_directory, *files)
127134 end
128135
148155 #
149156 # Returns the inspect string
150157 def inspect
151 "#<Jekyll::Collection @label=#{label} docs=#{docs}>"
158 "#<#{self.class} @label=#{label} docs=#{docs}>"
152159 end
153160
154161 # Produce a sanitized label name
159166 #
160167 # Returns a sanitized version of the label.
161168 def sanitize_label(label)
162 label.gsub(%r![^a-z0-9_\-\.]!i, "")
169 label.gsub(%r![^a-z0-9_\-.]!i, "")
163170 end
164171
165172 # Produce a representation of this Collection for use in Liquid.
206213 @container ||= site.config["collections_dir"]
207214 end
208215
209 private
210
211216 def read_document(full_path)
212217 doc = Document.new(full_path, :site => site, :collection => self)
213218 doc.read
214 if site.unpublished || doc.published?
215 docs << doc
216 end
217 end
218
219 private
219 docs << doc if site.unpublished || doc.published?
220 end
221
222 def sort_docs!
223 if metadata["order"].is_a?(Array)
224 rearrange_docs!
225 elsif metadata["sort_by"].is_a?(String)
226 sort_docs_by_key!
227 else
228 docs.sort!
229 end
230 end
231
232 # A custom sort function based on Schwartzian transform
233 # Refer https://byparker.com/blog/2017/schwartzian-transform-faster-sorting/ for details
234 def sort_docs_by_key!
235 meta_key = metadata["sort_by"]
236 # Modify `docs` array to cache document's property along with the Document instance
237 docs.map! { |doc| [doc.data[meta_key], doc] }.sort! do |apples, olives|
238 order = determine_sort_order(meta_key, apples, olives)
239
240 # Fall back to `Document#<=>` if the properties were equal or were non-sortable
241 # Otherwise continue with current sort-order
242 if order.nil? || order.zero?
243 apples[-1] <=> olives[-1]
244 else
245 order
246 end
247
248 # Finally restore the `docs` array with just the Document objects themselves
249 end.map!(&:last)
250 end
251
252 def determine_sort_order(sort_key, apples, olives)
253 apple_property, apple_document = apples
254 olive_property, olive_document = olives
255
256 if apple_property.nil? && !olive_property.nil?
257 order_with_warning(sort_key, apple_document, 1)
258 elsif !apple_property.nil? && olive_property.nil?
259 order_with_warning(sort_key, olive_document, -1)
260 else
261 apple_property <=> olive_property
262 end
263 end
264
265 def order_with_warning(sort_key, document, order)
266 Jekyll.logger.warn "Sort warning:", "'#{sort_key}' not defined in #{document.relative_path}"
267 order
268 end
269
270 # Rearrange documents within the `docs` array as listed in the `metadata["order"]` array.
271 #
272 # Involves converting the two arrays into hashes based on relative_paths as keys first, then
273 # merging them to remove duplicates and finally retrieving the Document instances from the
274 # merged array.
275 def rearrange_docs!
276 docs_table = {}
277 custom_order = {}
278
279 # pre-sort to normalize default array across platforms and then proceed to create a Hash
280 # from that sorted array.
281 docs.sort.each do |doc|
282 docs_table[doc.relative_path] = doc
283 end
284
285 metadata["order"].each do |entry|
286 custom_order[File.join(relative_directory, entry)] = nil
287 end
288
289 result = Jekyll::Utils.deep_merge_hashes(custom_order, docs_table).values
290 result.compact!
291 self.docs = result
292 end
220293
221294 def read_static_file(file_path, full_path)
222295 relative_dir = Jekyll.sanitized_path(
3939 # Returns a full Jekyll configuration
4040 def configuration_from_options(options)
4141 return options if options.is_a?(Jekyll::Configuration)
42
4243 Jekyll.configuration(options)
4344 end
4445
5051 # rubocop:disable Metrics/MethodLength
5152 def add_build_options(cmd)
5253 cmd.option "config", "--config CONFIG_FILE[,CONFIG_FILE2,...]",
53 Array, "Custom configuration file"
54 Array, "Custom configuration file"
5455 cmd.option "destination", "-d", "--destination DESTINATION",
55 "The current folder will be generated into DESTINATION"
56 "The current folder will be generated into DESTINATION"
5657 cmd.option "source", "-s", "--source SOURCE", "Custom source directory"
5758 cmd.option "future", "--future", "Publishes posts with a future date"
5859 cmd.option "limit_posts", "--limit_posts MAX_POSTS", Integer,
59 "Limits the number of posts to parse and publish"
60 "Limits the number of posts to parse and publish"
6061 cmd.option "watch", "-w", "--[no-]watch", "Watch for changes and rebuild"
6162 cmd.option "baseurl", "-b", "--baseurl URL",
62 "Serve the website from the given base URL"
63 "Serve the website from the given base URL"
6364 cmd.option "force_polling", "--force_polling", "Force watch to use polling"
6465 cmd.option "lsi", "--lsi", "Use LSI for improved related posts"
6566 cmd.option "show_drafts", "-D", "--drafts", "Render posts in the _drafts folder"
6667 cmd.option "unpublished", "--unpublished",
67 "Render posts that were marked as unpublished"
68 "Render posts that were marked as unpublished"
69 cmd.option "disable_disk_cache", "--disable-disk-cache",
70 "Disable caching to disk in non-safe mode"
6871 cmd.option "quiet", "-q", "--quiet", "Silence output."
6972 cmd.option "verbose", "-V", "--verbose", "Print verbose output."
7073 cmd.option "incremental", "-I", "--incremental", "Enable incremental rebuild."
7174 cmd.option "strict_front_matter", "--strict_front_matter",
72 "Fail if errors are present in front matter"
75 "Fail if errors are present in front matter"
7376 end
7477 # rubocop:enable Metrics/MethodLength
78
79 # Run ::process method in a given set of Jekyll::Command subclasses and suggest
80 # re-running the associated command with --trace switch to obtain any additional
81 # information or backtrace regarding the encountered Exception.
82 #
83 # cmd - the Jekyll::Command to be handled
84 # options - configuration overrides
85 # klass - an array of Jekyll::Command subclasses associated with the command
86 #
87 # Note that all exceptions are rescued..
88 # rubocop: disable Lint/RescueException
89 def process_with_graceful_fail(cmd, options, *klass)
90 klass.each { |k| k.process(options) if k.respond_to?(:process) }
91 rescue Exception => e
92 raise e if cmd.trace
93
94 msg = " Please append `--trace` to the `#{cmd.name}` command "
95 dashes = "-" * msg.length
96 Jekyll.logger.error "", dashes
97 Jekyll.logger.error "Jekyll #{Jekyll::VERSION} ", msg
98 Jekyll.logger.error "", " for any additional information or backtrace. "
99 Jekyll.logger.abort_with "", dashes
100 end
101 # rubocop: enable Lint/RescueException
75102 end
76103 end
77104 end
1414
1515 c.action do |_, options|
1616 options["serving"] = false
17 Jekyll::Commands::Build.process(options)
17 process_with_graceful_fail(c, options, self)
1818 end
1919 end
2020 end
2929 site = Jekyll::Site.new(options)
3030
3131 if options.fetch("skip_initial_build", false)
32 Jekyll.logger.warn "Build Warning:", "Skipping the initial build." \
33 " This may result in an out-of-date site."
32 Jekyll.logger.warn "Build Warning:",
33 "Skipping the initial build. This may result in an out-of-date site."
3434 else
3535 build(site, options)
3636 end
3737
3838 if options.fetch("detach", false)
3939 Jekyll.logger.info "Auto-regeneration:",
40 "disabled when running server detached."
40 "disabled when running server detached."
4141 elsif options.fetch("watch", false)
4242 watch(site, options)
4343 else
5353 # Returns nothing.
5454 def build(site, options)
5555 t = Time.now
56 source = options["source"]
57 destination = options["destination"]
56 source = File.expand_path(options["source"])
57 destination = File.expand_path(options["destination"])
5858 incremental = options["incremental"]
5959 Jekyll.logger.info "Source:", source
6060 Jekyll.logger.info "Destination:", destination
6161 Jekyll.logger.info "Incremental build:",
62 (incremental ? "enabled" : "disabled. Enable with --incremental")
62 (incremental ? "enabled" : "disabled. Enable with --incremental")
6363 Jekyll.logger.info "Generating..."
6464 process_site(site)
6565 Jekyll.logger.info "", "done in #{(Time.now - t).round(3)} seconds."
7272 #
7373 # Returns nothing.
7474 def watch(site, options)
75 # Warn Windows users that they might need to upgrade.
76 if Utils::Platforms.bash_on_windows?
77 Jekyll.logger.warn "",
78 "Auto-regeneration may not work on some Windows versions."
79 Jekyll.logger.warn "",
80 "Please see: https://github.com/Microsoft/BashOnWindows/issues/216"
81 Jekyll.logger.warn "",
82 "If it does not work, please upgrade Bash on Windows or "\
83 "run Jekyll with --no-watch."
84 end
85
8675 External.require_with_graceful_fail "jekyll-watch"
87 watch_method = Jekyll::Watcher.method(:watch)
88 if watch_method.parameters.size == 1
89 watch_method.call(
90 options
91 )
92 else
93 watch_method.call(
94 options, site
95 )
96 end
76 Jekyll::Watcher.watch(options, site)
9777 end
9878 end
9979 end
66 def init_with_program(prog)
77 prog.command(:clean) do |c|
88 c.syntax "clean [subcommand]"
9 c.description "Clean the site " \
10 "(removes site output and metadata file) without building."
9 c.description "Clean the site (removes site output and metadata file) without building."
1110
1211 add_build_options(c)
1312
2120 options = configuration_from_options(options)
2221 destination = options["destination"]
2322 metadata_file = File.join(options["source"], ".jekyll-metadata")
23 cache_dir = File.join(options["source"], options["cache_dir"])
2424 sass_cache = ".sass-cache"
2525
2626 remove(destination, :checker_func => :directory?)
2727 remove(metadata_file, :checker_func => :file?)
28 remove(cache_dir, :checker_func => :directory?)
2829 remove(sass_cache, :checker_func => :directory?)
2930 end
3031
1010 c.alias(:hyde)
1111
1212 c.option "config", "--config CONFIG_FILE[,CONFIG_FILE2,...]", Array,
13 "Custom configuration file"
13 "Custom configuration file"
1414
1515 c.action do |_, options|
1616 Jekyll::Commands::Doctor.process(options)
4444
4545 def properly_gathered_posts?(site)
4646 return true if site.config["collections_dir"].empty?
47
4748 posts_at_root = site.in_source_dir("_posts")
4849 return true unless File.directory?(posts_at_root)
50
4951 Jekyll.logger.warn "Warning:",
50 "Detected '_posts' directory outside custom `collections_dir`!"
52 "Detected '_posts' directory outside custom `collections_dir`!"
5153 Jekyll.logger.warn "",
52 "Please move '#{posts_at_root}' into the custom directory at " \
53 "'#{site.in_source_dir(site.config["collections_dir"])}'"
54 "Please move '#{posts_at_root}' into the custom directory at " \
55 "'#{site.in_source_dir(site.config["collections_dir"])}'"
5456 false
5557 end
5658
5759 def deprecated_relative_permalinks(site)
5860 if site.config["relative_permalinks"]
59 Jekyll::Deprecator.deprecation_message "Your site still uses relative" \
60 " permalinks, which was removed in" \
61 " Jekyll v3.0.0."
62 return true
61 Jekyll::Deprecator.deprecation_message "Your site still uses relative permalinks, " \
62 "which was removed in Jekyll v3.0.0."
63 true
6364 end
6465 end
6566
6667 def conflicting_urls(site)
6768 conflicting_urls = false
68 urls = {}
69 urls = collect_urls(urls, site.pages, site.dest)
70 urls = collect_urls(urls, site.posts.docs, site.dest)
71 urls.each do |url, paths|
69 destination_map(site).each do |dest, paths|
7270 next unless paths.size > 1
71
7372 conflicting_urls = true
74 Jekyll.logger.warn "Conflict:", "The URL '#{url}' is the destination" \
75 " for the following pages: #{paths.join(", ")}"
73 Jekyll.logger.warn "Conflict:",
74 "The following destination is shared by multiple files."
75 Jekyll.logger.warn "", "The written file may end up with unexpected contents."
76 Jekyll.logger.warn "", dest.to_s.cyan
77 paths.each { |path| Jekyll.logger.warn "", " - #{path}" }
78 Jekyll.logger.warn ""
7679 end
7780 conflicting_urls
7881 end
7982
8083 def fsnotify_buggy?(_site)
8184 return true unless Utils::Platforms.osx?
85
8286 if Dir.pwd != `pwd`.strip
83 Jekyll.logger.error " " + <<-STR.strip.gsub(%r!\n\s+!, "\n ")
87 Jekyll.logger.error <<~STR
8488 We have detected that there might be trouble using fsevent on your
8589 operating system, you can read https://github.com/thibaudgg/rb-fsevent/wiki/no-fsevents-fired-(OSX-bug)
8690 for possible work arounds or you can work around it immediately
98102 urls = case_insensitive_urls(site.pages + site.docs_to_write, site.dest)
99103 urls.each_value do |real_urls|
100104 next unless real_urls.uniq.size > 1
105
101106 urls_only_differ_by_case = true
102 Jekyll.logger.warn "Warning:", "The following URLs only differ" \
103 " by case. On a case-insensitive file system one of the URLs" \
104 " will be overwritten by the other: #{real_urls.join(", ")}"
107 Jekyll.logger.warn "Warning:", "The following URLs only differ by case. On a " \
108 "case-insensitive file system one of the URLs will be " \
109 "overwritten by the other: #{real_urls.join(", ")}"
105110 end
106111 urls_only_differ_by_case
107112 end
116121 end
117122
118123 private
119 def collect_urls(urls, things, destination)
120 things.each do |thing|
121 dest = thing.destination(destination)
122 if urls[dest]
123 urls[dest] << thing.path
124 else
125 urls[dest] = [thing.path]
124
125 def destination_map(site)
126 {}.tap do |result|
127 site.each_site_file do |thing|
128 next if allow_used_permalink?(thing)
129
130 dest_path = thing.destination(site.dest)
131 (result[dest_path] ||= []) << thing.path
126132 end
127133 end
128 urls
134 end
135
136 def allow_used_permalink?(item)
137 defined?(JekyllRedirectFrom) && item.is_a?(JekyllRedirectFrom::RedirectPage)
129138 end
130139
131140 def case_insensitive_urls(things, destination)
137146
138147 def url_exists?(url)
139148 return true unless url.nil? || url.empty?
140 Jekyll.logger.warn "Warning:", "You didn't set an URL in the config file, "\
141 "you may encounter problems with some plugins."
149
150 Jekyll.logger.warn "Warning:", "You didn't set an URL in the config file, you may " \
151 "encounter problems with some plugins."
142152 false
143153 end
144154
146156 Addressable::URI.parse(url)
147157 true
148158 # Addressable::URI#parse only raises a TypeError
149 # https://git.io/vFfbx
159 # https://github.com/sporkmonger/addressable/blob/0a0e96acb17225f9b1c9cab0bad332b448934c9a/lib/addressable/uri.rb#L103
150160 rescue TypeError
151 Jekyll.logger.warn "Warning:", "The site URL does not seem to be valid, "\
152 "check the value of `url` in your config file."
161 Jekyll.logger.warn "Warning:", "The site URL does not seem to be valid, " \
162 "check the value of `url` in your config file."
153163 false
154164 end
155165
156166 def url_absolute(url)
157 return true if Addressable::URI.parse(url).absolute?
158 Jekyll.logger.warn "Warning:", "Your site URL does not seem to be absolute, "\
159 "check the value of `url` in your config file."
167 return true if url.is_a?(String) && Addressable::URI.parse(url).absolute?
168
169 Jekyll.logger.warn "Warning:", "Your site URL does not seem to be absolute, " \
170 "check the value of `url` in your config file."
160171 false
161172 end
162173 end
2424
2525 def invalid_command(prog, cmd)
2626 Jekyll.logger.error "Error:",
27 "Hmm... we don't know what the '#{cmd}' command is."
27 "Hmm... we don't know what the '#{cmd}' command is."
2828 Jekyll.logger.info "Valid commands:", prog.commands.keys.join(", ")
2929 end
3030 end
2727 FileUtils.mkdir_p new_blog_path
2828 if preserve_source_location?(new_blog_path, options)
2929 Jekyll.logger.error "Conflict:", "#{new_blog_path} exists and is not empty."
30 Jekyll.logger.abort_with "", "Ensure #{new_blog_path} is empty or else " \
31 "try again with `--force` to proceed and overwrite any files."
30 Jekyll.logger.abort_with "", "Ensure #{new_blog_path} is empty or else try again " \
31 "with `--force` to proceed and overwrite any files."
3232 end
3333
3434 if options["blank"]
4040 after_install(new_blog_path, options)
4141 end
4242
43 def blank_template
44 File.expand_path("../../blank_template", __dir__)
45 end
46
4347 def create_blank_site(path)
48 FileUtils.cp_r blank_template + "/.", path
49 FileUtils.chmod_R "u+w", path
50
4451 Dir.chdir(path) do
45 FileUtils.mkdir(%w(_layouts _posts _drafts))
46 FileUtils.touch("index.html")
52 FileUtils.mkdir(%w(_data _drafts _includes _posts))
4753 end
4854 end
4955
6167 private
6268
6369 def gemfile_contents
64 <<-RUBY
65 source "https://rubygems.org"
70 <<~RUBY
71 source "https://rubygems.org"
72 # Hello! This is where you manage which Jekyll version is used to run.
73 # When you want to use a different version, change it below, save the
74 # file and run `bundle install`. Run Jekyll with `bundle exec`, like so:
75 #
76 # bundle exec jekyll serve
77 #
78 # This will help ensure the proper Jekyll version is running.
79 # Happy Jekylling!
80 gem "jekyll", "~> #{Jekyll::VERSION}"
81 # This is the default theme for new Jekyll sites. You may change this to anything you like.
82 gem "minima", "~> 2.5"
83 # If you want to use GitHub Pages, remove the "gem "jekyll"" above and
84 # uncomment the line below. To upgrade, run `bundle update github-pages`.
85 # gem "github-pages", group: :jekyll_plugins
86 # If you have any plugins, put them here!
87 group :jekyll_plugins do
88 gem "jekyll-feed", "~> 0.12"
89 end
6690
67 # Hello! This is where you manage which Jekyll version is used to run.
68 # When you want to use a different version, change it below, save the
69 # file and run `bundle install`. Run Jekyll with `bundle exec`, like so:
70 #
71 # bundle exec jekyll serve
72 #
73 # This will help ensure the proper Jekyll version is running.
74 # Happy Jekylling!
75 gem "jekyll", "~> #{Jekyll::VERSION}"
91 # Windows and JRuby does not include zoneinfo files, so bundle the tzinfo-data gem
92 # and associated library.
93 platforms :mingw, :x64_mingw, :mswin, :jruby do
94 gem "tzinfo", ">= 1", "< 3"
95 gem "tzinfo-data"
96 end
7697
77 # This is the default theme for new Jekyll sites. You may change this to anything you like.
78 gem "minima", "~> 2.0"
98 # Performance-booster for watching directories on Windows
99 gem "wdm", "~> 0.1.1", :platforms => [:mingw, :x64_mingw, :mswin]
79100
80 # If you want to use GitHub Pages, remove the "gem "jekyll"" above and
81 # uncomment the line below. To upgrade, run `bundle update github-pages`.
82 # gem "github-pages", group: :jekyll_plugins
83
84 # If you have any plugins, put them here!
85 group :jekyll_plugins do
86 gem "jekyll-feed", "~> 0.6"
87 end
88
89 # Windows does not include zoneinfo files, so bundle the tzinfo-data gem
90 # and associated library.
91 install_if -> { RUBY_PLATFORM =~ %r!mingw|mswin|java! } do
92 gem "tzinfo", "~> 1.2"
93 gem "tzinfo-data"
94 end
95
96 # Performance-booster for watching directories on Windows
97 gem "wdm", "~> 0.1.0", :install_if => Gem.win_platform?
98
99 # kramdown v2 ships without the gfm parser by default. If you're using
100 # kramdown v1, comment out this line.
101 gem "kramdown-parser-gfm"
102
103 RUBY
101 # Lock `http_parser.rb` gem to `v0.6.x` on JRuby builds since newer versions of the gem
102 # do not have a Java counterpart.
103 gem "http_parser.rb", "~> 0.6.0", :platforms => [:jruby]
104 RUBY
104105 end
105106
106107 def create_site(new_blog_path)
107108 create_sample_files new_blog_path
108109
109 File.open(File.expand_path(initialized_post_name, new_blog_path), "w") do |f|
110 f.write(scaffold_post_content)
111 end
110 File.write(File.expand_path(initialized_post_name, new_blog_path), scaffold_post_content)
112111
113 File.open(File.expand_path("Gemfile", new_blog_path), "w") do |f|
114 f.write(gemfile_contents)
115 end
112 File.write(File.expand_path("Gemfile", new_blog_path), gemfile_contents)
116113 end
117114
118115 def preserve_source_location?(path, options)
11
22 require "erb"
33
4 class Jekyll::Commands::NewTheme < Jekyll::Command
5 class << self
6 def init_with_program(prog)
7 prog.command(:"new-theme") do |c|
8 c.syntax "new-theme NAME"
9 c.description "Creates a new Jekyll theme scaffold"
10 c.option "code_of_conduct", \
11 "-c", "--code-of-conduct", \
12 "Include a Code of Conduct. (defaults to false)"
4 module Jekyll
5 module Commands
6 class NewTheme < Jekyll::Command
7 class << self
8 def init_with_program(prog)
9 prog.command(:"new-theme") do |c|
10 c.syntax "new-theme NAME"
11 c.description "Creates a new Jekyll theme scaffold"
12 c.option "code_of_conduct", "-c", "--code-of-conduct",
13 "Include a Code of Conduct. (defaults to false)"
1314
14 c.action do |args, opts|
15 Jekyll::Commands::NewTheme.process(args, opts)
15 c.action do |args, opts|
16 Jekyll::Commands::NewTheme.process(args, opts)
17 end
18 end
19 end
20
21 def process(args, opts)
22 if !args || args.empty?
23 raise Jekyll::Errors::InvalidThemeName, "You must specify a theme name."
24 end
25
26 new_theme_name = args.join("_")
27 theme = Jekyll::ThemeBuilder.new(new_theme_name, opts)
28 Jekyll.logger.abort_with "Conflict:", "#{theme.path} already exists." if theme.path.exist?
29
30 theme.create!
31 Jekyll.logger.info "Your new Jekyll theme, #{theme.name.cyan}, " \
32 "is ready for you in #{theme.path.to_s.cyan}!"
33 Jekyll.logger.info "For help getting started, read #{theme.path}/README.md."
1634 end
1735 end
1836 end
19
20 # rubocop:disable Metrics/AbcSize
21 def process(args, opts)
22 if !args || args.empty?
23 raise Jekyll::Errors::InvalidThemeName, "You must specify a theme name."
24 end
25
26 new_theme_name = args.join("_")
27 theme = Jekyll::ThemeBuilder.new(new_theme_name, opts)
28 if theme.path.exist?
29 Jekyll.logger.abort_with "Conflict:", "#{theme.path} already exists."
30 end
31
32 theme.create!
33 Jekyll.logger.info "Your new Jekyll theme, #{theme.name.cyan}," \
34 " is ready for you in #{theme.path.to_s.cyan}!"
35 Jekyll.logger.info "For help getting started, read #{theme.path}/README.md."
36 end
37 # rubocop:enable Metrics/AbcSize
3837 end
3938 end
77 module Commands
88 class Serve
99 class LiveReloadReactor
10 attr_reader :started_event
11 attr_reader :stopped_event
12 attr_reader :thread
10 attr_reader :started_event, :stopped_event, :thread
1311
1412 def initialize
1513 @websockets = []
5755 EM.add_shutdown_hook { @stopped_event.set }
5856
5957 Jekyll.logger.info "LiveReload address:",
60 "http://#{opts["host"]}:#{opts["livereload_port"]}"
58 "http://#{opts["host"]}:#{opts["livereload_port"]}"
6159 end
6260 end
6361 @thread.abort_on_exception = true
6765 # http://feedback.livereload.com/knowledgebase/articles/86174-livereload-protocol
6866 def reload(pages)
6967 pages.each do |p|
70 json_message = JSON.dump({
68 json_message = JSON.dump(
7169 :command => "reload",
7270 :path => p.url,
73 :liveCSS => true,
74 })
71 :liveCSS => true
72 )
7573
76 Jekyll.logger.debug "LiveReload:", "Reloading #{p.url}"
77 Jekyll.logger.debug "", json_message
74 Jekyll.logger.debug "LiveReload:", "Reloading URL #{p.url.inspect}"
7875 @websockets.each { |ws| ws.send(json_message) }
7976 end
8077 end
8178
8279 private
80
8381 def connect(websocket, handshake)
8482 @connections_count += 1
8583 if @connections_count == 1
9896 @websockets << websocket
9997 end
10098
101 private
10299 def disconnect(websocket)
103100 @websockets.delete(websocket)
104101 end
105102
106 private
107103 def print_message(json_message)
108104 msg = JSON.parse(json_message)
109105 # Not sure what the 'url' command even does in LiveReload. The spec is silent
110106 # on its purpose.
111 if msg["command"] == "url"
112 Jekyll.logger.info "LiveReload:", "Browser URL: #{msg["url"]}"
113 end
107 Jekyll.logger.info "LiveReload:", "Browser URL: #{msg["url"]}" if msg["command"] == "url"
114108 end
115109
116 private
117110 def log_error(error)
118111 Jekyll.logger.error "LiveReload experienced an error. " \
119 "Run with --trace for more information."
112 "Run with --trace for more information."
120113 raise error
121114 end
122115 end
0 {
1 "application/javascript": "UTF-8",
2 "application/json": "UTF-8",
3 "application/manifest+json": "UTF-8",
4 "application/vnd.syncml+xml": "UTF-8",
5 "application/vnd.syncml.dm+wbxml": "UTF-8",
6 "application/vnd.syncml.dm+xml": "UTF-8",
7 "application/vnd.syncml.dmddf+xml": "UTF-8",
8 "application/vnd.wap.wbxml": "UTF-8",
9 "text/cache-manifest": "UTF-8",
10 "text/calendar": "UTF-8",
11 "text/coffeescript": "UTF-8",
12 "text/css": "UTF-8",
13 "text/csv": "UTF-8",
14 "text/html": "UTF-8",
15 "text/jade": "UTF-8",
16 "text/jsx": "UTF-8",
17 "text/less": "UTF-8",
18 "text/markdown": "UTF-8",
19 "text/mathml": "UTF-8",
20 "text/mdx": "UTF-8",
21 "text/n3": "UTF-8",
22 "text/plain": "UTF-8",
23 "text/prs.lines.tag": "UTF-8",
24 "text/richtext": "UTF-8",
25 "text/sgml": "UTF-8",
26 "text/shex": "UTF-8",
27 "text/slim": "UTF-8",
28 "text/spdx": "UTF-8",
29 "text/stylus": "UTF-8",
30 "text/tab-separated-values": "UTF-8",
31 "text/troff": "UTF-8",
32 "text/turtle": "UTF-8",
33 "text/uri-list": "UTF-8",
34 "text/vcard": "UTF-8",
35 "text/vnd.curl": "UTF-8",
36 "text/vnd.curl.dcurl": "UTF-8",
37 "text/vnd.curl.mcurl": "UTF-8",
38 "text/vnd.curl.scurl": "UTF-8",
39 "text/vnd.familysearch.gedcom": "UTF-8",
40 "text/vnd.fly": "UTF-8",
41 "text/vnd.fmi.flexstor": "UTF-8",
42 "text/vnd.graphviz": "UTF-8",
43 "text/vnd.in3d.3dml": "UTF-8",
44 "text/vnd.in3d.spot": "UTF-8",
45 "text/vnd.sun.j2me.app-descriptor": "UTF-8",
46 "text/vnd.wap.wml": "UTF-8",
47 "text/vnd.wap.wmlscript": "UTF-8",
48 "text/vtt": "UTF-8",
49 "text/x-asm": "UTF-8",
50 "text/x-c": "UTF-8",
51 "text/x-component": "UTF-8",
52 "text/x-fortran": "UTF-8",
53 "text/x-handlebars-template": "UTF-8",
54 "text/x-java-source": "UTF-8",
55 "text/x-lua": "UTF-8",
56 "text/x-markdown": "UTF-8",
57 "text/x-nfo": "UTF-8",
58 "text/x-opml": "UTF-8",
59 "text/x-pascal": "UTF-8",
60 "text/x-processing": "UTF-8",
61 "text/x-sass": "UTF-8",
62 "text/x-scss": "UTF-8",
63 "text/x-setext": "UTF-8",
64 "text/x-sfv": "UTF-8",
65 "text/x-suse-ymp": "UTF-8",
66 "text/x-uuencode": "UTF-8",
67 "text/x-vcalendar": "UTF-8",
68 "text/x-vcard": "UTF-8",
69 "text/yaml": "UTF-8"
70 }
2828 end
2929
3030 def inline?
31 @response["Content-Disposition"] =~ %r!^inline!
31 @response["Content-Disposition"].to_s.start_with?("inline")
3232 end
3333
3434 def bad_browser?
35 BAD_USER_AGENTS.any? { |pattern| @request["User-Agent"] =~ pattern }
35 BAD_USER_AGENTS.any? { |pattern| pattern.match?(@request["User-Agent"]) }
3636 end
3737
3838 def html?
39 @response["Content-Type"] =~ %r!text/html!
39 @response["Content-Type"].to_s.include?("text/html")
4040 end
4141 end
4242
4343 # This class inserts the LiveReload script tags into HTML as it is served
4444 class BodyProcessor
45 HEAD_TAG_REGEX = %r!<head>|<head[^(er)][^<]*>!
45 HEAD_TAG_REGEX = %r!<head>|<head[^(er)][^<]*>!.freeze
4646
4747 attr_reader :content_length, :new_body, :livereload_added
4848
9797 # Complicated JavaScript to ensure that livereload.js is loaded from the
9898 # same origin as the page. Mostly useful for dealing with the browser's
9999 # distinction between 'localhost' and 127.0.0.1
100 template = <<-TEMPLATE
101 <script>
102 document.write(
103 '<script src="http://' +
104 (location.host || 'localhost').split(':')[0] +
105 ':<%=@options["livereload_port"] %>/livereload.js?snipver=1<%= livereload_args %>"' +
106 '></' +
107 'script>');
108 </script>
100 @template ||= ERB.new(<<~TEMPLATE)
101 <script>
102 document.write(
103 '<script src="' + location.protocol + '//' +
104 (location.host || 'localhost').split(':')[0] +
105 ':<%=@options["livereload_port"] %>/livereload.js?snipver=1<%= livereload_args %>"' +
106 '></' +
107 'script>');
108 </script>
109109 TEMPLATE
110 ERB.new(Jekyll::Utils.strip_heredoc(template))
111110 end
112111
113112 def livereload_args
120119 if @options["livereload_max_delay"]
121120 src += "&amp;maxdelay=#{@options["livereload_max_delay"]}"
122121 end
123 if @options["livereload_port"]
124 src += "&amp;port=#{@options["livereload_port"]}"
125 end
122 src += "&amp;port=#{@options["livereload_port"]}" if @options["livereload_port"]
126123 src
127124 end
128125 end
130127 class Servlet < WEBrick::HTTPServlet::FileHandler
131128 DEFAULTS = {
132129 "Cache-Control" => "private, max-age=0, proxy-revalidate, " \
133 "no-store, no-cache, must-revalidate",
130 "no-store, no-cache, must-revalidate",
134131 }.freeze
135132
136133 def initialize(server, root, callbacks)
137134 # So we can access them easily.
138135 @jekyll_opts = server.config[:JekyllOptions]
136 @mime_types_charset = server.config[:MimeTypesCharset]
139137 set_defaults
140138 super
141139 end
142140
143141 def search_index_file(req, res)
144 super || search_file(req, res, ".html")
142 super ||
143 search_file(req, res, ".html") ||
144 search_file(req, res, ".xhtml")
145145 end
146146
147147 # Add the ability to tap file.html the same way that Nginx does on our
150150
151151 def search_file(req, res, basename)
152152 # /file.* > /file/index.html > /file.html
153 super || super(req, res, "#{basename}.html")
153 super ||
154 super(req, res, "#{basename}.html") ||
155 super(req, res, "#{basename}.xhtml")
154156 end
155157
156158 # rubocop:disable Naming/MethodName
171173 end
172174 end
173175
174 validate_and_ensure_charset(req, res)
176 conditionally_inject_charset(res)
175177 res.header.merge!(@headers)
176178 rtn
177179 end
178180 # rubocop:enable Naming/MethodName
179181
182 private
183
184 # Inject charset based on Jekyll config only if our mime-types database contains
185 # the charset metadata.
180186 #
181
182 private
183 def validate_and_ensure_charset(_req, res)
184 key = res.header.keys.grep(%r!content-type!i).first
185 typ = res.header[key]
186
187 unless typ =~ %r!;\s*charset=!
188 res.header[key] = "#{typ}; charset=#{@jekyll_opts["encoding"]}"
189 end
190 end
191
192 #
193
194 private
187 # Refer `script/vendor-mimes` in the repository for further details.
188 def conditionally_inject_charset(res)
189 typ = res.header["content-type"]
190 return unless @mime_types_charset.key?(typ)
191 return if %r!;\s*charset=!.match?(typ)
192
193 res.header["content-type"] = "#{typ}; charset=#{@jekyll_opts["encoding"]}"
194 end
195
195196 def set_defaults
196197 hash_ = @jekyll_opts.fetch("webrick", {}).fetch("headers", {})
197198 DEFAULTS.each_with_object(@headers = hash_) do |(key, val), hash|
4545 # WebSockets requests will have a Connection: Upgrade header
4646 if parser.http_method != "GET" || parser.upgrade?
4747 super
48 elsif parser.request_url =~ %r!^\/livereload.js!
48 elsif parser.request_url.start_with?("/livereload.js")
4949 headers = [
5050 "HTTP/1.1 200 OK",
5151 "Content-Type: application/javascript",
00 # frozen_string_literal: true
1
2 require "thread"
31
42 module Jekyll
53 module Commands
1816 "host" => ["host", "-H", "--host [HOST]", "Host to bind to"],
1917 "open_url" => ["-o", "--open-url", "Launch your site in a browser"],
2018 "detach" => ["-B", "--detach",
21 "Run the server in the background",],
19 "Run the server in the background",],
2220 "ssl_key" => ["--ssl-key [KEY]", "X.509 (SSL) Private Key."],
2321 "port" => ["-P", "--port [PORT]", "Port to listen on"],
2422 "show_dir_listing" => ["--show-dir-listing",
25 "Show a directory listing instead of loading your index file.",],
23 "Show a directory listing instead of loading " \
24 "your index file.",],
2625 "skip_initial_build" => ["skip_initial_build", "--skip-initial-build",
27 "Skips the initial site build which occurs before the server is started.",],
26 "Skips the initial site build which occurs before " \
27 "the server is started.",],
2828 "livereload" => ["-l", "--livereload",
29 "Use LiveReload to automatically refresh browsers",],
29 "Use LiveReload to automatically refresh browsers",],
3030 "livereload_ignore" => ["--livereload-ignore ignore GLOB1[,GLOB2[,...]]",
31 Array,
32 "Files for LiveReload to ignore. Remember to quote the values so your shell "\
33 "won't expand them",],
31 Array,
32 "Files for LiveReload to ignore. " \
33 "Remember to quote the values so your shell " \
34 "won't expand them",],
3435 "livereload_min_delay" => ["--livereload-min-delay [SECONDS]",
35 "Minimum reload delay",],
36 "Minimum reload delay",],
3637 "livereload_max_delay" => ["--livereload-max-delay [SECONDS]",
37 "Maximum reload delay",],
38 "Maximum reload delay",],
3839 "livereload_port" => ["--livereload-port [PORT]", Integer,
39 "Port for LiveReload to listen on",],
40 "Port for LiveReload to listen on",],
4041 }.freeze
4142
4243 DIRECTORY_INDEX = %w(
4344 index.htm
4445 index.html
4546 index.rhtml
47 index.xht
48 index.xhtml
4649 index.cgi
4750 index.xml
4851 index.json
7174 opts["serving"] = true
7275 opts["watch"] = true unless opts.key?("watch")
7376
74 start(opts)
75 end
76 end
77 end
78
79 #
80
81 def start(opts)
82 # Set the reactor to nil so any old reactor will be GCed.
83 # We can't unregister a hook so in testing when Serve.start is
84 # called multiple times we don't want to inadvertently keep using
85 # a reactor created by a previous test when our test might not
86 @reload_reactor = nil
87
88 config = configuration_from_options(opts)
89 if Jekyll.env == "development"
90 config["url"] = default_url(config)
91 end
92 [Build, Serve].each { |klass| klass.process(config) }
77 # Set the reactor to nil so any old reactor will be GCed.
78 # We can't unregister a hook so while running tests we don't want to
79 # inadvertently keep using a reactor created by a previous test.
80 @reload_reactor = nil
81
82 config = configuration_from_options(opts)
83 config["url"] = default_url(config) if Jekyll.env == "development"
84
85 process_with_graceful_fail(cmd, config, Build, Serve)
86 end
87 end
9388 end
9489
9590 #
9792 def process(opts)
9893 opts = configuration_from_options(opts)
9994 destination = opts["destination"]
100 register_reload_hooks(opts) if opts["livereload"]
95 if opts["livereload"]
96 validate_options(opts)
97 register_reload_hooks(opts)
98 end
10199 setup(destination)
102100
103101 start_up_webrick(opts, destination)
110108 # Perform logical validation of CLI options
111109
112110 private
111
113112 def validate_options(opts)
114113 if opts["livereload"]
115114 if opts["detach"]
116 Jekyll.logger.warn "Warning:",
117 "--detach and --livereload are mutually exclusive. Choosing --livereload"
115 Jekyll.logger.warn "Warning:", "--detach and --livereload are mutually exclusive. " \
116 "Choosing --livereload"
118117 opts["detach"] = false
119118 end
120119 if opts["ssl_cert"] || opts["ssl_key"]
129128 opts["watch"] = true
130129 end
131130 elsif %w(livereload_min_delay
132 livereload_max_delay
133 livereload_ignore
134 livereload_port).any? { |o| opts[o] }
135 Jekyll.logger.abort_with "--livereload-min-delay, "\
136 "--livereload-max-delay, --livereload-ignore, and "\
137 "--livereload-port require the --livereload option."
138 end
139 end
140
141 #
142
143 private
131 livereload_max_delay
132 livereload_ignore
133 livereload_port).any? { |o| opts[o] }
134 Jekyll.logger.abort_with "--livereload-min-delay, --livereload-max-delay, " \
135 "--livereload-ignore, and --livereload-port require " \
136 "the --livereload option."
137 end
138 end
139
144140 # rubocop:disable Metrics/AbcSize
145141 def register_reload_hooks(opts)
146142 require_relative "serve/live_reload_reactor"
147143 @reload_reactor = LiveReloadReactor.new
148144
149145 Jekyll::Hooks.register(:site, :post_render) do |site|
150 regenerator = Jekyll::Regenerator.new(site)
151 @changed_pages = site.pages.select do |p|
152 regenerator.regenerate?(p)
146 @changed_pages = []
147 site.each_site_file do |item|
148 @changed_pages << item if site.regenerator.regenerate?(item)
153149 end
154150 end
155151
177173 # Do a base pre-setup of WEBRick so that everything is in place
178174 # when we get ready to party, checking for an setting up an error page
179175 # and making sure our destination exists.
180
181 private
176 #
177 # rubocop:disable Security/IoMethods
182178 def setup(destination)
183179 require_relative "serve/servlet"
184180
192188 end
193189 end
194190 end
195
196 #
197
198 private
191 # rubocop:enable Security/IoMethods
192
199193 def webrick_opts(opts)
200194 opts = {
201195 :JekyllOptions => opts,
202196 :DoNotReverseLookup => true,
203197 :MimeTypes => mime_types,
198 :MimeTypesCharset => mime_types_charset,
204199 :DocumentRoot => opts["destination"],
205200 :StartCallback => start_callback(opts["detach"]),
206201 :StopCallback => stop_callback(opts["detach"]),
216211 opts
217212 end
218213
219 #
220
221 private
222214 def start_up_webrick(opts, destination)
223 if opts["livereload"]
224 @reload_reactor.start(opts)
225 end
215 @reload_reactor.start(opts) if opts["livereload"]
226216
227217 @server = WEBrick::HTTPServer.new(webrick_opts(opts)).tap { |o| o.unmount("") }
228218 @server.mount(opts["baseurl"].to_s, Servlet, destination, file_handler_opts)
233223 end
234224
235225 # Recreate NondisclosureName under utf-8 circumstance
236
237 private
238226 def file_handler_opts
239 WEBrick::Config::FileHandler.merge({
227 WEBrick::Config::FileHandler.merge(
240228 :FancyIndexing => true,
241229 :NondisclosureName => [
242230 ".ht*", "~*",
243 ],
244 })
245 end
246
247 #
248
249 private
231 ]
232 )
233 end
234
250235 def server_address(server, options = {})
251236 format_url(
252237 server.config[:SSLEnable],
256241 )
257242 end
258243
259 private
260244 def format_url(ssl_enabled, address, port, baseurl = nil)
261 format("%<prefix>s://%<address>s:%<port>i%<baseurl>s", {
262 :prefix => ssl_enabled ? "https" : "http",
263 :address => address,
264 :port => port,
265 :baseurl => baseurl ? "#{baseurl}/" : "",
266 })
267 end
268
269 #
270
271 private
245 format("%<prefix>s://%<address>s:%<port>i%<baseurl>s",
246 :prefix => ssl_enabled ? "https" : "http",
247 :address => address,
248 :port => port,
249 :baseurl => baseurl ? "#{baseurl}/" : "")
250 end
251
272252 def default_url(opts)
273253 config = configuration_from_options(opts)
274254 format_url(
278258 )
279259 end
280260
281 #
282
283 private
284261 def launch_browser(server, opts)
285262 address = server_address(server, opts)
286263 return system "start", address if Utils::Platforms.windows?
287264 return system "xdg-open", address if Utils::Platforms.linux?
288265 return system "open", address if Utils::Platforms.osx?
289 Jekyll.logger.error "Refusing to launch browser; " \
290 "Platform launcher unknown."
266
267 Jekyll.logger.error "Refusing to launch browser. Platform launcher unknown."
291268 end
292269
293270 # Keep in our area with a thread or detach the server as requested
294271 # by the user. This method determines what we do based on what you
295272 # ask us to do.
296
297 private
298273 def boot_or_detach(server, opts)
299274 if opts["detach"]
300275 pid = Process.fork do
302277 end
303278
304279 Process.detach(pid)
305 Jekyll.logger.info "Server detached with pid '#{pid}'.", \
306 "Run `pkill -f jekyll' or `kill -9 #{pid}' to stop the server."
280 Jekyll.logger.info "Server detached with pid '#{pid}'.",
281 "Run `pkill -f jekyll' or `kill -9 #{pid}' to stop the server."
307282 else
308283 t = Thread.new { server.start }
309284 trap("INT") { server.shutdown }
312287 end
313288
314289 # Make the stack verbose if the user requests it.
315
316 private
317290 def enable_logging(opts)
318291 opts[:AccessLog] = []
319292 level = WEBrick::Log.const_get(opts[:JekyllOptions]["verbose"] ? :DEBUG : :WARN)
323296 # Add SSL to the stack if the user triggers --enable-ssl and they
324297 # provide both types of certificates commonly needed. Raise if they
325298 # forget to add one of the certificates.
326
327 private
328299 def enable_ssl(opts)
329300 cert, key, src =
330301 opts[:JekyllOptions].values_at("ssl_cert", "ssl_key", "source")
336307 require "webrick/https"
337308
338309 opts[:SSLCertificate] = OpenSSL::X509::Certificate.new(read_file(src, cert))
339 opts[:SSLPrivateKey] = OpenSSL::PKey::RSA.new(read_file(src, key))
310 begin
311 opts[:SSLPrivateKey] = OpenSSL::PKey::RSA.new(read_file(src, key))
312 rescue StandardError
313 if defined?(OpenSSL::PKey::EC)
314 opts[:SSLPrivateKey] = OpenSSL::PKey::EC.new(read_file(src, key))
315 else
316 raise
317 end
318 end
340319 opts[:SSLEnable] = true
341320 end
342321
343 private
344322 def start_callback(detached)
345323 unless detached
346324 proc do
347325 mutex.synchronize do
348326 # Block until EventMachine reactor starts
349 @reload_reactor.started_event.wait unless @reload_reactor.nil?
327 @reload_reactor&.started_event&.wait
350328 @running = true
351329 Jekyll.logger.info("Server running...", "press ctrl-c to stop.")
352330 @run_cond.broadcast
355333 end
356334 end
357335
358 private
359336 def stop_callback(detached)
360337 unless detached
361338 proc do
371348 end
372349 end
373350
374 private
375351 def mime_types
376352 file = File.expand_path("../mime.types", __dir__)
377353 WEBrick::HTTPUtils.load_mime_types(file)
378354 end
379355
380 private
356 def mime_types_charset
357 SafeYAML.load_file(File.expand_path("serve/mime_types_charset.json", __dir__))
358 end
359
381360 def read_file(source_dir, file_path)
382361 File.read(Jekyll.sanitized_path(source_dir, file_path))
383362 end
33 class Configuration < Hash
44 # Default options. Overridden by values in _config.yml.
55 # Strings rather than symbols are used for compatibility with YAML.
6 DEFAULTS = Configuration[{
6 DEFAULTS = {
77 # Where things are
88 "source" => Dir.pwd,
99 "destination" => File.join(Dir.pwd, "_site"),
1010 "collections_dir" => "",
11 "cache_dir" => ".jekyll-cache",
1112 "plugins_dir" => "_plugins",
1213 "layouts_dir" => "_layouts",
1314 "data_dir" => "_data",
1718 # Handling Reading
1819 "safe" => false,
1920 "include" => [".htaccess"],
20 "exclude" => %w(
21 Gemfile Gemfile.lock node_modules vendor/bundle/ vendor/cache/ vendor/gems/
22 vendor/ruby/
23 ),
21 "exclude" => [],
2422 "keep_files" => [".git", ".svn"],
2523 "encoding" => "utf-8",
2624 "markdown_ext" => "markdown,mkdown,mkdn,mkd,md",
6563 "strict_variables" => false,
6664 },
6765
68 "rdiscount" => {
69 "extensions" => [],
70 },
71
72 "redcarpet" => {
73 "extensions" => [],
74 },
75
7666 "kramdown" => {
7767 "auto_ids" => true,
78 "toc_levels" => "1..6",
68 "toc_levels" => (1..6).to_a,
7969 "entity_output" => "as_char",
8070 "smart_quotes" => "lsquo,rsquo,ldquo,rdquo",
8171 "input" => "GFM",
8474 "footnote_nr" => 1,
8575 "show_warnings" => false,
8676 },
87 }.map { |k, v| [k, v.freeze] }].freeze
77 }.each_with_object(Configuration.new) { |(k, v), hsh| hsh[k] = v.freeze }.freeze
8878
8979 class << self
9080 # Static: Produce a Configuration ready for use in a Site.
91 # It takes the input, fills in the defaults where values do not
92 # exist, and patches common issues including migrating options for
93 # backwards compatiblity. Except where a key or value is being fixed,
94 # the user configuration will override the defaults.
81 # It takes the input, fills in the defaults where values do not exist.
9582 #
9683 # user_config - a Hash or Configuration of overrides.
9784 #
98 # Returns a Configuration filled with defaults and fixed for common
99 # problems and backwards-compatibility.
85 # Returns a Configuration filled with defaults.
10086 def from(user_config)
10187 Utils.deep_merge_hashes(DEFAULTS, Configuration[user_config].stringify_keys)
102 .add_default_collections
88 .add_default_collections.add_default_excludes
10389 end
10490 end
10591
10793 #
10894 # Return a copy of the hash where all its keys are strings
10995 def stringify_keys
110 reduce({}) { |hsh, (k, v)| hsh.merge(k.to_s => v) }
96 each_with_object({}) { |(k, v), hsh| hsh[k.to_s] = v }
11197 end
11298
11399 def get_config_value_with_override(config_key, override)
141127 when %r!\.ya?ml!i
142128 SafeYAML.load_file(filename) || {}
143129 else
144 raise ArgumentError, "No parser for '#{filename}' is available.
145 Use a .y(a)ml or .toml file instead."
130 raise ArgumentError,
131 "No parser for '#{filename}' is available. Use a .y(a)ml or .toml file instead."
146132 end
147133 end
148134
161147 # Get configuration from <source>/_config.yml or <source>/<config_file>
162148 config_files = override["config"]
163149 if config_files.to_s.empty?
164 default = %w(yml yaml).find(-> { "yml" }) do |ext|
150 default = %w(yml yaml toml).find(-> { "yml" }) do |ext|
165151 File.exist?(Jekyll.sanitized_path(source(override), "_config.#{ext}"))
166152 end
167153 config_files = Jekyll.sanitized_path(source(override), "_config.#{default}")
176162 #
177163 # Returns this configuration, overridden by the values in the file
178164 def read_config_file(file)
165 file = File.expand_path(file)
179166 next_config = safe_load_file(file)
180 check_config_is_hash!(next_config, file)
167
168 unless next_config.is_a?(Hash)
169 raise ArgumentError, "Configuration file: (INVALID) #{file}".yellow
170 end
171
181172 Jekyll.logger.info "Configuration file:", file
182173 next_config
183174 rescue SystemCallError
185176 Jekyll.logger.warn "Configuration file:", "none"
186177 {}
187178 else
188 Jekyll.logger.error "Fatal:", "The configuration file '#{file}'
189 could not be found."
179 Jekyll.logger.error "Fatal:", "The configuration file '#{file}' could not be found."
190180 raise LoadError, "The Configuration file '#{file}' could not be found."
191181 end
192182 end
203193 begin
204194 files.each do |config_file|
205195 next if config_file.nil? || config_file.empty?
196
206197 new_config = read_config_file(config_file)
207198 configuration = Utils.deep_merge_hashes(configuration, new_config)
208199 end
209 rescue ArgumentError => err
210 Jekyll.logger.warn "WARNING:", "Error reading configuration. " \
211 "Using defaults (and options)."
212 warn err
213 end
214
215 configuration.backwards_compatibilize.add_default_collections
200 rescue ArgumentError => e
201 Jekyll.logger.warn "WARNING:", "Error reading configuration. Using defaults (and options)."
202 warn e
203 end
204
205 configuration.validate.add_default_collections
216206 end
217207
218208 # Public: Split a CSV string into an array containing its values
224214 csv.split(",").map(&:strip)
225215 end
226216
227 # Public: Ensure the proper options are set in the configuration to allow for
228 # backwards-compatibility with Jekyll pre-1.0
229 #
230 # Returns the backwards-compatible configuration
231 def backwards_compatibilize
217 # Public: Ensure the proper options are set in the configuration
218 #
219 # Returns the configuration Hash
220 def validate
232221 config = clone
233 # Provide backwards-compatibility
234 check_auto(config)
235 check_server(config)
222
236223 check_plugins(config)
237
238 renamed_key "server_port", "port", config
239 renamed_key "gems", "plugins", config
240 renamed_key "layouts", "layouts_dir", config
241 renamed_key "data_source", "data_dir", config
242
243 check_pygments(config)
244224 check_include_exclude(config)
245 check_coderay(config)
246 check_maruku(config)
247225
248226 config
249 end
250
251 # DEPRECATED.
252 def fix_common_issues
253 self
254227 end
255228
256229 def add_default_collections
261234
262235 # Ensure we have a hash.
263236 if config["collections"].is_a?(Array)
264 config["collections"] = Hash[config["collections"].map { |c| [c, {}] }]
237 config["collections"] = config["collections"].each_with_object({}) do |collection, hash|
238 hash[collection] = {}
239 end
265240 end
266241
267242 config["collections"] = Utils.deep_merge_hashes(
276251 config
277252 end
278253
279 def renamed_key(old, new, config, _ = nil)
280 if config.key?(old)
281 Jekyll::Deprecator.deprecation_message "The '#{old}' configuration" \
282 " option has been renamed to '#{new}'. Please update your config" \
283 " file accordingly."
284 config[new] = config.delete(old)
285 end
254 DEFAULT_EXCLUDES = %w(
255 .sass-cache .jekyll-cache
256 gemfiles Gemfile Gemfile.lock
257 node_modules
258 vendor/bundle/ vendor/cache/ vendor/gems/ vendor/ruby/
259 ).freeze
260
261 def add_default_excludes
262 config = clone
263 return config if config["exclude"].nil?
264
265 config["exclude"].concat(DEFAULT_EXCLUDES).uniq!
266 config
286267 end
287268
288269 private
270
271 STYLE_TO_PERMALINK = {
272 :none => "/:categories/:title:output_ext",
273 :date => "/:categories/:year/:month/:day/:title:output_ext",
274 :ordinal => "/:categories/:year/:y_day/:title:output_ext",
275 :pretty => "/:categories/:year/:month/:day/:title/",
276 :weekdate => "/:categories/:year/W:week/:short_day/:title:output_ext",
277 }.freeze
278
279 private_constant :STYLE_TO_PERMALINK
280
289281 def style_to_permalink(permalink_style)
290 case permalink_style.to_sym
291 when :pretty
292 "/:categories/:year/:month/:day/:title/"
293 when :none
294 "/:categories/:title:output_ext"
295 when :date
296 "/:categories/:year/:month/:day/:title:output_ext"
297 when :ordinal
298 "/:categories/:year/:y_day/:title:output_ext"
299 else
300 permalink_style.to_s
301 end
302 end
303
304 # Private: Checks if a given config is a hash
305 #
306 # extracted_config - the value to check
307 # file - the file from which the config was extracted
308 #
309 # Raises an ArgumentError if given config is not a hash
310 private
311 def check_config_is_hash!(extracted_config, file)
312 unless extracted_config.is_a?(Hash)
313 raise ArgumentError, "Configuration file: (INVALID) #{file}".yellow
314 end
315 end
316
317 private
318 def check_auto(config)
319 if config.key?("auto") || config.key?("watch")
320 Jekyll::Deprecator.deprecation_message "Auto-regeneration can no longer" \
321 " be set from your configuration file(s). Use the" \
322 " --[no-]watch/-w command-line option instead."
323 config.delete("auto")
324 config.delete("watch")
325 end
326 end
327
328 private
329 def check_server(config)
330 if config.key?("server")
331 Jekyll::Deprecator.deprecation_message "The 'server' configuration option" \
332 " is no longer accepted. Use the 'jekyll serve'" \
333 " subcommand to serve your site with WEBrick."
334 config.delete("server")
335 end
336 end
337
338 private
339 def check_pygments(config)
340 if config.key?("pygments")
341 Jekyll::Deprecator.deprecation_message "The 'pygments' configuration option" \
342 " has been renamed to 'highlighter'. Please update your" \
343 " config file accordingly. The allowed values are 'rouge', " \
344 "'pygments' or null."
345
346 config["highlighter"] = "pygments" if config["pygments"]
347 config.delete("pygments")
348 end
349 end
350
351 private
282 STYLE_TO_PERMALINK[permalink_style.to_sym] || permalink_style.to_s
283 end
284
352285 def check_include_exclude(config)
353286 %w(include exclude).each do |option|
354 if config[option].is_a?(String)
355 Jekyll::Deprecator.deprecation_message "The '#{option}' configuration option" \
356 " must now be specified as an array, but you specified" \
357 " a string. For now, we've treated the string you provided" \
358 " as a list of comma-separated values."
359 config[option] = csv_to_array(config[option])
360 end
361 config[option].map!(&:to_s) if config[option]
362 end
363 end
364
365 private
366 def check_coderay(config)
367 if (config["kramdown"] || {}).key?("use_coderay")
368 Jekyll::Deprecator.deprecation_message "Please change 'use_coderay'" \
369 " to 'enable_coderay' in your configuration file."
370 config["kramdown"]["use_coderay"] = config["kramdown"].delete("enable_coderay")
371 end
372 end
373
374 private
375 def check_maruku(config)
376 if config.fetch("markdown", "kramdown").to_s.casecmp("maruku").zero?
377 Jekyll.logger.abort_with "Error:", "You're using the 'maruku' " \
378 "Markdown processor, which has been removed as of 3.0.0. " \
379 "We recommend you switch to Kramdown. To do this, replace " \
380 "`markdown: maruku` with `markdown: kramdown` in your " \
381 "`_config.yml` file."
287 next unless config.key?(option)
288 next if config[option].is_a?(Array)
289
290 raise Jekyll::Errors::InvalidConfigurationError,
291 "'#{option}' should be set as an array, but was: #{config[option].inspect}."
382292 end
383293 end
384294
387297 # config - the config hash
388298 #
389299 # Raises a Jekyll::Errors::InvalidConfigurationError if the config `plugins`
390 # is a string
391 private
300 # is not an Array.
392301 def check_plugins(config)
393 if config.key?("plugins") && config["plugins"].is_a?(String)
394 Jekyll.logger.error "Configuration Error:", "You specified the" \
395 " `plugins` config in your configuration file as a string, please" \
396 " use an array instead. If you wanted to set the directory of your" \
397 " plugins, use the config key `plugins_dir` instead."
398 raise Jekyll::Errors::InvalidConfigurationError,
399 "'plugins' should not be a string, but was: " \
400 "#{config["plugins"].inspect}. Use 'plugins_dir' instead."
401 end
302 return unless config.key?("plugins")
303 return if config["plugins"].is_a?(Array)
304
305 Jekyll.logger.error "'plugins' should be set as an array of gem-names, but was: " \
306 "#{config["plugins"].inspect}. Use 'plugins_dir' instead to set " \
307 "the directory for your non-gemified Ruby plugins."
308 raise Jekyll::Errors::InvalidConfigurationError,
309 "'plugins' should be set as an array, but was: #{config["plugins"].inspect}."
402310 end
403311 end
404312 end
11
22 module Jekyll
33 module Converters
4 # Identity converter. Returns same content as given.
5 # For more info on converters see https://jekyllrb.com/docs/plugins/converters/
46 class Identity < Converter
57 safe true
68
79 priority :lowest
810
11 # Public: Does the given extension match this converter's list of acceptable extensions?
12 # Takes one argument: the file's extension (including the dot).
13 #
14 # _ext - The String extension to check (not relevant here)
15 #
16 # Returns true since it always matches.
917 def matches(_ext)
1018 true
1119 end
1220
21 # Public: The extension to be given to the output file (including the dot).
22 #
23 # ext - The String extension or original file.
24 #
25 # Returns The String output file extension.
1326 def output_ext(ext)
1427 ext
1528 end
1629
30 # Logic to do the content conversion.
31 #
32 # content - String content of file (without front matter).
33 #
34 # Returns a String of the converted content.
1735 def convert(content)
1836 content
1937 end
0 # Frozen-string-literal: true
0 # frozen_string_literal: true
1
2 module Kramdown
3 # A Kramdown::Document subclass meant to optimize memory usage from initializing
4 # a kramdown document for parsing.
5 #
6 # The optimization is by using the same options Hash (and its derivatives) for
7 # converting all Markdown documents in a Jekyll site.
8 class JekyllDocument < Document
9 class << self
10 attr_reader :options, :parser
11
12 # The implementation is basically the core logic in +Kramdown::Document#initialize+
13 #
14 # rubocop:disable Naming/MemoizedInstanceVariableName
15 def setup(options)
16 @cache ||= {}
17
18 # reset variables on a subsequent set up with a different options Hash
19 unless @cache[:id] == options.hash
20 @options = @parser = nil
21 @cache[:id] = options.hash
22 end
23
24 @options ||= Options.merge(options).freeze
25 @parser ||= begin
26 parser_name = (@options[:input] || "kramdown").to_s
27 parser_name = parser_name[0..0].upcase + parser_name[1..-1]
28 try_require("parser", parser_name)
29
30 if Parser.const_defined?(parser_name)
31 Parser.const_get(parser_name)
32 else
33 raise Kramdown::Error, "kramdown has no parser to handle the specified " \
34 "input format: #{@options[:input]}"
35 end
36 end
37 end
38 # rubocop:enable Naming/MemoizedInstanceVariableName
39
40 private
41
42 def try_require(type, name)
43 require "kramdown/#{type}/#{Utils.snake_case(name)}"
44 rescue LoadError
45 false
46 end
47 end
48
49 def initialize(source, options = {})
50 JekyllDocument.setup(options)
51
52 @options = JekyllDocument.options
53 @root, @warnings = JekyllDocument.parser.parse(source, @options)
54 end
55
56 # Use Kramdown::Converter::Html class to convert this document into HTML.
57 #
58 # The implementation is basically an optimized version of core logic in
59 # +Kramdown::Document#method_missing+ from kramdown-2.1.0.
60 def to_html
61 output, warnings = Kramdown::Converter::Html.convert(@root, @options)
62 @warnings.concat(warnings)
63 output
64 end
65 end
66 end
67
68 #
169
270 module Jekyll
371 module Converters
34102 @config["syntax_highlighter_opts"]["guess_lang"] = @config["guess_lang"]
35103 @config["coderay"] ||= {} # XXX: Legacy.
36104 modernize_coderay_config
37 make_accessible
38105 end
39106
40107 def convert(content)
41 document = Kramdown::Document.new(content, @config)
108 document = Kramdown::JekyllDocument.new(content, @config)
42109 html_output = document.to_html
43110 if @config["show_warnings"]
44111 document.warnings.each do |warning|
51118 private
52119
53120 def load_dependencies
54 return if Kramdown::VERSION.to_i < 2
55 if @config["input"] == "GFM"
56 Jekyll::External.require_with_graceful_fail("kramdown-parser-gfm")
57 end
121 require "kramdown-parser-gfm" if @config["input"] == "GFM"
58122
59123 if highlighter == "coderay"
60124 Jekyll::External.require_with_graceful_fail("kramdown-syntax-coderay")
61125 end
62126
63 # `mathjax` emgine is bundled within kramdown-2.x and will be handled by
127 # `mathjax` engine is bundled within kramdown-2.x and will be handled by
64128 # kramdown itself.
65129 if (math_engine = @config["math_engine"]) && math_engine != "mathjax"
66130 Jekyll::External.require_with_graceful_fail("kramdown-math-#{math_engine}")
67131 end
68132 end
69133
70 def make_accessible(hash = @config)
71 hash.keys.each do |key|
72 hash[key.to_sym] = hash[key]
73 make_accessible(hash[key]) if hash[key].is_a?(Hash)
74 end
75 end
76
77 # config[kramdown][syntax_higlighter] >
134 # config[kramdown][syntax_highlighter] >
78135 # config[kramdown][enable_coderay] >
79136 # config[highlighter]
80137 # Where `enable_coderay` is now deprecated because Kramdown
81138 # supports Rouge now too.
82
83 private
84139 def highlighter
85140 return @highlighter if @highlighter
86141
90145 ]
91146 end
92147
93 @highlighter = begin
94 if @config.key?("enable_coderay") && @config["enable_coderay"]
95 Jekyll::Deprecator.deprecation_message(
96 "You are using 'enable_coderay', " \
97 "use syntax_highlighter: coderay in your configuration file."
98 )
148 @highlighter = if @config.key?("enable_coderay") && @config["enable_coderay"]
149 Jekyll::Deprecator.deprecation_message(
150 "You are using 'enable_coderay', " \
151 "use syntax_highlighter: coderay in your configuration file."
152 )
99153
100 "coderay"
101 else
102 @main_fallback_highlighter
103 end
104 end
154 "coderay"
155 else
156 @main_fallback_highlighter
157 end
105158 end
106159
107 private
108160 def strip_coderay_prefix(hash)
109161 hash.each_with_object({}) do |(key, val), hsh|
110 cleaned_key = key.to_s.gsub(%r!\Acoderay_!, "")
162 cleaned_key = key.to_s.delete_prefix("coderay_")
111163
112164 if key != cleaned_key
113165 Jekyll::Deprecator.deprecation_message(
122174 # If our highlighter is CodeRay we go in to merge the CodeRay defaults
123175 # with your "coderay" key if it's there, deprecating it in the
124176 # process of you using it.
125
126 private
127177 def modernize_coderay_config
128178 unless @config["coderay"].empty?
129179 Jekyll::Deprecator.deprecation_message(
+0
-37
lib/jekyll/converters/markdown/rdiscount_parser.rb less more
0 # frozen_string_literal: true
1
2 module Jekyll
3 module Converters
4 class Markdown
5 class RDiscountParser
6 def initialize(config)
7 unless defined?(RDiscount)
8 Jekyll::External.require_with_graceful_fail "rdiscount"
9 end
10 @config = config
11 @rdiscount_extensions = @config["rdiscount"]["extensions"].map(&:to_sym)
12 end
13
14 def convert(content)
15 rd = RDiscount.new(content, *@rdiscount_extensions)
16 html = rd.to_html
17 if @config["rdiscount"]["toc_token"]
18 html = replace_generated_toc(rd, html, @config["rdiscount"]["toc_token"])
19 end
20 html
21 end
22
23 private
24 def replace_generated_toc(rd_instance, html, toc_token)
25 if rd_instance.generate_toc && html.include?(toc_token)
26 utf8_toc = rd_instance.toc_content
27 utf8_toc.force_encoding("utf-8") if utf8_toc.respond_to?(:force_encoding)
28 html.gsub(toc_token, utf8_toc)
29 else
30 html
31 end
32 end
33 end
34 end
35 end
36 end
+0
-112
lib/jekyll/converters/markdown/redcarpet_parser.rb less more
0 # frozen_string_literal: true
1
2 class Jekyll::Converters::Markdown::RedcarpetParser
3 module CommonMethods
4 def add_code_tags(code, lang)
5 code = code.to_s
6 code = code.sub(
7 %r!<pre>!,
8 "<pre><code class=\"language-#{lang}\" data-lang=\"#{lang}\">"
9 )
10 code = code.sub(%r!</pre>!, "</code></pre>")
11 code
12 end
13 end
14
15 module WithPygments
16 include CommonMethods
17 def block_code(code, lang)
18 unless defined?(Pygments)
19 Jekyll::External.require_with_graceful_fail("pygments")
20 end
21 lang = lang && lang.split.first || "text"
22 add_code_tags(
23 Pygments.highlight(
24 code,
25 {
26 :lexer => lang,
27 :options => { :encoding => "utf-8" },
28 }
29 ),
30 lang
31 )
32 end
33 end
34
35 module WithoutHighlighting
36 require "cgi"
37
38 include CommonMethods
39
40 def code_wrap(code)
41 "<figure class=\"highlight\"><pre>#{CGI.escapeHTML(code)}</pre></figure>"
42 end
43
44 def block_code(code, lang)
45 lang = lang && lang.split.first || "text"
46 add_code_tags(code_wrap(code), lang)
47 end
48 end
49
50 module WithRouge
51 def block_code(_code, lang)
52 code = "<pre>#{super}</pre>"
53
54 "<div class=\"highlight\">#{add_code_tags(code, lang)}</div>"
55 end
56
57 protected
58 def rouge_formatter(_lexer)
59 Jekyll::Utils::Rouge.html_formatter(:wrap => false)
60 end
61 end
62
63 def initialize(config)
64 unless defined?(Redcarpet)
65 Jekyll::External.require_with_graceful_fail("redcarpet")
66 end
67 @config = config
68 @redcarpet_extensions = {}
69 @config["redcarpet"]["extensions"].each do |e|
70 @redcarpet_extensions[e.to_sym] = true
71 end
72
73 @renderer ||= class_with_proper_highlighter(@config["highlighter"])
74 end
75
76 def class_with_proper_highlighter(highlighter)
77 Class.new(Redcarpet::Render::HTML) do
78 case highlighter
79 when "pygments"
80 include WithPygments
81 when "rouge"
82 Jekyll::External.require_with_graceful_fail(%w(
83 rouge rouge/plugins/redcarpet
84 ))
85
86 unless Gem::Version.new(Rouge.version) > Gem::Version.new("1.3.0")
87 abort "Please install Rouge 1.3.0 or greater and try running Jekyll again."
88 end
89
90 include Rouge::Plugins::Redcarpet
91 include CommonMethods
92 include WithRouge
93 else
94 include WithoutHighlighting
95 end
96 end
97 end
98
99 def convert(content)
100 @redcarpet_extensions[:fenced_code_blocks] = \
101 !@redcarpet_extensions[:no_fenced_code_blocks]
102 if @redcarpet_extensions[:smart]
103 @renderer.send :include, Redcarpet::Render::SmartyPants
104 end
105 markdown = Redcarpet::Markdown.new(
106 @renderer.new(@redcarpet_extensions),
107 @redcarpet_extensions
108 )
109 markdown.render(content)
110 end
111 end
11
22 module Jekyll
33 module Converters
4 # Markdown converter.
5 # For more info on converters see https://jekyllrb.com/docs/plugins/converters/
46 class Markdown < Converter
57 highlighter_prefix "\n"
68 highlighter_suffix "\n"
810
911 def setup
1012 return if @setup ||= false
13
1114 unless (@parser = get_processor)
12 Jekyll.logger.error "Invalid Markdown processor given:", @config["markdown"]
1315 if @config["safe"]
14 Jekyll.logger.info "", "Custom processors are not loaded in safe mode"
16 Jekyll.logger.warn "Build Warning:", "Custom processors are not loaded in safe mode"
1517 end
16 Jekyll.logger.error(
17 "",
18 "Available processors are: #{valid_processors.join(", ")}"
19 )
20 raise Errors::FatalException, "Bailing out; invalid Markdown processor."
18
19 Jekyll.logger.error "Markdown processor:",
20 "#{@config["markdown"].inspect} is not a valid Markdown processor."
21 Jekyll.logger.error "", "Available processors are: #{valid_processors.join(", ")}"
22 Jekyll.logger.error ""
23 raise Errors::FatalException, "Invalid Markdown processor given: #{@config["markdown"]}"
2124 end
2225
26 @cache = Jekyll::Cache.new("Jekyll::Converters::Markdown")
2327 @setup = true
2428 end
2529
26 # Rubocop does not allow reader methods to have names starting with `get_`
30 # RuboCop does not allow reader methods to have names starting with `get_`
2731 # To ensure compatibility, this check has been disabled on this method
2832 #
2933 # rubocop:disable Naming/AccessorMethodName
3034 def get_processor
3135 case @config["markdown"].downcase
32 when "redcarpet" then return RedcarpetParser.new(@config)
33 when "kramdown" then return KramdownParser.new(@config)
34 when "rdiscount" then return RDiscountParser.new(@config)
36 when "kramdown" then KramdownParser.new(@config)
3537 else
3638 custom_processor
3739 end
3840 end
3941 # rubocop:enable Naming/AccessorMethodName
4042
41 # Public: Provides you with a list of processors, the ones we
42 # support internally and the ones that you have provided to us (if you
43 # are not in safe mode.)
44
43 # Public: Provides you with a list of processors comprised of the ones we support internally
44 # and the ones that you have provided to us (if they're whitelisted for use in safe mode).
45 #
46 # Returns an array of symbols.
4547 def valid_processors
46 %w(rdiscount kramdown redcarpet) + third_party_processors
48 [:kramdown] + third_party_processors
4749 end
4850
4951 # Public: A list of processors that you provide via plugins.
50 # This is really only available if you are not in safe mode, if you are
51 # in safe mode (re: GitHub) then there will be none.
52
52 #
53 # Returns an array of symbols
5354 def third_party_processors
54 self.class.constants - \
55 %w(KramdownParser RDiscountParser RedcarpetParser PRIORITIES).map(
56 &:to_sym
57 )
55 self.class.constants - [:KramdownParser, :PRIORITIES]
5856 end
5957
60 def extname_list
61 @extname_list ||= @config["markdown_ext"].split(",").map do |e|
62 ".#{e.downcase}"
63 end
64 end
65
58 # Does the given extension match this converter's list of acceptable extensions?
59 # Takes one argument: the file's extension (including the dot).
60 #
61 # ext - The String extension to check.
62 #
63 # Returns true if it matches, false otherwise.
6664 def matches(ext)
6765 extname_list.include?(ext.downcase)
6866 end
6967
68 # Public: The extension to be given to the output file (including the dot).
69 #
70 # ext - The String extension or original file.
71 #
72 # Returns The String output file extension.
7073 def output_ext(_ext)
7174 ".html"
7275 end
7376
77 # Logic to do the content conversion.
78 #
79 # content - String content of file (without front matter).
80 #
81 # Returns a String of the converted content.
7482 def convert(content)
7583 setup
76 @parser.convert(content)
84 @cache.getset(content) do
85 @parser.convert(content)
86 end
87 end
88
89 def extname_list
90 @extname_list ||= @config["markdown_ext"].split(",").map! { |e| ".#{e.downcase}" }
7791 end
7892
7993 private
94
8095 def custom_processor
8196 converter_name = @config["markdown"]
82 if custom_class_allowed?(converter_name)
83 self.class.const_get(converter_name).new(@config)
84 end
97 self.class.const_get(converter_name).new(@config) if custom_class_allowed?(converter_name)
8598 end
8699
87100 # Private: Determine whether a class name is an allowed custom
89102 #
90103 # parser_name - the name of the parser class
91104 #
92 # Returns true if the parser name contains only alphanumeric
93 # characters and is defined within Jekyll::Converters::Markdown
94
95 private
105 # Returns true if the parser name contains only alphanumeric characters and is defined
106 # within Jekyll::Converters::Markdown
96107 def custom_class_allowed?(parser_name)
97 parser_name !~ %r![^A-Za-z0-9_]! && self.class.constants.include?(
98 parser_name.to_sym
99 )
108 parser_name !~ %r![^A-Za-z0-9_]! && self.class.constants.include?(parser_name.to_sym)
100109 end
101110 end
102111 end
00 # frozen_string_literal: true
11
2 class Kramdown::Parser::SmartyPants < Kramdown::Parser::Kramdown
3 def initialize(source, options)
4 super
5 @block_parsers = [:block_html, :content]
6 @span_parsers = [:smart_quotes, :html_entity, :typographic_syms, :span_html]
2 module Kramdown
3 module Parser
4 class SmartyPants < Kramdown::Parser::Kramdown
5 def initialize(source, options)
6 super
7 @block_parsers = [:block_html, :content]
8 @span_parsers = [:smart_quotes, :html_entity, :typographic_syms, :span_html]
9 end
10
11 def parse_content
12 add_text @src.scan(%r!\A.*\n!)
13 end
14 define_parser(:content, %r!\A!)
15 end
716 end
8
9 def parse_content
10 add_text @src.scan(%r!\A.*\n!)
11 end
12 define_parser(:content, %r!\A!)
1317 end
1418
1519 module Jekyll
1620 module Converters
21 # SmartyPants converter.
22 # For more info on converters see https://jekyllrb.com/docs/plugins/converters/
1723 class SmartyPants < Converter
1824 safe true
1925 priority :low
2026
2127 def initialize(config)
22 unless defined?(Kramdown)
23 Jekyll::External.require_with_graceful_fail "kramdown"
24 end
28 Jekyll::External.require_with_graceful_fail "kramdown" unless defined?(Kramdown)
2529 @config = config["kramdown"].dup || {}
2630 @config[:input] = :SmartyPants
2731 end
2832
29 def matches(_)
33 # Does the given extension match this converter's list of acceptable extensions?
34 # Takes one argument: the file's extension (including the dot).
35 #
36 # ext - The String extension to check.
37 #
38 # Returns true if it matches, false otherwise.
39 def matches(_ext)
3040 false
3141 end
3242
33 def output_ext(_)
43 # Public: The extension to be given to the output file (including the dot).
44 #
45 # ext - The String extension or original file.
46 #
47 # Returns The String output file extension.
48 def output_ext(_ext)
3449 nil
3550 end
3651
52 # Logic to do the content conversion.
53 #
54 # content - String content of file (without front matter).
55 #
56 # Returns a String of the converted content.
3757 def convert(content)
3858 document = Kramdown::Document.new(content, @config)
3959 html_output = document.to_html.chomp
3434 # Returns nothing.
3535 # rubocop:disable Metrics/AbcSize
3636 def read_yaml(base, name, opts = {})
37 filename = File.join(base, name)
37 filename = @path || site.in_source_dir(base, name)
38 Jekyll.logger.debug "Reading:", relative_path
3839
3940 begin
40 self.content = File.read(@path || site.in_source_dir(base, name),
41 **Utils.merged_file_read_opts(site, opts))
41 self.content = File.read(filename, **Utils.merged_file_read_opts(site, opts))
4242 if content =~ Document::YAML_FRONT_MATTER_REGEXP
43 self.content = $POSTMATCH
43 self.content = Regexp.last_match.post_match
4444 self.data = SafeYAML.load(Regexp.last_match(1))
4545 end
4646 rescue Psych::SyntaxError => e
4747 Jekyll.logger.warn "YAML Exception reading #{filename}: #{e.message}"
48 raise e if self.site.config["strict_front_matter"]
48 raise e if site.config["strict_front_matter"]
4949 rescue StandardError => e
5050 Jekyll.logger.warn "Error reading file #{filename}: #{e.message}"
51 raise e if self.site.config["strict_front_matter"]
51 raise e if site.config["strict_front_matter"]
5252 end
5353
5454 self.data ||= {}
6363 def validate_data!(filename)
6464 unless self.data.is_a?(Hash)
6565 raise Errors::InvalidYAMLFrontMatterError,
66 "Invalid YAML front matter in #{filename}"
66 "Invalid YAML front matter in #{filename}"
6767 end
6868 end
6969
7070 def validate_permalink!(filename)
71 if self.data["permalink"] && self.data["permalink"].to_s.empty?
71 if self.data["permalink"] == ""
7272 raise Errors::InvalidPermalinkError, "Invalid permalink in #{filename}"
7373 end
7474 end
7777 #
7878 # Returns the transformed contents.
7979 def transform
80 _renderer.convert(content)
80 renderer.convert(content)
8181 end
8282
8383 # Determine the extension depending on content_type.
8585 # Returns the String extension for the output file.
8686 # e.g. ".html" for an HTML output file.
8787 def output_ext
88 _renderer.output_ext
88 renderer.output_ext
8989 end
9090
9191 # Determine which converter to use based on this convertible's
9393 #
9494 # Returns the Converter instance.
9595 def converters
96 _renderer.converters
96 renderer.converters
9797 end
9898
9999 # Render Liquid in the content
104104 #
105105 # Returns the converted content
106106 def render_liquid(content, payload, info, path)
107 _renderer.render_liquid(content, payload, info, path)
107 renderer.render_liquid(content, payload, info, path)
108108 end
109109
110110 # Convert this Convertible's data to a Hash suitable for use by Liquid.
111111 #
112112 # Returns the Hash representation of this Convertible.
113113 def to_liquid(attrs = nil)
114 further_data = Hash[(attrs || self.class::ATTRIBUTES_FOR_LIQUID).map do |attribute|
115 [attribute, send(attribute)]
116 end]
117
118 defaults = site.frontmatter_defaults.all(relative_path, type)
114 further_data = \
115 (attrs || self.class::ATTRIBUTES_FOR_LIQUID).each_with_object({}) do |attribute, hsh|
116 hsh[attribute] = send(attribute)
117 end
118
119119 Utils.deep_merge_hashes defaults, Utils.deep_merge_hashes(data, further_data)
120120 end
121121
124124 #
125125 # Returns the type of self.
126126 def type
127 if is_a?(Page)
128 :pages
129 end
127 :pages if is_a?(Page)
130128 end
131129
132130 # returns the owner symbol for hook triggering
133131 def hook_owner
134 if is_a?(Page)
135 :pages
136 end
132 :pages if is_a?(Page)
137133 end
138134
139135 # Determine whether the document is an asset file.
149145 #
150146 # Returns true if extname == .sass or .scss, false otherwise.
151147 def sass_file?
152 %w(.sass .scss).include?(ext)
148 Jekyll::Document::SASS_FILE_EXTS.include?(ext)
153149 end
154150
155151 # Determine whether the document is a CoffeeScript file.
163159 #
164160 # Returns true if the file has Liquid Tags or Variables, false otherwise.
165161 def render_with_liquid?
162 return false if data["render_with_liquid"] == false
163
166164 Jekyll::Utils.has_liquid_construct?(content)
167165 end
168166
180178 #
181179 # Returns true if the layout is invalid, false if otherwise
182180 def invalid_layout?(layout)
183 !data["layout"].nil? && layout.nil? && !(self.is_a? Jekyll::Excerpt)
181 !data["layout"].nil? && layout.nil? && !(is_a? Jekyll::Excerpt)
184182 end
185183
186184 # Recursively render layouts
191189 #
192190 # Returns nothing
193191 def render_all_layouts(layouts, payload, info)
194 _renderer.layouts = layouts
195 self.output = _renderer.place_in_layouts(output, payload, info)
192 renderer.layouts = layouts
193 self.output = renderer.place_in_layouts(output, payload, info)
196194 ensure
197 @_renderer = nil # this will allow the modifications above to disappear
195 @renderer = nil # this will allow the modifications above to disappear
198196 end
199197
200198 # Add any necessary layouts to this convertible document.
204202 #
205203 # Returns nothing.
206204 def do_layout(payload, layouts)
207 self.output = _renderer.tap do |renderer|
208 renderer.layouts = layouts
209 renderer.payload = payload
205 self.output = renderer.tap do |doc_renderer|
206 doc_renderer.layouts = layouts
207 doc_renderer.payload = payload
210208 end.run
211209
212 Jekyll.logger.debug "Post-Render Hooks:", self.relative_path
210 Jekyll.logger.debug "Post-Render Hooks:", relative_path
213211 Jekyll::Hooks.trigger hook_owner, :post_render, self
214212 ensure
215 @_renderer = nil # this will allow the modifications above to disappear
213 @renderer = nil # this will allow the modifications above to disappear
216214 end
217215
218216 # Write the generated page file to the destination directory.
241239 end
242240 end
243241
242 def renderer
243 @renderer ||= Jekyll::Renderer.new(site, self)
244 end
245
244246 private
245247
246 def _renderer
247 @_renderer ||= Jekyll::Renderer.new(site, self)
248 def defaults
249 @defaults ||= site.frontmatter_defaults.all(relative_path, type)
248250 end
249251
250252 def no_layout?
1414 '--watch'."
1515 arg_is_present? args, "--no-auto", "To disable auto-replication, simply leave off \
1616 the '--watch' switch."
17 arg_is_present? args, "--pygments", "The 'pygments'settings has been removed in \
17 arg_is_present? args, "--pygments", "The 'pygments' settings has been removed in \
1818 favour of 'highlighter'."
1919 arg_is_present? args, "--paginate", "The 'paginate' setting can only be set in \
2020 your config files."
3333 end
3434
3535 def arg_is_present?(args, deprecated_argument, message)
36 if args.include?(deprecated_argument)
37 deprecation_message(message)
38 end
36 deprecation_message(message) if args.include?(deprecated_argument)
3937 end
4038
4139 def deprecation_message(message)
44 include Comparable
55 extend Forwardable
66
7 attr_reader :path, :site, :extname, :collection
7 attr_reader :path, :site, :extname, :collection, :type
88 attr_accessor :content, :output
99
1010 def_delegator :self, :read_post_data, :post_read
1111
12 YAML_FRONT_MATTER_REGEXP = %r!\A(---\s*\n.*?\n?)^((---|\.\.\.)\s*$\n?)!m
13 DATELESS_FILENAME_MATCHER = %r!^(?:.+/)*(.*)(\.[^.]+)$!
14 DATE_FILENAME_MATCHER = %r!^(?:.+/)*(\d{2,4}-\d{1,2}-\d{1,2})-(.*)(\.[^.]+)$!
12 YAML_FRONT_MATTER_REGEXP = %r!\A(---\s*\n.*?\n?)^((---|\.\.\.)\s*$\n?)!m.freeze
13 DATELESS_FILENAME_MATCHER = %r!^(?:.+/)*(.*)(\.[^.]+)$!.freeze
14 DATE_FILENAME_MATCHER = %r!^(?>.+/)*?(\d{2,4}-\d{1,2}-\d{1,2})-([^/]*)(\.[^.]+)$!.freeze
15
16 SASS_FILE_EXTS = %w(.sass .scss).freeze
17 YAML_FILE_EXTS = %w(.yaml .yml).freeze
18
19 #
20
21 # Class-wide cache to stash and retrieve regexp to detect "super-directories"
22 # of a particular Jekyll::Document object.
23 #
24 # dirname - The *special directory* for the Document.
25 # e.g. "_posts" or "_drafts" for Documents from the `site.posts` collection.
26 def self.superdirs_regex(dirname)
27 @superdirs_regex ||= {}
28 @superdirs_regex[dirname] ||= %r!#{dirname}.*!
29 end
30
31 #
1532
1633 # Create a new Document.
1734 #
2643 @path = path
2744 @extname = File.extname(path)
2845 @collection = relations[:collection]
46 @type = @collection.label.to_sym
47
2948 @has_yaml_header = nil
3049
3150 if draft?
3554 end
3655
3756 data.default_proc = proc do |_, key|
38 site.frontmatter_defaults.find(relative_path, collection.label, key)
57 site.frontmatter_defaults.find(relative_path, type, key)
3958 end
4059
4160 trigger_hooks(:post_init)
5978 data
6079 end
6180
81 # Returns the document date. If metadata is not present then calculates it
82 # based on Jekyll::Site#time or the document file modification time.
83 #
84 # Return document date string.
6285 def date
6386 data["date"] ||= (draft? ? source_file_mtime : site.time)
6487 end
6588
89 # Return document file modification time in the form of a Time object.
90 #
91 # Return document file modification Time object.
6692 def source_file_mtime
67 @source_file_mtime ||= File.mtime(path)
93 File.mtime(path)
6894 end
6995
7096 # Returns whether the document is a draft. This is only the case if
89115 #
90116 # Returns the output extension
91117 def output_ext
92 @output_ext ||= Jekyll::Renderer.new(site, self).output_ext
118 renderer.output_ext
93119 end
94120
95121 # The base filename of the document, without the file extname.
104130 # Returns the base filename of the document.
105131 def basename
106132 @basename ||= File.basename(path)
133 end
134
135 def renderer
136 @renderer ||= Jekyll::Renderer.new(site, self)
107137 end
108138
109139 # Produces a "cleaned" relative path.
111141 # and with the collection's directory removed as well.
112142 # This method is useful when building the URL of the document.
113143 #
144 # NOTE: `String#gsub` removes all trailing periods (in comparison to `String#chomp`)
145 #
114146 # Examples:
115 # When relative_path is "_methods/site/generate.md":
147 # When relative_path is "_methods/site/generate...md":
116148 # cleaned_relative_path
117149 # # => "/site/generate"
118150 #
119151 # Returns the cleaned relative path of the document.
120152 def cleaned_relative_path
121153 @cleaned_relative_path ||=
122 relative_path[0..-extname.length - 1].sub(collection.relative_directory, "")
154 relative_path[0..-extname.length - 1]
155 .sub(collection.relative_directory, "")
156 .gsub(%r!\.*\z!, "")
123157 end
124158
125159 # Determine whether the document is a YAML file.
126160 #
127161 # Returns true if the extname is either .yml or .yaml, false otherwise.
128162 def yaml_file?
129 %w(.yaml .yml).include?(extname)
163 YAML_FILE_EXTS.include?(extname)
130164 end
131165
132166 # Determine whether the document is an asset file.
142176 #
143177 # Returns true if extname == .sass or .scss, false otherwise.
144178 def sass_file?
145 %w(.sass .scss).include?(extname)
179 SASS_FILE_EXTS.include?(extname)
146180 end
147181
148182 # Determine whether the document is a CoffeeScript file.
158192 # or if the document doesn't contain any Liquid Tags or Variables,
159193 # true otherwise.
160194 def render_with_liquid?
195 return false if data["render_with_liquid"] == false
196
161197 !(coffeescript_file? || yaml_file? || !Utils.has_liquid_construct?(content))
162198 end
163199
203239 #
204240 # Returns the computed URL for the document.
205241 def url
206 @url ||= URL.new({
242 @url ||= URL.new(
207243 :template => url_template,
208244 :placeholders => url_placeholders,
209 :permalink => permalink,
210 }).to_s
245 :permalink => permalink
246 ).to_s
211247 end
212248
213249 def [](key)
220256 #
221257 # Returns the full path to the output file of this document.
222258 def destination(base_directory)
223 dest = site.in_dest_dir(base_directory)
224 path = site.in_dest_dir(dest, URL.unescape_path(url))
225 if url.end_with? "/"
226 path = File.join(path, "index.html")
227 else
228 path << output_ext unless path.end_with? output_ext
229 end
230 path
259 @destination ||= {}
260 @destination[base_directory] ||= begin
261 path = site.in_dest_dir(base_directory, URL.unescape_path(url))
262 if url.end_with? "/"
263 path = File.join(path, "index.html")
264 else
265 path << output_ext unless path.end_with? output_ext
266 end
267 path
268 end
231269 end
232270
233271 # Write the generated Document file to the destination directory.
285323 #
286324 # Returns the inspect string for this document.
287325 def inspect
288 "#<Jekyll::Document #{relative_path} collection=#{collection.label}>"
326 "#<#{self.class} #{relative_path} collection=#{collection.label}>"
289327 end
290328
291329 # The string representation for this document.
302340 # equal or greater than the other doc's path. See String#<=> for more details.
303341 def <=>(other)
304342 return nil unless other.respond_to?(:data)
343
305344 cmp = data["date"] <=> other.data["date"]
306345 cmp = path <=> other.path if cmp.nil? || cmp.zero?
307346 cmp
313352 # True if the document has a collection and if that collection's #write?
314353 # method returns true, and if the site's Publisher will publish the document.
315354 # False otherwise.
355 #
356 # rubocop:disable Naming/MemoizedInstanceVariableName
316357 def write?
317 collection && collection.write? && site.publisher.publish?(self)
318 end
358 return @write_p if defined?(@write_p)
359
360 @write_p = collection&.write? && site.publisher.publish?(self)
361 end
362 # rubocop:enable Naming/MemoizedInstanceVariableName
319363
320364 # The Document excerpt_separator, from the YAML Front-Matter or site
321365 # default excerpt_separator value
322366 #
323367 # Returns the document excerpt_separator
324368 def excerpt_separator
325 (data["excerpt_separator"] || site.config["excerpt_separator"]).to_s
369 @excerpt_separator ||= (data["excerpt_separator"] || site.config["excerpt_separator"]).to_s
326370 end
327371
328372 # Whether to generate an excerpt
334378
335379 def next_doc
336380 pos = collection.docs.index { |post| post.equal?(self) }
337 if pos && pos < collection.docs.length - 1
338 collection.docs[pos + 1]
339 end
381 collection.docs[pos + 1] if pos && pos < collection.docs.length - 1
340382 end
341383
342384 def previous_doc
343385 pos = collection.docs.index { |post| post.equal?(self) }
344 if pos && pos > 0
345 collection.docs[pos - 1]
346 end
386 collection.docs[pos - 1] if pos && pos.positive?
347387 end
348388
349389 def trigger_hooks(hook_name, *args)
362402 @related_posts ||= Jekyll::RelatedPosts.new(self).build
363403 end
364404
365 # Override of normal respond_to? to match method_missing's logic for
366 # looking in @data.
367 def respond_to?(method, include_private = false)
368 data.key?(method.to_s) || super
369 end
370
371405 # Override of method_missing to check in @data for the key.
372406 def method_missing(method, *args, &blck)
373407 if data.key?(method.to_s)
374 Jekyll::Deprecator.deprecation_message "Document##{method} is now a key "\
375 "in the #data hash."
376 Jekyll::Deprecator.deprecation_message "Called by #{caller(0..0)}."
408 Jekyll::Deprecator.deprecation_message "Document##{method} is now a key in the #data hash."
409 Jekyll.logger.warn "", "Called by #{caller(1..1)[0]}."
377410 data[method.to_s]
378411 else
379412 super
390423 #
391424 # Returns nothing.
392425 def categories_from_path(special_dir)
393 superdirs = relative_path.sub(%r!#{special_dir}(.*)!, "")
394 .split(File::SEPARATOR)
395 .reject do |c|
396 c.empty? || c == special_dir || c == basename
397 end
426 if relative_path.start_with?(special_dir)
427 superdirs = []
428 else
429 superdirs = relative_path.sub(Document.superdirs_regex(special_dir), "")
430 superdirs = superdirs.split(File::SEPARATOR)
431 superdirs.reject! { |c| c.empty? || c == special_dir || c == basename }
432 end
433
398434 merge_data!({ "categories" => superdirs }, :source => "file path")
399435 end
400436
401437 def populate_categories
402 merge_data!({
403 "categories" => (
404 Array(data["categories"]) + Utils.pluralized_array_from_hash(
405 data, "category", "categories"
406 )
407 ).map(&:to_s).flatten.uniq,
408 })
438 categories = Array(data["categories"]) + Utils.pluralized_array_from_hash(
439 data, "category", "categories"
440 )
441 categories.map!(&:to_s)
442 categories.flatten!
443 categories.uniq!
444
445 merge_data!({ "categories" => categories })
409446 end
410447
411448 def populate_tags
412 merge_data!({
413 "tags" => Utils.pluralized_array_from_hash(data, "tag", "tags").flatten,
414 })
449 tags = Utils.pluralized_array_from_hash(data, "tag", "tags")
450 tags.flatten!
451
452 merge_data!({ "tags" => tags })
415453 end
416454
417455 private
456
418457 def merge_categories!(other)
419458 if other.key?("categories") && !other["categories"].nil?
420 if other["categories"].is_a?(String)
421 other["categories"] = other["categories"].split
459 other["categories"] = other["categories"].split if other["categories"].is_a?(String)
460
461 if data["categories"].is_a?(Array)
462 other["categories"] = data["categories"] | other["categories"]
422463 end
423 other["categories"] = (data["categories"] || []) | other["categories"]
424 end
425 end
426
427 private
464 end
465 end
466
428467 def merge_date!(source)
429468 if data.key?("date")
430469 data["date"] = Utils.parse_date(
434473 end
435474 end
436475
437 private
438476 def merge_defaults
439 defaults = @site.frontmatter_defaults.all(
440 relative_path,
441 collection.label.to_sym
442 )
477 defaults = @site.frontmatter_defaults.all(relative_path, type)
443478 merge_data!(defaults, :source => "front matter defaults") unless defaults.empty?
444479 end
445480
446 private
447481 def read_content(**opts)
448482 self.content = File.read(path, **Utils.merged_file_read_opts(site, opts))
449483 if content =~ YAML_FRONT_MATTER_REGEXP
450 self.content = $POSTMATCH
484 self.content = Regexp.last_match.post_match
451485 data_file = SafeYAML.load(Regexp.last_match(1))
452486 merge_data!(data_file, :source => "YAML front matter") if data_file
453487 end
454488 end
455489
456 private
457490 def read_post_data
458491 populate_title
459492 populate_categories
461494 generate_excerpt
462495 end
463496
464 private
465497 def handle_read_error(error)
466498 if error.is_a? Psych::SyntaxError
467499 Jekyll.logger.error "Error:", "YAML Exception reading #{path}: #{error.message}"
474506 end
475507 end
476508
477 private
478509 def populate_title
479510 if relative_path =~ DATE_FILENAME_MATCHER
480511 date, slug, ext = Regexp.last_match.captures
482513 elsif relative_path =~ DATELESS_FILENAME_MATCHER
483514 slug, ext = Regexp.last_match.captures
484515 end
516 # `slug` will be nil for documents without an extension since the regex patterns
517 # above tests for an extension as well.
518 # In such cases, assign `basename_without_ext` as the slug.
519 slug ||= basename_without_ext
520
521 # slugs shouldn't end with a period
522 # `String#gsub!` removes all trailing periods (in comparison to `String#chomp!`)
523 slug.gsub!(%r!\.*\z!, "")
485524
486525 # Try to ensure the user gets a title.
487526 data["title"] ||= Utils.titleize_slug(slug)
490529 data["ext"] ||= ext
491530 end
492531
493 private
494532 def modify_date(date)
495533 if !data["date"] || data["date"].to_i == site.time.to_i
496534 merge_data!({ "date" => date }, :source => "filename")
497535 end
498536 end
499537
500 private
501538 def generate_excerpt
502 if generate_excerpt?
503 data["excerpt"] ||= Jekyll::Excerpt.new(self)
504 end
539 data["excerpt"] ||= Jekyll::Excerpt.new(self) if generate_excerpt?
505540 end
506541 end
507542 end
66
77 mutable false
88
9 def_delegator :@obj, :write?, :output
10 def_delegators :@obj, :label, :docs, :files, :directory,
11 :relative_directory
9 delegate_method_as :write?, :output
10 delegate_methods :label, :docs, :files, :directory, :relative_directory
1211
13 private def_delegator :@obj, :metadata, :fallback_data
12 private delegate_method_as :metadata, :fallback_data
1413
1514 def to_s
1615 docs.to_s
1010
1111 mutable false
1212
13 def_delegator :@obj, :relative_path, :path
14 def_delegators :@obj, :id, :output, :content, :to_s, :relative_path, :url
13 delegate_method_as :relative_path, :path
14 private delegate_method_as :data, :fallback_data
1515
16 private def_delegator :@obj, :data, :fallback_data
16 delegate_methods :id, :output, :content, :to_s, :relative_path, :url, :date
17 data_delegators "title", "categories", "tags"
1718
1819 def collection
1920 @obj.collection.label
2324 fallback_data["excerpt"].to_s
2425 end
2526
27 def name
28 fallback_data["name"] || @obj.basename
29 end
30
2631 def <=>(other)
2732 return nil unless other.is_a? DocumentDrop
33
2834 cmp = self["date"] <=> other["date"]
2935 cmp = self["path"] <=> other["path"] if cmp.nil? || cmp.zero?
3036 cmp
55 include Enumerable
66
77 NON_CONTENT_METHODS = [:fallback_data, :collapse_document].freeze
8
9 # Get or set whether the drop class is mutable.
10 # Mutability determines whether or not pre-defined fields may be
11 # overwritten.
12 #
13 # is_mutable - Boolean set mutability of the class (default: nil)
14 #
15 # Returns the mutability of the class
16 def self.mutable(is_mutable = nil)
17 @is_mutable = if is_mutable
18 is_mutable
19 else
20 false
21 end
22 end
23
24 def self.mutable?
25 @is_mutable
8 NON_CONTENT_METHOD_NAMES = NON_CONTENT_METHODS.map(&:to_s).freeze
9 private_constant :NON_CONTENT_METHOD_NAMES
10
11 # A private stash to avoid repeatedly generating the setter method name string for
12 # a call to `Drops::Drop#[]=`.
13 # The keys of the stash below have a very high probability of being called upon during
14 # the course of various `Jekyll::Renderer#run` calls.
15 SETTER_KEYS_STASH = {
16 "content" => "content=",
17 "layout" => "layout=",
18 "page" => "page=",
19 "paginator" => "paginator=",
20 "highlighter_prefix" => "highlighter_prefix=",
21 "highlighter_suffix" => "highlighter_suffix=",
22 }.freeze
23 private_constant :SETTER_KEYS_STASH
24
25 class << self
26 # Get or set whether the drop class is mutable.
27 # Mutability determines whether or not pre-defined fields may be
28 # overwritten.
29 #
30 # is_mutable - Boolean set mutability of the class (default: nil)
31 #
32 # Returns the mutability of the class
33 def mutable(is_mutable = nil)
34 @is_mutable = is_mutable || false
35 end
36
37 def mutable?
38 @is_mutable
39 end
40
41 # public delegation helper methods that calls onto Drop's instance
42 # variable `@obj`.
43
44 # Generate private Drop instance_methods for each symbol in the given list.
45 #
46 # Returns nothing.
47 def private_delegate_methods(*symbols)
48 symbols.each { |symbol| private delegate_method(symbol) }
49 nil
50 end
51
52 # Generate public Drop instance_methods for each symbol in the given list.
53 #
54 # Returns nothing.
55 def delegate_methods(*symbols)
56 symbols.each { |symbol| delegate_method(symbol) }
57 nil
58 end
59
60 # Generate public Drop instance_method for given symbol that calls `@obj.<sym>`.
61 #
62 # Returns delegated method symbol.
63 def delegate_method(symbol)
64 define_method(symbol) { @obj.send(symbol) }
65 end
66
67 # Generate public Drop instance_method named `delegate` that calls `@obj.<original>`.
68 #
69 # Returns delegated method symbol.
70 def delegate_method_as(original, delegate)
71 define_method(delegate) { @obj.send(original) }
72 end
73
74 # Generate public Drop instance_methods for each string entry in the given list.
75 # The generated method(s) access(es) `@obj`'s data hash.
76 #
77 # Returns nothing.
78 def data_delegators(*strings)
79 strings.each do |key|
80 data_delegator(key) if key.is_a?(String)
81 end
82 nil
83 end
84
85 # Generate public Drop instance_methods for given string `key`.
86 # The generated method access(es) `@obj`'s data hash.
87 #
88 # Returns method symbol.
89 def data_delegator(key)
90 define_method(key.to_sym) { @obj.data[key] }
91 end
92
93 # Array of stringified instance methods that do not end with the assignment operator.
94 #
95 # (<klass>.instance_methods always generates a new Array object so it can be mutated)
96 #
97 # Returns array of strings.
98 def getter_method_names
99 @getter_method_names ||= instance_methods.map!(&:to_s).tap do |list|
100 list.reject! { |item| item.end_with?("=") }
101 end
102 end
26103 end
27104
28105 # Create a new Drop
33110 # Returns nothing
34111 def initialize(obj)
35112 @obj = obj
36 @mutations = {} # only if mutable: true
37113 end
38114
39115 # Access a method in the Drop or a field in the underlying hash data.
45121 #
46122 # Returns the value for the given key, or nil if none exists
47123 def [](key)
48 if self.class.mutable? && @mutations.key?(key)
49 @mutations[key]
124 if self.class.mutable? && mutations.key?(key)
125 mutations[key]
50126 elsif self.class.invokable? key
51127 public_send key
52128 else
69145 # and the key matches a method in which case it raises a
70146 # DropMutationException.
71147 def []=(key, val)
72 if respond_to?("#{key}=")
73 public_send("#{key}=", val)
148 setter = SETTER_KEYS_STASH[key] || "#{key}="
149 if respond_to?(setter)
150 public_send(setter, val)
74151 elsif respond_to?(key.to_s)
75152 if self.class.mutable?
76 @mutations[key] = val
153 mutations[key] = val
77154 else
78155 raise Errors::DropMutationException, "Key #{key} cannot be set in the drop."
79156 end
87164 #
88165 # Returns an Array of strings which represent method-specific keys.
89166 def content_methods
90 @content_methods ||= (
91 self.class.instance_methods \
92 - Jekyll::Drops::Drop.instance_methods \
93 - NON_CONTENT_METHODS
94 ).map(&:to_s).reject do |method|
95 method.end_with?("=")
96 end
167 @content_methods ||= \
168 self.class.getter_method_names \
169 - Jekyll::Drops::Drop.getter_method_names \
170 - NON_CONTENT_METHOD_NAMES
97171 end
98172
99173 # Check if key exists in Drop
103177 # Returns true if the given key is present
104178 def key?(key)
105179 return false if key.nil?
106 return true if self.class.mutable? && @mutations.key?(key)
180 return true if self.class.mutable? && mutations.key?(key)
181
107182 respond_to?(key) || fallback_data.key?(key)
108183 end
109184
115190 # Returns an Array of unique keys for content for the Drop.
116191 def keys
117192 (content_methods |
118 @mutations.keys |
193 mutations.keys |
119194 fallback_data.keys).flatten
120195 end
121196
172247 end
173248
174249 def merge(other, &block)
175 self.dup.tap do |me|
250 dup.tap do |me|
176251 if block.nil?
177252 me.merge!(other)
178253 else
206281 return yield(key) unless block.nil?
207282 return default unless default.nil?
208283 end
284
285 private
286
287 def mutations
288 @mutations ||= {}
289 end
209290 end
210291 end
211292 end
66 @obj.doc.data["layout"]
77 end
88
9 def date
10 @obj.doc.date
11 end
12
913 def excerpt
1014 nil
15 end
16
17 def name
18 @obj.doc.data["name"] || @obj.doc.basename
1119 end
1220 end
1321 end
66
77 mutable false
88
9 def_delegator :@obj, :site_data, :data
10 def_delegators :@obj, :time, :pages, :static_files, :tags, :categories
9 delegate_method_as :site_data, :data
10 delegate_methods :time, :pages, :static_files, :tags, :categories
1111
12 private def_delegator :@obj, :config, :fallback_data
12 private delegate_method_as :config, :fallback_data
1313
1414 def [](key)
15 if @obj.collections.key?(key) && key != "posts"
15 if key != "posts" && @obj.collections.key?(key)
1616 @obj.collections[key].docs
1717 else
1818 super(key)
2020 end
2121
2222 def key?(key)
23 (@obj.collections.key?(key) && key != "posts") || super
23 (key != "posts" && @obj.collections.key?(key)) || super
2424 end
2525
2626 def posts
4040 # `Site#documents` cannot be memoized so that `Site#docs_to_write` can access the
4141 # latest state of the attribute.
4242 #
43 # Since this method will be called after `Site#pre_render` hook,
44 # the `Site#documents` array shouldn't thereafter change and can therefore be
45 # safely memoized to prevent additional computation of `Site#documents`.
43 # Since this method will be called after `Site#pre_render` hook, the `Site#documents`
44 # array shouldn't thereafter change and can therefore be safely memoized to prevent
45 # additional computation of `Site#documents`.
4646 def documents
4747 @documents ||= @obj.documents
4848 end
5353 # We should remove this in 4.0 and switch to `{{ post.related_posts }}`.
5454 def related_posts
5555 return nil unless @current_document.is_a?(Jekyll::Document)
56
5657 @current_document.related_posts
5758 end
5859 attr_writer :current_document
33 module Drops
44 class StaticFileDrop < Drop
55 extend Forwardable
6 def_delegators :@obj, :name, :extname, :modified_time, :basename
7 def_delegator :@obj, :relative_path, :path
8 def_delegator :@obj, :type, :collection
6 delegate_methods :name, :extname, :modified_time, :basename
7 delegate_method_as :relative_path, :path
8 delegate_method_as :type, :collection
99
10 private def_delegator :@obj, :data, :fallback_data
10 private delegate_method_as :data, :fallback_data
1111 end
1212 end
1313 end
0 # frozen_string_literal: true
1
2 module Jekyll
3 module Drops
4 class ThemeDrop < Drop
5 delegate_methods :root
6 delegate_method_as :runtime_dependencies, :dependencies
7
8 def authors
9 @authors ||= gemspec.authors.join(", ")
10 end
11
12 def version
13 @version ||= gemspec.version.to_s
14 end
15
16 def description
17 @description ||= gemspec.description || gemspec.summary
18 end
19
20 def metadata
21 @metadata ||= gemspec.metadata
22 end
23
24 private
25
26 def gemspec
27 @gemspec ||= @obj.send(:gemspec)
28 end
29
30 def fallback_data
31 @fallback_data ||= {}
32 end
33 end
34 end
35 end
44 class UnifiedPayloadDrop < Drop
55 mutable true
66
7 attr_accessor :page, :layout, :content, :paginator
8 attr_accessor :highlighter_prefix, :highlighter_suffix
7 attr_accessor :content, :page, :layout, :paginator,
8 :highlighter_prefix, :highlighter_suffix
99
1010 def jekyll
1111 JekyllDrop.global
1515 @site_drop ||= SiteDrop.new(@obj)
1616 end
1717
18 def theme
19 @theme_drop ||= ThemeDrop.new(@obj.theme) if @obj.theme
20 end
21
1822 private
23
1924 def fallback_data
2025 @fallback_data ||= {}
2126 end
66
77 mutable false
88
9 def_delegator :@obj, :cleaned_relative_path, :path
10 def_delegator :@obj, :output_ext, :output_ext
9 delegate_method :output_ext
10 delegate_method_as :cleaned_relative_path, :path
1111
1212 def collection
1313 @obj.collection.label
3434 category_set.to_a.join("/")
3535 end
3636
37 # Similar to output from #categories, but each category will be downcased and
38 # all non-alphanumeric characters of the category replaced with a hyphen.
39 def slugified_categories
40 Array(@obj.data["categories"]).each_with_object(Set.new) do |category, set|
41 set << Utils.slugify(category.to_s)
42 end.to_a.join("/")
43 end
44
45 # CCYY
3746 def year
3847 @obj.date.strftime("%Y")
3948 end
4049
50 # MM: 01..12
4151 def month
4252 @obj.date.strftime("%m")
4353 end
4454
55 # DD: 01..31
4556 def day
4657 @obj.date.strftime("%d")
4758 end
4859
60 # hh: 00..23
4961 def hour
5062 @obj.date.strftime("%H")
5163 end
5264
65 # mm: 00..59
5366 def minute
5467 @obj.date.strftime("%M")
5568 end
5669
70 # ss: 00..59
5771 def second
5872 @obj.date.strftime("%S")
5973 end
6074
75 # D: 1..31
6176 def i_day
6277 @obj.date.strftime("%-d")
6378 end
6479
80 # M: 1..12
6581 def i_month
6682 @obj.date.strftime("%-m")
6783 end
6884
85 # MMM: Jan..Dec
6986 def short_month
7087 @obj.date.strftime("%b")
7188 end
7289
90 # MMMM: January..December
91 def long_month
92 @obj.date.strftime("%B")
93 end
94
95 # YY: 00..99
7396 def short_year
7497 @obj.date.strftime("%y")
7598 end
7699
100 # CCYYw, ISO week year
101 # may differ from CCYY for the first days of January and last days of December
102 def w_year
103 @obj.date.strftime("%G")
104 end
105
106 # WW: 01..53
107 # %W and %U do not comply with ISO 8601-1
108 def week
109 @obj.date.strftime("%V")
110 end
111
112 # d: 1..7 (Monday..Sunday)
113 def w_day
114 @obj.date.strftime("%u")
115 end
116
117 # dd: Mon..Sun
118 def short_day
119 @obj.date.strftime("%a")
120 end
121
122 # ddd: Monday..Sunday
123 def long_day
124 @obj.date.strftime("%A")
125 end
126
127 # DDD: 001..366
77128 def y_day
78129 @obj.date.strftime("%j")
79130 end
80131
81132 private
133
82134 def fallback_data
83 {}
135 @fallback_data ||= {}
84136 end
85137 end
86138 end
22 module Jekyll
33 class EntryFilter
44 attr_reader :site
5 SPECIAL_LEADING_CHARACTERS = [
6 ".", "_", "#", "~",
7 ].freeze
5
6 SPECIAL_LEADING_CHAR_REGEX = %r!\A#{Regexp.union([".", "_", "#", "~"])}!o.freeze
87
98 def initialize(site, base_directory = nil)
109 @site = site
3029
3130 def filter(entries)
3231 entries.reject do |e|
33 # Reject this entry if it is a symlink.
32 # Reject this entry if it is just a "dot" representation.
33 # e.g.: '.', '..', '_movies/.', 'music/..', etc
34 next true if e.end_with?(".")
35
36 # Check if the current entry is explicitly included and cache the result
37 included = included?(e)
38
39 # Reject current entry if it is excluded but not explicitly included as well.
40 next true if excluded?(e) && !included
41
42 # Reject current entry if it is a symlink.
3443 next true if symlink?(e)
35 # Do not reject this entry if it is included.
36 next false if included?(e)
37 # Reject this entry if it is special, a backup file, or excluded.
38 special?(e) || backup?(e) || excluded?(e)
44
45 # Do not reject current entry if it is explicitly included.
46 next false if included
47
48 # Reject current entry if it is special or a backup file.
49 special?(e) || backup?(e)
3950 end
4051 end
4152
4556 end
4657
4758 def special?(entry)
48 SPECIAL_LEADING_CHARACTERS.include?(entry[0..0]) ||
49 SPECIAL_LEADING_CHARACTERS.include?(File.basename(entry)[0..0])
59 SPECIAL_LEADING_CHAR_REGEX.match?(entry) ||
60 SPECIAL_LEADING_CHAR_REGEX.match?(File.basename(entry))
5061 end
5162
5263 def backup?(entry)
53 entry[-1..-1] == "~"
64 entry.end_with?("~")
5465 end
5566
5667 def excluded?(entry)
57 glob_include?(site.exclude, relative_to_source(entry)).tap do |excluded|
68 glob_include?(site.exclude - site.include, relative_to_source(entry)).tap do |excluded|
5869 if excluded
5970 Jekyll.logger.debug(
6071 "EntryFilter:",
7485 end
7586
7687 # --
77 # NOTE: Pathutil#in_path? gets the realpath.
78 # @param [<Anything>] entry the entry you want to validate.
79 # Check if a path is outside of our given root.
88 # Check if given path is outside of current site's configured source directory.
8089 # --
8190 def symlink_outside_site_source?(entry)
82 !Pathutil.new(entry).in_path?(
83 site.in_source_dir
84 )
91 !File.realpath(entry).start_with?(site.in_source_dir)
8592 end
8693
87 # --
88 # Check if an entry matches a specific pattern and return true,false.
89 # Returns true if path matches against any glob pattern.
90 # --
91 def glob_include?(enum, entry)
92 entry_path = Pathutil.new(site.in_source_dir).join(entry)
93 enum.any? do |exp|
94 # Users who send a Regexp knows what they want to
95 # exclude, so let them send a Regexp to exclude files,
96 # we will not bother caring if it works or not, it's
97 # on them at this point.
94 # Check if an entry matches a specific pattern.
95 # Returns true if path matches against any glob pattern, else false.
96 def glob_include?(enumerator, entry)
97 entry_with_source = PathManager.join(site.source, entry)
98 entry_is_directory = File.directory?(entry_with_source)
9899
99 if exp.is_a?(Regexp)
100 entry_path =~ exp
100 enumerator.any? do |pattern|
101 case pattern
102 when String
103 pattern_with_source = PathManager.join(site.source, pattern)
101104
105 File.fnmatch?(pattern_with_source, entry_with_source) ||
106 entry_with_source.start_with?(pattern_with_source) ||
107 (pattern_with_source == "#{entry_with_source}/" if entry_is_directory)
108 when Regexp
109 pattern.match?(entry_with_source)
102110 else
103 item = Pathutil.new(site.in_source_dir).join(exp)
104
105 # If it's a directory they want to exclude, AKA
106 # ends with a "/" then we will go on to check and
107 # see if the entry falls within that path and
108 # exclude it if that's the case.
109
110 if entry.end_with?("/")
111 entry_path.in_path?(
112 item
113 )
114
115 else
116 File.fnmatch?(item, entry_path) ||
117 entry_path.to_path.start_with?(
118 item
119 )
120 end
111 false
121112 end
122113 end
123114 end
33 class Excerpt
44 extend Forwardable
55
6 attr_accessor :doc
7 attr_accessor :content, :ext
6 attr_accessor :content, :doc, :ext
87 attr_writer :output
98
10 def_delegators :@doc, :site, :name, :ext, :extname,
11 :collection, :related_posts,
12 :coffeescript_file?, :yaml_file?,
13 :url, :next_doc, :previous_doc
9 def_delegators :@doc,
10 :site, :name, :ext, :extname,
11 :collection, :related_posts, :type,
12 :coffeescript_file?, :yaml_file?,
13 :url, :next_doc, :previous_doc
1414
1515 private :coffeescript_file?, :yaml_file?
1616
4747 #
4848 # Returns the relative_path for the doc this excerpt belongs to with #excerpt appended
4949 def relative_path
50 File.join(doc.relative_path, "#excerpt")
50 @relative_path ||= File.join(doc.relative_path, "#excerpt")
5151 end
5252
5353 # Check if excerpt includes a string
5454 #
5555 # Returns true if the string passed in
5656 def include?(something)
57 (output && output.include?(something)) || content.include?(something)
57 output&.include?(something) || content.include?(something)
5858 end
5959
6060 # The UID for this doc (useful in feeds).
7575
7676 # Returns the shorthand String identifier of this doc.
7777 def inspect
78 "<Excerpt: #{self.id}>"
78 "<#{self.class} id=#{id}>"
7979 end
8080
8181 def output
8787 end
8888
8989 def render_with_liquid?
90 return false if data["render_with_liquid"] == false
91
9092 !(coffeescript_file? || yaml_file? || !Utils.has_liquid_construct?(content))
9193 end
9294
127129 #
128130 # Returns excerpt String
129131
130 LIQUID_TAG_REGEX = %r!{%-?\s*(\w+)\s*.*?-?%}!m
131 MKDWN_LINK_REF_REGEX = %r!^ {0,3}\[[^\]]+\]:.+$!
132 LIQUID_TAG_REGEX = %r!{%-?\s*(\w+)\s*.*?-?%}!m.freeze
133 MKDWN_LINK_REF_REGEX = %r!^ {0,3}(?:(\[[^\]]+\])(:.+))$!.freeze
132134
133135 def extract_excerpt(doc_content)
134136 head, _, tail = doc_content.to_s.partition(doc.excerpt_separator)
135
136 # append appropriate closing tag(s) (for each Liquid block), to the `head`
137 # if the partitioning resulted in leaving the closing tag somewhere
138 # in the `tail` partition.
139
140 if head.include?("{%")
141 modified = false
142 tag_names = head.scan(LIQUID_TAG_REGEX)
143 tag_names.flatten!
144 tag_names.reverse_each do |tag_name|
145 next unless liquid_block?(tag_name)
146 next if head =~ endtag_regex_stash(tag_name)
147
148 modified = true
149 head << "\n{% end#{tag_name} %}"
137 return head if tail.empty?
138
139 head = sanctify_liquid_tags(head) if head.include?("{%")
140 definitions = extract_markdown_link_reference_definitions(head, tail)
141 return head if definitions.empty?
142
143 head << "\n\n" << definitions.join("\n")
144 end
145
146 private
147
148 # append appropriate closing tag(s) (for each Liquid block), to the `head` if the
149 # partitioning resulted in leaving the closing tag somewhere in the `tail` partition.
150 def sanctify_liquid_tags(head)
151 modified = false
152 tag_names = head.scan(LIQUID_TAG_REGEX)
153 tag_names.flatten!
154 tag_names.reverse_each do |tag_name|
155 next unless liquid_block?(tag_name)
156 next if endtag_regex_stash(tag_name).match?(head)
157
158 modified = true
159 head << "\n{% end#{tag_name} %}"
160 end
161
162 print_build_warning if modified
163 head
164 end
165
166 def extract_markdown_link_reference_definitions(head, tail)
167 [].tap do |definitions|
168 tail.scan(MKDWN_LINK_REF_REGEX).each do |segments|
169 definitions << segments.join if head.include?(segments[0])
150170 end
151 print_build_warning if modified
152171 end
153
154 return head if tail.empty?
155
156 head << "\n\n" << tail.scan(MKDWN_LINK_REF_REGEX).join("\n")
157 end
158
159 private
172 end
160173
161174 def endtag_regex_stash(tag_name)
162175 @endtag_regex_stash ||= {}
170183 Liquid::Template.tags[tag_name].ancestors.include?(Liquid::Block)
171184 rescue NoMethodError
172185 Jekyll.logger.error "Error:",
173 "A Liquid tag in the excerpt of #{doc.relative_path} couldn't be " \
174 "parsed."
186 "A Liquid tag in the excerpt of #{doc.relative_path} couldn't be parsed."
175187 raise
176188 end
177189
178190 def print_build_warning
179191 Jekyll.logger.warn "Warning:", "Excerpt modified in #{doc.relative_path}!"
180 Jekyll.logger.warn "", "Found a Liquid block containing the excerpt separator" \
181 " #{doc.excerpt_separator.inspect}. "
182 Jekyll.logger.warn "", "The block has been modified with the appropriate" \
183 " closing tag."
184 Jekyll.logger.warn "", "Feel free to define a custom excerpt or" \
185 " excerpt_separator in the document's front matter" \
186 " if the generated excerpt is unsatisfactory."
192 Jekyll.logger.warn "", "Found a Liquid block containing the excerpt separator " \
193 "#{doc.excerpt_separator.inspect}."
194 Jekyll.logger.warn "", "The block has been modified with the appropriate closing tag."
195 Jekyll.logger.warn "", "Feel free to define a custom excerpt or excerpt_separator in the"
196 Jekyll.logger.warn "", "document's Front Matter if the generated excerpt is unsatisfactory."
187197 end
188198 end
189199 end
88 #
99 def blessed_gems
1010 %w(
11 jekyll-compose
1112 jekyll-docs
1213 jekyll-import
1314 )
2021 #
2122 def require_if_present(names)
2223 Array(names).each do |name|
23 begin
24 require name
25 rescue LoadError
26 Jekyll.logger.debug "Couldn't load #{name}. Skipping."
27 yield(name, version_constraint(name)) if block_given?
28 false
29 end
24 require name
25 rescue LoadError
26 Jekyll.logger.debug "Couldn't load #{name}. Skipping."
27 yield(name, version_constraint(name)) if block_given?
28 false
3029 end
3130 end
3231
4039 # RubyGems.
4140 def version_constraint(gem_name)
4241 return "= #{Jekyll::VERSION}" if gem_name.to_s.eql?("jekyll-docs")
42
4343 "> 0"
4444 end
4545
5252 #
5353 def require_with_graceful_fail(names)
5454 Array(names).each do |name|
55 begin
56 Jekyll.logger.debug "Requiring:", name.to_s
57 require name
58 rescue LoadError => e
59 Jekyll.logger.error "Dependency Error:", <<-MSG
60 Yikes! It looks like you don't have #{name} or one of its dependencies installed.
61 In order to use Jekyll as currently configured, you'll need to install this gem.
55 Jekyll.logger.debug "Requiring:", name.to_s
56 require name
57 rescue LoadError => e
58 Jekyll.logger.error "Dependency Error:", <<~MSG
59 Yikes! It looks like you don't have #{name} or one of its dependencies installed.
60 In order to use Jekyll as currently configured, you'll need to install this gem.
6261
63 The full error message from Ruby is: '#{e.message}'
62 If you've run Jekyll with `bundle exec`, ensure that you have included the #{name}
63 gem in your Gemfile as well.
6464
65 If you run into trouble, you can find helpful resources at https://jekyllrb.com/help/!
66 MSG
67 raise Jekyll::Errors::MissingDependencyException, name
68 end
65 The full error message from Ruby is: '#{e.message}'
66
67 If you run into trouble, you can find helpful resources at https://jekyllrb.com/help/!
68 MSG
69 raise Jekyll::Errors::MissingDependencyException, name
6970 end
7071 end
7172 end
4444 # Returns the formatted String.
4545 def date_to_xmlschema(date)
4646 return date if date.to_s.empty?
47
4748 time(date).xmlschema
4849 end
4950
5960 # Returns the formatted String.
6061 def date_to_rfc822(date)
6162 return date if date.to_s.empty?
63
6264 time(date).rfc822
6365 end
6466
6567 private
68
6669 # month_type: Notations that evaluate to 'Month' via `Time#strftime` ("%b", "%B")
6770 # type: nil (default) or "ordinal"
6871 # style: nil (default) or "US"
7073 # Returns a stringified date or the empty input.
7174 def stringify_date(date, month_type, type = nil, style = nil)
7275 return date if date.to_s.empty?
76
7377 time = time(date)
7478 if type == "ordinal"
7579 day = time.day
7680 ordinal_day = "#{day}#{ordinal(day)}"
7781 return time.strftime("#{month_type} #{ordinal_day}, %Y") if style == "US"
82
7883 return time.strftime("#{ordinal_day} #{month_type} %Y")
7984 end
8085 time.strftime("%d #{month_type} %Y")
8186 end
8287
83 private
8488 def ordinal(number)
8589 return "th" if (11..13).cover?(number)
8690
9296 end
9397 end
9498
95 private
9699 def time(input)
97100 date = Liquid::Utils.to_date(input)
98101 unless date.respond_to?(:to_time)
99102 raise Errors::InvalidDateError,
100 "Invalid Date: '#{input.inspect}' is not a valid datetime."
103 "Invalid Date: '#{input.inspect}' is not a valid datetime."
101104 end
102105 date.to_time.dup.localtime
103106 end
4040 end
4141
4242 private
43
4344 def parse_expression(str)
4445 Liquid::Variable.new(str, Liquid::ParseContext.new)
4546 end
4647
47 private
4848 def groupable?(element)
4949 element.respond_to?(:group_by)
5050 end
5151
52 private
5352 def grouped_array(groups)
5453 groups.each_with_object([]) do |item, array|
5554 array << {
99 # Returns the absolute URL as a String.
1010 def absolute_url(input)
1111 return if input.nil?
12 input = input.url if input.respond_to?(:url)
13 return input if Addressable::URI.parse(input.to_s).absolute?
14 site = @context.registers[:site]
15 return relative_url(input) if site.config["url"].nil?
16 Addressable::URI.parse(
17 site.config["url"].to_s + relative_url(input)
18 ).normalize.to_s
12
13 cache = if input.is_a?(String)
14 (@context.registers[:site].filter_cache[:absolute_url] ||= {})
15 else
16 (@context.registers[:cached_absolute_url] ||= {})
17 end
18 cache[input] ||= compute_absolute_url(input)
19
20 # Duplicate cached string so that the cached value is never mutated by
21 # a subsequent filter.
22 cache[input].dup
1923 end
2024
2125 # Produces a URL relative to the domain root based on site.baseurl
2630 # Returns a URL relative to the domain root as a String.
2731 def relative_url(input)
2832 return if input.nil?
29 input = input.url if input.respond_to?(:url)
30 return input if Addressable::URI.parse(input.to_s).absolute?
3133
32 parts = [sanitized_baseurl, input]
33 Addressable::URI.parse(
34 parts.compact.map { |part| ensure_leading_slash(part.to_s) }.join
35 ).normalize.to_s
34 cache = if input.is_a?(String)
35 (@context.registers[:site].filter_cache[:relative_url] ||= {})
36 else
37 (@context.registers[:cached_relative_url] ||= {})
38 end
39 cache[input] ||= compute_relative_url(input)
40
41 # Duplicate cached string so that the cached value is never mutated by
42 # a subsequent filter.
43 cache[input].dup
3644 end
3745
3846 # Strips trailing `/index.html` from URLs to create pretty permalinks
4250 # Returns a URL with the trailing `/index.html` removed
4351 def strip_index(input)
4452 return if input.nil? || input.to_s.empty?
53
4554 input.sub(%r!/index\.html?$!, "/")
4655 end
4756
4857 private
4958
59 def compute_absolute_url(input)
60 input = input.url if input.respond_to?(:url)
61 return input if Addressable::URI.parse(input.to_s).absolute?
62
63 site = @context.registers[:site]
64 site_url = site.config["url"]
65 return relative_url(input) if site_url.nil? || site_url == ""
66
67 Addressable::URI.parse(
68 site_url.to_s + relative_url(input)
69 ).normalize.to_s
70 end
71
72 def compute_relative_url(input)
73 input = input.url if input.respond_to?(:url)
74 return input if Addressable::URI.parse(input.to_s).absolute?
75
76 parts = [sanitized_baseurl, input]
77 Addressable::URI.parse(
78 parts.map! { |part| ensure_leading_slash(part.to_s) }.join
79 ).normalize.to_s
80 end
81
5082 def sanitized_baseurl
5183 site = @context.registers[:site]
52 site.config["baseurl"].to_s.chomp("/")
84 baseurl = site.config["baseurl"]
85 return "" if baseurl.nil?
86
87 baseurl.to_s.chomp("/")
5388 end
5489
5590 def ensure_leading_slash(input)
5691 return input if input.nil? || input.empty? || input.start_with?("/")
92
5793 "/#{input}"
5894 end
59
6095 end
6196 end
6297 end
112112 #
113113 # Returns the formatted String
114114 def normalize_whitespace(input)
115 input.to_s.gsub(%r!\s+!, " ").strip
115 input.to_s.gsub(%r!\s+!, " ").tap(&:strip!)
116116 end
117117
118118 # Count the number of words in the input string.
120120 # input - The String on which to operate.
121121 #
122122 # Returns the Integer word count.
123 def number_of_words(input)
124 input.split.length
123 def number_of_words(input, mode = nil)
124 cjk_charset = '\p{Han}\p{Katakana}\p{Hiragana}\p{Hangul}'
125 cjk_regex = %r![#{cjk_charset}]!o
126 word_regex = %r![^#{cjk_charset}\s]+!o
127
128 case mode
129 when "cjk"
130 input.scan(cjk_regex).length + input.scan(word_regex).length
131 when "auto"
132 cjk_count = input.scan(cjk_regex).length
133 cjk_count.zero? ? input.split.length : cjk_count + input.scan(word_regex).length
134 else
135 input.split.length
136 end
125137 end
126138
127139 # Join an array of things into a string by separating with commas and the
160172
161173 # Filter an array of objects
162174 #
163 # input - the object array
164 # property - property within each object to filter by
165 # value - desired value
175 # input - the object array.
176 # property - the property within each object to filter by.
177 # value - the desired value.
178 # Cannot be an instance of Array nor Hash since calling #to_s on them returns
179 # their `#inspect` string object.
166180 #
167181 # Returns the filtered array of objects
168182 def where(input, property, value)
169 return input if property.nil? || value.nil?
183 return input if !property || value.is_a?(Array) || value.is_a?(Hash)
170184 return input unless input.respond_to?(:select)
185
171186 input = input.values if input.is_a?(Hash)
172187 input_id = input.hash
173188
177192 @where_filter_cache[input_id] ||= {}
178193 @where_filter_cache[input_id][property] ||= {}
179194
180 # stash or retrive results to return
181 @where_filter_cache[input_id][property][value] ||= begin
182 input.select do |object|
183 Array(item_property(object, property)).map!(&:to_s).include?(value.to_s)
184 end || []
185 end
195 # stash or retrieve results to return
196 @where_filter_cache[input_id][property][value] ||= input.select do |object|
197 compare_property_vs_target(item_property(object, property), value)
198 end.to_a
186199 end
187200
188201 # Filters an array of objects against an expression
194207 # Returns the filtered array of objects
195208 def where_exp(input, variable, expression)
196209 return input unless input.respond_to?(:select)
210
197211 input = input.values if input.is_a?(Hash) # FIXME
198212
199213 condition = parse_condition(expression)
205219 end || []
206220 end
207221
222 # Search an array of objects and returns the first object that has the queried attribute
223 # with the given value or returns nil otherwise.
224 #
225 # input - the object array.
226 # property - the property within each object to search by.
227 # value - the desired value.
228 # Cannot be an instance of Array nor Hash since calling #to_s on them returns
229 # their `#inspect` string object.
230 #
231 # Returns the found object or nil
232 #
233 # rubocop:disable Metrics/CyclomaticComplexity
234 def find(input, property, value)
235 return input if !property || value.is_a?(Array) || value.is_a?(Hash)
236 return input unless input.respond_to?(:find)
237
238 input = input.values if input.is_a?(Hash)
239 input_id = input.hash
240
241 # implement a hash based on method parameters to cache the end-result for given parameters.
242 @find_filter_cache ||= {}
243 @find_filter_cache[input_id] ||= {}
244 @find_filter_cache[input_id][property] ||= {}
245
246 # stash or retrieve results to return
247 # Since `enum.find` can return nil or false, we use a placeholder string "<__NO MATCH__>"
248 # to validate caching.
249 result = @find_filter_cache[input_id][property][value] ||= input.find do |object|
250 compare_property_vs_target(item_property(object, property), value)
251 end || "<__NO MATCH__>"
252
253 return nil if result == "<__NO MATCH__>"
254
255 result
256 end
257 # rubocop:enable Metrics/CyclomaticComplexity
258
259 # Searches an array of objects against an expression and returns the first object for which
260 # the expression evaluates to true, or returns nil otherwise.
261 #
262 # input - the object array
263 # variable - the variable to assign each item to in the expression
264 # expression - a Liquid comparison expression passed in as a string
265 #
266 # Returns the found object or nil
267 def find_exp(input, variable, expression)
268 return input unless input.respond_to?(:find)
269
270 input = input.values if input.is_a?(Hash)
271
272 condition = parse_condition(expression)
273 @context.stack do
274 input.find do |object|
275 @context[variable] = object
276 condition.evaluate(@context)
277 end
278 end
279 end
280
208281 # Convert the input into integer
209282 #
210283 # input - the object string
213286 def to_integer(input)
214287 return 1 if input == true
215288 return 0 if input == false
289
216290 input.to_i
217291 end
218292
224298 #
225299 # Returns the filtered array of objects
226300 def sort(input, property = nil, nils = "first")
227 if input.nil?
228 raise ArgumentError, "Cannot sort a null object."
229 end
301 raise ArgumentError, "Cannot sort a null object." if input.nil?
302
230303 if property.nil?
231304 input.sort
232305 else
233 if nils == "first"
306 case nils
307 when "first"
234308 order = - 1
235 elsif nils == "last"
309 when "last"
236310 order = + 1
237311 else
238312 raise ArgumentError, "Invalid nils order: " \
239 "'#{nils}' is not a valid nils order. It must be 'first' or 'last'."
313 "'#{nils}' is not a valid nils order. It must be 'first' or 'last'."
240314 end
241315
242316 sort_input(input, property, order)
245319
246320 def pop(array, num = 1)
247321 return array unless array.is_a?(Array)
322
248323 num = Liquid::Utils.to_integer(num)
249324 new_ary = array.dup
250325 new_ary.pop(num)
253328
254329 def push(array, input)
255330 return array unless array.is_a?(Array)
331
256332 new_ary = array.dup
257333 new_ary.push(input)
258334 new_ary
260336
261337 def shift(array, num = 1)
262338 return array unless array.is_a?(Array)
339
263340 num = Liquid::Utils.to_integer(num)
264341 new_ary = array.dup
265342 new_ary.shift(num)
268345
269346 def unshift(array, input)
270347 return array unless array.is_a?(Array)
348
271349 new_ary = array.dup
272350 new_ary.unshift(input)
273351 new_ary
275353
276354 def sample(input, num = 1)
277355 return input unless input.respond_to?(:sample)
356
278357 num = Liquid::Utils.to_integer(num) rescue 1
279358 if num == 1
280359 input.sample
300379 # We also utilize the Schwartzian transform to make this more efficient.
301380 def sort_input(input, property, order)
302381 input.map { |item| [item_property(item, property), item] }
303 .sort! do |apple_info, orange_info|
304 apple_property = apple_info.first
305 orange_property = orange_info.first
306
307 if !apple_property.nil? && orange_property.nil?
382 .sort! do |a_info, b_info|
383 a_property = a_info.first
384 b_property = b_info.first
385
386 if !a_property.nil? && b_property.nil?
308387 - order
309 elsif apple_property.nil? && !orange_property.nil?
388 elsif a_property.nil? && !b_property.nil?
310389 + order
311390 else
312 apple_property <=> orange_property
391 a_property <=> b_property || a_property.to_s <=> b_property.to_s
313392 end
314393 end
315394 .map!(&:last)
316395 end
317396
318 private
397 # `where` filter helper
398 #
399 def compare_property_vs_target(property, target)
400 case target
401 when NilClass
402 return true if property.nil?
403 when Liquid::Expression::MethodLiteral # `empty` or `blank`
404 target = target.to_s
405 return true if property == target || Array(property).join == target
406 else
407 target = target.to_s
408 if property.is_a? String
409 return true if property == target
410 else
411 Array(property).each do |prop|
412 return true if prop.to_s == target
413 end
414 end
415 end
416
417 false
418 end
419
319420 def item_property(item, property)
320 if item.respond_to?(:to_liquid)
321 property.to_s.split(".").reduce(item.to_liquid) do |subvalue, attribute|
322 subvalue[attribute]
323 end
324 elsif item.respond_to?(:data)
325 item.data[property.to_s]
326 else
327 item[property.to_s]
328 end
329 end
330
331 private
421 @item_property_cache ||= @context.registers[:site].filter_cache[:item_property] ||= {}
422 @item_property_cache[property] ||= {}
423 @item_property_cache[property][item] ||= begin
424 property = property.to_s
425 property = if item.respond_to?(:to_liquid)
426 read_liquid_attribute(item.to_liquid, property)
427 elsif item.respond_to?(:data)
428 item.data[property]
429 else
430 item[property]
431 end
432
433 parse_sort_input(property)
434 end
435 end
436
437 def read_liquid_attribute(liquid_data, property)
438 return liquid_data[property] unless property.include?(".")
439
440 property.split(".").reduce(liquid_data) do |data, key|
441 data.respond_to?(:[]) && data[key]
442 end
443 end
444
445 FLOAT_LIKE = %r!\A\s*-?(?:\d+\.?\d*|\.\d+)\s*\Z!.freeze
446 INTEGER_LIKE = %r!\A\s*-?\d+\s*\Z!.freeze
447 private_constant :FLOAT_LIKE, :INTEGER_LIKE
448
449 # return numeric values as numbers for proper sorting
450 def parse_sort_input(property)
451 stringified = property.to_s
452 return property.to_i if INTEGER_LIKE.match?(stringified)
453 return property.to_f if FLOAT_LIKE.match?(stringified)
454
455 property
456 end
457
332458 def as_liquid(item)
333459 case item
334460 when Hash
335 pairs = item.map { |k, v| as_liquid([k, v]) }
336 Hash[pairs]
461 item.each_with_object({}) { |(k, v), result| result[as_liquid(k)] = as_liquid(v) }
337462 when Array
338463 item.map { |i| as_liquid(i) }
339464 else
351476 end
352477 end
353478
479 # ----------- The following set of code was *adapted* from Liquid::If
480 # ----------- ref: https://github.com/Shopify/liquid/blob/ffb0ace30315bbcf3548a0383fab531452060ae8/lib/liquid/tags/if.rb#L84-L107
481
354482 # Parse a string to a Liquid Condition
355 private
356483 def parse_condition(exp)
357 parser = Liquid::Parser.new(exp)
358 left_expr = parser.expression
359 operator = parser.consume?(:comparison)
360 condition =
361 if operator
362 Liquid::Condition.new(Liquid::Expression.parse(left_expr),
363 operator,
364 Liquid::Expression.parse(parser.expression))
365 else
366 Liquid::Condition.new(Liquid::Expression.parse(left_expr))
367 end
484 parser = Liquid::Parser.new(exp)
485 condition = parse_binary_comparison(parser)
486
368487 parser.consume(:end_of_string)
369
370488 condition
371489 end
372490
491 # Generate a Liquid::Condition object from a Liquid::Parser object additionally processing
492 # the parsed expression based on whether the expression consists of binary operations with
493 # Liquid operators `and` or `or`
494 #
495 # - parser: an instance of Liquid::Parser
496 #
497 # Returns an instance of Liquid::Condition
498 def parse_binary_comparison(parser)
499 condition = parse_comparison(parser)
500 first_condition = condition
501 while (binary_operator = parser.id?("and") || parser.id?("or"))
502 child_condition = parse_comparison(parser)
503 condition.send(binary_operator, child_condition)
504 condition = child_condition
505 end
506 first_condition
507 end
508
509 # Generates a Liquid::Condition object from a Liquid::Parser object based on whether the parsed
510 # expression involves a "comparison" operator (e.g. <, ==, >, !=, etc)
511 #
512 # - parser: an instance of Liquid::Parser
513 #
514 # Returns an instance of Liquid::Condition
515 def parse_comparison(parser)
516 left_operand = Liquid::Expression.parse(parser.expression)
517 operator = parser.consume?(:comparison)
518
519 # No comparison-operator detected. Initialize a Liquid::Condition using only left operand
520 return Liquid::Condition.new(left_operand) unless operator
521
522 # Parse what remained after extracting the left operand and the `:comparison` operator
523 # and initialize a Liquid::Condition object using the operands and the comparison-operator
524 Liquid::Condition.new(left_operand, operator, Liquid::Expression.parse(parser.expression))
525 end
373526 end
374527 end
375528
99 # Initializes a new instance.
1010 def initialize(site)
1111 @site = site
12 end
13
14 def reset
15 @glob_cache = {} if @glob_cache
1216 end
1317
1418 def update_deprecated_types(set)
3539 def ensure_time!(set)
3640 return set unless set.key?("values") && set["values"].key?("date")
3741 return set if set["values"]["date"].is_a?(Time)
42
3843 set["values"]["date"] = Utils.parse_date(
3944 set["values"]["date"],
4045 "An invalid date format was found in a front-matter default set: #{set}"
9196 # path - the path to check for
9297 # type - the type (:post, :page or :draft) to check for
9398 #
94 # Returns true if the scope applies to the given path and type
99 # Returns true if the scope applies to the given type and path
95100 def applies?(scope, path, type)
96 applies_path?(scope, path) && applies_type?(scope, type)
97 end
98
99 # rubocop:disable Metrics/AbcSize
101 applies_type?(scope, type) && applies_path?(scope, path)
102 end
103
100104 def applies_path?(scope, path)
101 return true if !scope.key?("path") || scope["path"].empty?
102
103 sanitized_path = Pathname.new(sanitize_path(path))
104 site_path = Pathname.new(@site.source)
105 rel_scope_path = Pathname.new(scope["path"])
106 abs_scope_path = File.join(@site.source, rel_scope_path)
107
108 if scope["path"].to_s.include?("*")
109 Dir.glob(abs_scope_path).each do |scope_path|
110 scope_path = Pathname.new(scope_path).relative_path_from(site_path)
111 scope_path = strip_collections_dir(scope_path)
112 Jekyll.logger.debug "Globbed Scope Path:", scope_path
113 return true if path_is_subpath?(sanitized_path, scope_path)
114 end
115 false
105 rel_scope_path = scope["path"]
106 return true if !rel_scope_path.is_a?(String) || rel_scope_path.empty?
107
108 sanitized_path = sanitize_path(path)
109
110 if rel_scope_path.include?("*")
111 glob_scope(sanitized_path, rel_scope_path)
116112 else
117113 path_is_subpath?(sanitized_path, strip_collections_dir(rel_scope_path))
118114 end
119115 end
120 # rubocop:enable Metrics/AbcSize
116
117 def glob_scope(sanitized_path, rel_scope_path)
118 site_source = Pathname.new(@site.source)
119 abs_scope_path = site_source.join(rel_scope_path).to_s
120
121 glob_cache(abs_scope_path).each do |scope_path|
122 scope_path = Pathname.new(scope_path).relative_path_from(site_source).to_s
123 scope_path = strip_collections_dir(scope_path)
124 Jekyll.logger.debug "Globbed Scope Path:", scope_path
125 return true if path_is_subpath?(sanitized_path, scope_path)
126 end
127 false
128 end
129
130 def glob_cache(path)
131 @glob_cache ||= {}
132 @glob_cache[path] ||= Dir.glob(path)
133 end
121134
122135 def path_is_subpath?(path, parent_path)
123 path.ascend do |ascended_path|
124 if ascended_path.to_s == parent_path.to_s
125 return true
126 end
127 end
128
129 false
136 path.start_with?(parent_path)
130137 end
131138
132139 def strip_collections_dir(path)
133140 collections_dir = @site.config["collections_dir"]
134 slashed_coll_dir = "#{collections_dir}/"
141 slashed_coll_dir = collections_dir.empty? ? "/" : "#{collections_dir}/"
135142 return path if collections_dir.empty? || !path.to_s.start_with?(slashed_coll_dir)
143
136144 path.sub(slashed_coll_dir, "")
137145 end
138146
148156 # Returns true if either of the above conditions are satisfied,
149157 # otherwise returns false
150158 def applies_type?(scope, type)
151 !scope.key?("type") || scope["type"].eql?(type.to_s)
159 !scope.key?("type") || type&.to_sym.eql?(scope["type"].to_sym)
152160 end
153161
154162 # Checks if a given set of default values is valid
166174 # new_scope - the new scope hash
167175 #
168176 # Returns true if the new scope has precedence over the older
169 # rubocop: disable PredicateName
177 # rubocop: disable Naming/PredicateName
170178 def has_precedence?(old_scope, new_scope)
171179 return true if old_scope.nil?
172180
181189 !old_scope.key? "type"
182190 end
183191 end
184 # rubocop: enable PredicateName
192 # rubocop: enable Naming/PredicateName
185193
186194 # Collects a list of sets that match the given path and type
187195 #
188196 # Returns an array of hashes
189197 def matching_sets(path, type)
190 valid_sets.select do |set|
198 @matched_set_cache ||= {}
199 @matched_set_cache[path] ||= {}
200 @matched_set_cache[path][type] ||= valid_sets.select do |set|
191201 !set.key?("scope") || applies?(set["scope"], path, type)
192202 end
193203 end
210220 Jekyll.logger.warn set.to_s
211221 nil
212222 end
213 end.compact
214 end
215
216 # Sanitizes the given path by removing a leading and adding a trailing slash
217
218 SANITIZATION_REGEX = %r!\A/|(?<=[^/])\z!
219
223 end.tap(&:compact!)
224 end
225
226 # Sanitizes the given path by removing a leading slash
220227 def sanitize_path(path)
221228 if path.nil? || path.empty?
222229 ""
230 elsif path.start_with?("/")
231 path.gsub(%r!\A/|(?<=[^/])\z!, "")
223232 else
224 path.gsub(SANITIZATION_REGEX, "")
233 path
225234 end
226235 end
227236 end
2121 :post_write => [],
2222 },
2323 :pages => {
24 :post_init => [],
25 :pre_render => [],
26 :post_render => [],
27 :post_write => [],
24 :post_init => [],
25 :pre_render => [],
26 :post_convert => [],
27 :post_render => [],
28 :post_write => [],
2829 },
2930 :posts => {
30 :post_init => [],
31 :pre_render => [],
32 :post_render => [],
33 :post_write => [],
31 :post_init => [],
32 :pre_render => [],
33 :post_convert => [],
34 :post_render => [],
35 :post_write => [],
3436 },
3537 :documents => {
36 :post_init => [],
37 :pre_render => [],
38 :post_render => [],
39 :post_write => [],
38 :post_init => [],
39 :pre_render => [],
40 :post_convert => [],
41 :post_render => [],
42 :post_write => [],
4043 },
4144 :clean => {
4245 :on_obsolete => [],
5962 # Ensure the priority is a Fixnum
6063 def self.priority_value(priority)
6164 return priority if priority.is_a?(Integer)
65
6266 PRIORITY_MAP[priority] || DEFAULT_PRIORITY
6367 end
6468
6569 # register a single hook to be called later, internal API
6670 def self.register_one(owner, event, priority, &block)
6771 @registry[owner] ||= {
68 :post_init => [],
69 :pre_render => [],
70 :post_render => [],
71 :post_write => [],
72 :post_init => [],
73 :pre_render => [],
74 :post_convert => [],
75 :post_render => [],
76 :post_write => [],
7277 }
7378
7479 unless @registry[owner][event]
75 raise NotAvailable, "Invalid hook. #{owner} supports only the " \
76 "following hooks #{@registry[owner].keys.inspect}"
80 raise NotAvailable, "Invalid hook. #{owner} supports only the following hooks " \
81 "#{@registry[owner].keys.inspect}"
7782 end
7883
79 unless block.respond_to? :call
80 raise Uncallable, "Hooks must respond to :call"
81 end
84 raise Uncallable, "Hooks must respond to :call" unless block.respond_to? :call
8285
8386 insert_hook owner, event, priority, &block
8487 end
9194 # interface for Jekyll core components to trigger hooks
9295 def self.trigger(owner, event, *args)
9396 # proceed only if there are hooks to call
94 return unless @registry[owner]
95 return unless @registry[owner][event]
96
97 # hooks to call for this owner and event
98 hooks = @registry[owner][event]
97 hooks = @registry.dig(owner, event)
98 return if hooks.nil? || hooks.empty?
9999
100100 # sort and call hooks according to priority and load order
101101 hooks.sort_by { |h| @hook_priority[h] }.each do |hook|
0 # frozen_string_literal: true
1
2 module Jekyll
3 class Inclusion
4 attr_reader :site, :name, :path
5 private :site
6
7 def initialize(site, base, name)
8 @site = site
9 @name = name
10 @path = PathManager.join(base, name)
11 end
12
13 def render(context)
14 @template ||= site.liquid_renderer.file(path).parse(content)
15 @template.render!(context)
16 rescue Liquid::Error => e
17 e.template_name = path
18 e.markup_context = "included " if e.markup_context.nil?
19 raise e
20 end
21
22 def content
23 @content ||= File.read(path, **site.file_read_opts)
24 end
25
26 def inspect
27 "#{self.class} #{path.inspect}"
28 end
29 alias_method :to_s, :inspect
30 end
31 end
33 class Layout
44 include Convertible
55
6 # Gets the Site object.
7 attr_reader :site
6 attr_accessor :content, # content of layout
7 :data, # the Hash that holds the metadata for this layout
8 :ext # extension of layout
89
9 # Gets the name of this layout.
10 attr_reader :name
11
12 # Gets the path to this layout.
13 attr_reader :path
14
15 # Gets the path to this layout relative to its base
16 attr_reader :relative_path
17
18 # Gets/Sets the extension of this layout.
19 attr_accessor :ext
20
21 # Gets/Sets the Hash that holds the metadata for this layout.
22 attr_accessor :data
23
24 # Gets/Sets the content of this layout.
25 attr_accessor :content
10 attr_reader :name, # name of layout
11 :path, # path to layout
12 :site, # the Site object
13 :relative_path # path to layout relative to its base
2614
2715 # Initialize a new Layout.
2816 #
5745 def process(name)
5846 self.ext = File.extname(name)
5947 end
48
49 # Returns the object as a debug String.
50 def inspect
51 "#<#{self.class} @path=#{@path.inspect}>"
52 end
6053 end
6154 end
11
22 module Jekyll
33 module LiquidExtensions
4
54 # Lookup a Liquid variable in the given context.
65 #
76 # context - the Liquid context in question.
1817
1918 lookup || variable
2019 end
21
2220 end
2321 end
99
1010 def parse(content)
1111 measure_time do
12 @template = Liquid::Template.parse(content, :line_numbers => true)
12 @renderer.cache[@filename] ||= Liquid::Template.parse(content, :line_numbers => true)
1313 end
14 @template = @renderer.cache[@filename]
1415
1516 self
1617 end
1718
1819 def render(*args)
20 reset_template_assigns
21
1922 measure_time do
2023 measure_bytes do
21 @template.render(*args)
24 measure_counts do
25 @template.render(*args)
26 end
2227 end
2328 end
2429 end
2530
31 # This method simply 'rethrows any error' before attempting to render the template.
2632 def render!(*args)
33 reset_template_assigns
34
2735 measure_time do
2836 measure_bytes do
29 @template.render!(*args)
37 measure_counts do
38 @template.render!(*args)
39 end
3040 end
3141 end
3242 end
3646 end
3747
3848 private
49
50 # clear assigns to `Liquid::Template` instance prior to rendering since
51 # `Liquid::Template` instances are cached in Jekyll 4.
52 def reset_template_assigns
53 @template.instance_assigns.clear
54 end
55
56 def measure_counts
57 @renderer.increment_count(@filename)
58 yield
59 end
3960
4061 def measure_bytes
4162 yield.tap do |str|
00 # frozen_string_literal: true
11
22 module Jekyll
3 class LiquidRenderer::Table
4 def initialize(stats)
5 @stats = stats
6 end
3 class LiquidRenderer
4 class Table
5 GAUGES = [:count, :bytes, :time].freeze
76
8 def to_s(num_of_rows = 50)
9 data = data_for_table(num_of_rows)
10 widths = table_widths(data)
11 generate_table(data, widths)
12 end
13
14 private
15
16 def generate_table(data, widths)
17 str = String.new("\n")
18
19 table_head = data.shift
20 str << generate_row(table_head, widths)
21 str << generate_table_head_border(table_head, widths)
22
23 data.each do |row_data|
24 str << generate_row(row_data, widths)
7 def initialize(stats)
8 @stats = stats
259 end
2610
27 str << "\n"
28 str
29 end
30
31 def generate_table_head_border(row_data, widths)
32 str = String.new("")
33
34 row_data.each_index do |cell_index|
35 str << "-" * widths[cell_index]
36 str << "-+-" unless cell_index == row_data.length - 1
11 def to_s(num_of_rows = 50)
12 Jekyll::Profiler.tabulate(data_for_table(num_of_rows))
3713 end
3814
39 str << "\n"
40 str
41 end
15 private
4216
43 def generate_row(row_data, widths)
44 str = String.new("")
17 # rubocop:disable Metrics/AbcSize
18 def data_for_table(num_of_rows)
19 sorted = @stats.sort_by { |_, file_stats| -file_stats[:time] }
20 sorted = sorted.slice(0, num_of_rows)
4521
46 row_data.each_with_index do |cell_data, cell_index|
47 str << if cell_index.zero?
48 cell_data.ljust(widths[cell_index], " ")
49 else
50 cell_data.rjust(widths[cell_index], " ")
51 end
22 table = [header_labels]
23 totals = Hash.new { |hash, key| hash[key] = 0 }
5224
53 str << " | " unless cell_index == row_data.length - 1
25 sorted.each do |filename, file_stats|
26 GAUGES.each { |gauge| totals[gauge] += file_stats[gauge] }
27 row = []
28 row << filename
29 row << file_stats[:count].to_s
30 row << format_bytes(file_stats[:bytes])
31 row << format("%.3f", file_stats[:time])
32 table << row
33 end
34
35 footer = []
36 footer << "TOTAL (for #{sorted.size} files)"
37 footer << totals[:count].to_s
38 footer << format_bytes(totals[:bytes])
39 footer << format("%.3f", totals[:time])
40 table << footer
41 end
42 # rubocop:enable Metrics/AbcSize
43
44 def header_labels
45 GAUGES.map { |gauge| gauge.to_s.capitalize }.unshift("Filename")
5446 end
5547
56 str << "\n"
57 str
58 end
59
60 def table_widths(data)
61 widths = []
62
63 data.each do |row|
64 row.each_with_index do |cell, index|
65 widths[index] = [cell.length, widths[index]].compact.max
66 end
48 def format_bytes(bytes)
49 bytes /= 1024.0
50 format("%.2fK", bytes)
6751 end
68
69 widths
70 end
71
72 def data_for_table(num_of_rows)
73 sorted = @stats.sort_by { |_, file_stats| -file_stats[:time] }
74 sorted = sorted.slice(0, num_of_rows)
75
76 table = [%w(Filename Count Bytes Time)]
77
78 sorted.each do |filename, file_stats|
79 row = []
80 row << filename
81 row << file_stats[:count].to_s
82 row << format_bytes(file_stats[:bytes])
83 row << format("%.3f", file_stats[:time])
84 table << row
85 end
86
87 table
88 end
89
90 def format_bytes(bytes)
91 bytes /= 1024.0
92 format("%.2fK", bytes)
9352 end
9453 end
9554 end
44
55 module Jekyll
66 class LiquidRenderer
7 extend Forwardable
8
9 private def_delegator :@site, :in_source_dir, :source_dir
10 private def_delegator :@site, :in_theme_dir, :theme_dir
11
127 def initialize(site)
138 @site = site
149 Liquid::Template.error_mode = @site.config["liquid"]["error_mode"].to_sym
1712
1813 def reset
1914 @stats = {}
15 @cache = {}
2016 end
2117
2218 def file(filename)
23 filename.match(filename_regex)
24 filename =
25 if Regexp.last_match(1) == theme_dir("")
26 ::File.join(::File.basename(Regexp.last_match(1)), Regexp.last_match(2))
27 else
28 Regexp.last_match(2)
29 end
19 filename = normalize_path(filename)
3020 LiquidRenderer::File.new(self, filename).tap do
3121 @stats[filename] ||= new_profile_hash
32 @stats[filename][:count] += 1
3322 end
3423 end
3524
4130 @stats[filename][:time] += time
4231 end
4332
33 def increment_count(filename)
34 @stats[filename][:count] += 1
35 end
36
4437 def stats_table(num_of_rows = 50)
4538 LiquidRenderer::Table.new(@stats).to_s(num_of_rows)
4639 end
4942 "#{error.message} in #{path}"
5043 end
5144
45 # A persistent cache to store and retrieve parsed templates based on the filename
46 # via `LiquidRenderer::File#parse`
47 #
48 # It is emptied when `self.reset` is called.
49 def cache
50 @cache ||= {}
51 end
52
5253 private
5354
54 def filename_regex
55 @filename_regex ||= begin
56 %r!\A(#{Regexp.escape(source_dir)}/|#{Regexp.escape(theme_dir.to_s)}/|/*)(.*)!i
55 def normalize_path(filename)
56 @normalize_path ||= {}
57 @normalize_path[filename] ||= begin
58 theme_dir = @site.theme&.root
59 case filename
60 when %r!\A(#{Regexp.escape(@site.source)}/)(?<rest>.*)!io
61 Regexp.last_match(:rest)
62 when %r!(/gems/.*)*/gems/(?<dirname>[^/]+)(?<rest>.*)!,
63 %r!(?<dirname>[^/]+/lib)(?<rest>.*)!
64 "#{Regexp.last_match(:dirname)}#{Regexp.last_match(:rest)}"
65 when theme_dir && %r!\A#{Regexp.escape(theme_dir)}/(?<rest>.*)!io
66 PathManager.join(@site.theme.basename, Regexp.last_match(:rest))
67 when %r!\A/(.*)!
68 Regexp.last_match(1)
69 else
70 filename
71 end
5772 end
5873 end
5974
2828 #
2929 # Returns nothing
3030 def log_level=(level)
31 writer.level = LOG_LEVELS.fetch(level)
31 writer.level = level if level.is_a?(Integer) && level.between?(0, 3)
32 writer.level = LOG_LEVELS[level] ||
33 raise(ArgumentError, "unknown log level")
3234 @level = level
3335 end
3436
4042 self.log_level = :debug
4143 end
4244 debug "Logging at level:", LOG_LEVELS.key(writer.level).to_s
45 debug "Jekyll Version:", Jekyll::VERSION
4346 end
4447
4548 # Public: Print a debug message
140143 # the appropriate writer method, e.g. writer.info.
141144 def write(level_of_message, topic, message = nil, &block)
142145 return false unless write_message?(level_of_message)
146
143147 writer.public_send(level_of_message, message(topic, message, &block))
144148 end
145149 end
44 application/applixware aw
55 application/atom+xml atom
66 application/atomcat+xml atomcat
7 application/atomdeleted+xml atomdeleted
78 application/atomsvc+xml atomsvc
9 application/atsc-dwd+xml dwd
10 application/atsc-held+xml held
11 application/atsc-rsat+xml rsat
812 application/bdoc bdoc
13 application/calendar+xml xcs
914 application/ccxml+xml ccxml
15 application/cdfx+xml cdfx
1016 application/cdmi-capability cdmia
1117 application/cdmi-container cdmic
1218 application/cdmi-domain cdmid
1319 application/cdmi-object cdmio
1420 application/cdmi-queue cdmiq
21 application/cpl+xml cpl
1522 application/cu-seeme cu
1623 application/dash+xml mpd
24 application/dash-patch+xml mpp
1725 application/davmount+xml davmount
1826 application/docbook+xml dbk
1927 application/dssc+der dssc
2028 application/dssc+xml xdssc
21 application/ecmascript ecma
29 application/ecmascript ecma es
2230 application/emma+xml emma
31 application/emotionml+xml emotionml
2332 application/epub+zip epub
2433 application/exi exi
34 application/express exp
35 application/fdt+xml fdt
2536 application/font-tdpfr pfr
26 application/font-woff woff
27 application/font-woff2 woff2
2837 application/geo+json geojson
2938 application/gml+xml gml
3039 application/gpx+xml gpx
3140 application/gxf gxf
3241 application/gzip gz
42 application/hjson hjson
3343 application/hyperstudio stk
3444 application/inkml+xml ink inkml
3545 application/ipfix ipfix
36 application/java-archive jar war ear
46 application/its+xml its
47 application/java-archive ear jar war
3748 application/java-serialized-object ser
3849 application/java-vm class
3950 application/javascript js mjs
4152 application/json5 json5
4253 application/jsonml+json jsonml
4354 application/ld+json jsonld
55 application/lgr+xml lgr
4456 application/lost+xml lostxml
4557 application/mac-binhex40 hqx
4658 application/mac-compactpro cpt
4860 application/manifest+json webmanifest
4961 application/marc mrc
5062 application/marcxml+xml mrcx
51 application/mathematica ma nb mb
63 application/mathematica ma mb nb
5264 application/mathml+xml mathml
5365 application/mbox mbox
66 application/media-policy-dataset+xml mpf
5467 application/mediaservercontrol+xml mscml
5568 application/metalink+xml metalink
5669 application/metalink4+xml meta4
5770 application/mets+xml mets
71 application/mmt-aei+xml maei
72 application/mmt-usd+xml musd
5873 application/mods+xml mods
5974 application/mp21 m21 mp21
60 application/mp4 mp4s m4p
75 application/mp4 m4p mp4s
6176 application/msword doc dot
6277 application/mxf mxf
63 application/octet-stream bin dms lrf mar so dist distz pkg bpk dump elc deploy exe dll deb dmg iso img msi msp msm buffer
78 application/n-quads nq
79 application/n-triples nt
80 application/node cjs
81 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
6482 application/oda oda
6583 application/oebps-package+xml opf
6684 application/ogg ogx
6785 application/omdoc+xml omdoc
68 application/onenote onetoc onetoc2 onetmp onepkg
86 application/onenote onepkg onetmp onetoc onetoc2
6987 application/oxps oxps
88 application/p2p-overlay+xml relo
7089 application/patch-ops-error+xml xer
7190 application/pdf pdf
7291 application/pgp-encrypted pgp
73 application/pgp-signature asc sig
92 application/pgp-keys asc
93 application/pgp-signature sig
7494 application/pics-rules prf
7595 application/pkcs10 p10
76 application/pkcs7-mime p7m p7c
96 application/pkcs7-mime p7c p7m
7797 application/pkcs7-signature p7s
7898 application/pkcs8 p8
7999 application/pkix-attr-cert ac
83103 application/pkixcmp pki
84104 application/pls+xml pls
85105 application/postscript ai eps ps
106 application/provenance+xml provx
86107 application/prs.cww cww
87108 application/pskc+xml pskcxml
88 application/rdf+xml rdf
109 application/raml+yaml raml
110 application/rdf+xml owl rdf
89111 application/reginfo+xml rif
90112 application/relax-ng-compact-syntax rnc
91113 application/resource-lists+xml rl
92114 application/resource-lists-diff+xml rld
93115 application/rls-services+xml rs
116 application/route-apd+xml rapd
117 application/route-s-tsid+xml sls
118 application/route-usd+xml rusd
94119 application/rpki-ghostbusters gbr
95120 application/rpki-manifest mft
96121 application/rpki-roa roa
103128 application/scvp-vp-request spq
104129 application/scvp-vp-response spp
105130 application/sdp sdp
131 application/senml+xml senmlx
132 application/sensml+xml sensmlx
106133 application/set-payment-initiation setpay
107134 application/set-registration-initiation setreg
108135 application/shf+xml shf
136 application/sieve sieve siv
109137 application/smil+xml smi smil
110138 application/sparql-query rq
111139 application/sparql-results+xml srx
114142 application/sru+xml sru
115143 application/ssdl+xml ssdl
116144 application/ssml+xml ssml
145 application/swid+xml swidtag
117146 application/tei+xml tei teicorpus
118147 application/thraud+xml tfi
119148 application/timestamped-data tsd
149 application/toml toml
150 application/trig trig
151 application/ttml+xml ttml
152 application/ubjson ubj
153 application/urc-ressheet+xml rsheet
154 application/urc-targetdesc+xml td
155 application/vnd.1000minds.decision-model+xml 1km
120156 application/vnd.3gpp.pic-bw-large plb
121157 application/vnd.3gpp.pic-bw-small psb
122158 application/vnd.3gpp.pic-bw-var pvb
125161 application/vnd.accpac.simply.aso aso
126162 application/vnd.accpac.simply.imp imp
127163 application/vnd.acucobol acu
128 application/vnd.acucorp atc acutc
164 application/vnd.acucorp acutc atc
129165 application/vnd.adobe.air-application-installer-package+zip air
130166 application/vnd.adobe.formscentral.fcdt fcdt
131167 application/vnd.adobe.fxp fxp fxpl
132168 application/vnd.adobe.xdp+xml xdp
133169 application/vnd.adobe.xfdf xfdf
170 application/vnd.age age
134171 application/vnd.ahead.space ahead
135172 application/vnd.airzip.filesecure.azf azf
136173 application/vnd.airzip.filesecure.azs azs
142179 application/vnd.anser-web-funds-transfer-initiation fti
143180 application/vnd.antix.game-component atx
144181 application/vnd.apple.installer+xml mpkg
182 application/vnd.apple.keynote key
145183 application/vnd.apple.mpegurl m3u8
184 application/vnd.apple.numbers numbers
185 application/vnd.apple.pages pages
146186 application/vnd.apple.pkpass pkpass
147187 application/vnd.aristanetworks.swi swi
148188 application/vnd.astraea-software.iota iota
149189 application/vnd.audiograph aep
190 application/vnd.balsamiq.bmml+xml bmml
150191 application/vnd.blueice.multipass mpm
151192 application/vnd.bmi bmi
152193 application/vnd.businessobjects rep
153194 application/vnd.chemdraw+xml cdxml
154195 application/vnd.chipnuts.karaoke-mmd mmd
155196 application/vnd.cinderella cdy
197 application/vnd.citationstyles.style+xml csl
156198 application/vnd.claymore cla
157199 application/vnd.cloanto.rp9 rp9
158 application/vnd.clonk.c4group c4g c4d c4f c4p c4u
200 application/vnd.clonk.c4group c4d c4f c4g c4p c4u
159201 application/vnd.cluetrust.cartomobile-config c11amc
160202 application/vnd.cluetrust.cartomobile-config-pkg c11amz
161203 application/vnd.commonspace csp
173215 application/vnd.curl.pcurl pcurl
174216 application/vnd.dart dart
175217 application/vnd.data-vision.rdz rdz
176 application/vnd.dece.data uvf uvvf uvd uvvd
218 application/vnd.dbf dbf
219 application/vnd.dece.data uvd uvf uvvd uvvf
177220 application/vnd.dece.ttml+xml uvt uvvt
178 application/vnd.dece.unspecified uvx uvvx
179 application/vnd.dece.zip uvz uvvz
221 application/vnd.dece.unspecified uvvx uvx
222 application/vnd.dece.zip uvvz uvz
180223 application/vnd.denovo.fcselayout-link fe_launch
181224 application/vnd.dna dna
182225 application/vnd.dolby.mlp mlp
198241 application/vnd.ezpix-package ez3
199242 application/vnd.fdf fdf
200243 application/vnd.fdsn.mseed mseed
201 application/vnd.fdsn.seed seed dataless
244 application/vnd.fdsn.seed dataless seed
202245 application/vnd.flographit gph
203246 application/vnd.fluxtime.clip ftc
204 application/vnd.framemaker fm frame maker book
247 application/vnd.framemaker book fm frame maker
205248 application/vnd.frogans.fnc fnc
206249 application/vnd.frogans.ltf ltf
207250 application/vnd.fsc.weblaunch fsc
247290 application/vnd.hp-pclxl pclxl
248291 application/vnd.hydrostatix.sof-data sfd-hdstx
249292 application/vnd.ibm.minipay mpy
250 application/vnd.ibm.modcap afp listafp list3820
293 application/vnd.ibm.modcap afp list3820 listafp
251294 application/vnd.ibm.rights-management irm
252295 application/vnd.ibm.secure-container sc
253296 application/vnd.iccprofile icc icm
267310 application/vnd.jcp.javame.midlet-rms rms
268311 application/vnd.jisp jisp
269312 application/vnd.joost.joda-archive joda
270 application/vnd.kahootz ktz ktr
313 application/vnd.kahootz ktr ktz
271314 application/vnd.kde.karbon karbon
272315 application/vnd.kde.kchart chrt
273316 application/vnd.kde.kformula kfo
279322 application/vnd.kenameaapp htke
280323 application/vnd.kidspiration kia
281324 application/vnd.kinar kne knp
282 application/vnd.koan skp skd skt skm
325 application/vnd.koan skd skm skp skt
283326 application/vnd.kodak-descriptor sse
284327 application/vnd.las.las+xml lasxml
285328 application/vnd.llamagraphics.life-balance.desktop lbd
292335 application/vnd.lotus-screencam scm
293336 application/vnd.lotus-wordpro lwp
294337 application/vnd.macports.portpkg portpkg
338 application/vnd.mapbox-vector-tile mvt
295339 application/vnd.mcd mcd
296340 application/vnd.medcalcdata mc1
297341 application/vnd.mediastation.cdkey cdkey
312356 application/vnd.mozilla.xul+xml xul
313357 application/vnd.ms-artgalry cil
314358 application/vnd.ms-cab-compressed cab
315 application/vnd.ms-excel xls xlm xla xlc xlt xlw
359 application/vnd.ms-excel xla xlc xlm xls xlt xlw
316360 application/vnd.ms-excel.addin.macroenabled.12 xlam
317361 application/vnd.ms-excel.sheet.binary.macroenabled.12 xlsb
318362 application/vnd.ms-excel.sheet.macroenabled.12 xlsm
325369 application/vnd.ms-outlook msg
326370 application/vnd.ms-pki.seccat cat
327371 application/vnd.ms-pki.stl stl
328 application/vnd.ms-powerpoint ppt pps pot
372 application/vnd.ms-powerpoint pot pps ppt
329373 application/vnd.ms-powerpoint.addin.macroenabled.12 ppam
330374 application/vnd.ms-powerpoint.presentation.macroenabled.12 pptm
331375 application/vnd.ms-powerpoint.slide.macroenabled.12 sldm
332376 application/vnd.ms-powerpoint.slideshow.macroenabled.12 ppsm
333377 application/vnd.ms-powerpoint.template.macroenabled.12 potm
334 application/vnd.ms-project mpp mpt
378 application/vnd.ms-project mpt
335379 application/vnd.ms-word.document.macroenabled.12 docm
336380 application/vnd.ms-word.template.macroenabled.12 dotm
337 application/vnd.ms-works wps wks wcm wdb
381 application/vnd.ms-works wcm wdb wks wps
338382 application/vnd.ms-wpl wpl
339383 application/vnd.ms-xpsdocument xps
340384 application/vnd.mseq mseq
342386 application/vnd.muvee.style msty
343387 application/vnd.mynfc taglet
344388 application/vnd.neurolanguage.nlu nlu
345 application/vnd.nitf ntf nitf
389 application/vnd.nitf nitf ntf
346390 application/vnd.noblenet-directory nnd
347391 application/vnd.noblenet-sealer nns
348392 application/vnd.noblenet-web nnw
372416 application/vnd.oasis.opendocument.text-web oth
373417 application/vnd.olpc-sugar xo
374418 application/vnd.oma.dd2+xml dd2
419 application/vnd.openblox.game+xml obgx
375420 application/vnd.openofficeorg.extension oxt
421 application/vnd.openstreetmap.data+xml osm
376422 application/vnd.openxmlformats-officedocument.presentationml.presentation pptx
377423 application/vnd.openxmlformats-officedocument.presentationml.slide sldx
378424 application/vnd.openxmlformats-officedocument.presentationml.slideshow ppsx
384430 application/vnd.osgeo.mapguide.package mgp
385431 application/vnd.osgi.dp dp
386432 application/vnd.osgi.subsystem esa
387 application/vnd.palm pdb pqa oprc
433 application/vnd.palm oprc pdb pqa
388434 application/vnd.pawaafile paw
389435 application/vnd.pg.format str
390436 application/vnd.pg.osasli ei6
396442 application/vnd.proteus.magazine mgz
397443 application/vnd.publishare-delta-tree qps
398444 application/vnd.pvi.ptid1 ptid
399 application/vnd.quark.quarkxpress qxd qxt qwd qwt qxl qxb
445 application/vnd.quark.quarkxpress qwd qwt qxb qxd qxl qxt
446 application/vnd.rar rar
400447 application/vnd.realvnc.bed bed
401448 application/vnd.recordare.musicxml mxl
402449 application/vnd.recordare.musicxml+xml musicxml
417464 application/vnd.simtech-mindmapper twd twds
418465 application/vnd.smaf mmf
419466 application/vnd.smart.teacher teacher
420 application/vnd.solent.sdkm+xml sdkm sdkd
467 application/vnd.software602.filler.form+xml fo
468 application/vnd.solent.sdkm+xml sdkd sdkm
421469 application/vnd.spotfire.dxp dxp
422470 application/vnd.spotfire.sfs sfs
423471 application/vnd.stardivision.calc sdc
445493 application/vnd.syncml+xml xsm
446494 application/vnd.syncml.dm+wbxml bdm
447495 application/vnd.syncml.dm+xml xdm
496 application/vnd.syncml.dmddf+xml ddf
448497 application/vnd.tao.intent-module-archive tao
449 application/vnd.tcpdump.pcap pcap cap dmp
498 application/vnd.tcpdump.pcap cap dmp pcap
450499 application/vnd.tmobile-livetv tmo
451500 application/vnd.trid.tpt tpt
452501 application/vnd.triscape.mxs mxs
457506 application/vnd.unity unityweb
458507 application/vnd.uoml+xml uoml
459508 application/vnd.vcx vcx
460 application/vnd.visio vsd vst vss vsw
509 application/vnd.visio vsd vss vst vsw
461510 application/vnd.visionary vis
462511 application/vnd.vsf vsf
463512 application/vnd.wap.wbxml wbxml
481530 application/vnd.zul zir zirz
482531 application/vnd.zzazz.deck+xml zaz
483532 application/voicexml+xml vxml
533 application/wasm wasm
534 application/watcherinfo+xml wif
484535 application/widget wgt
485536 application/winhlp hlp
486537 application/wsdl+xml wsdl
489540 application/x-abiword abw
490541 application/x-ace-compressed ace
491542 application/x-arj arj
492 application/x-authorware-bin aab x32 u32 vox
543 application/x-authorware-bin aab u32 vox x32
493544 application/x-authorware-map aam
494545 application/x-authorware-seg aas
495546 application/x-bcpio bcpio
496547 application/x-bittorrent torrent
497548 application/x-blorb blb blorb
498549 application/x-bzip bz
499 application/x-bzip2 bz2 boz
500 application/x-cbr cbr cba cbt cbz cb7
550 application/x-bzip2 boz bz2
551 application/x-cbr cb7 cba cbr cbt cbz
501552 application/x-cdlink vcd
502553 application/x-cfs-compressed cfs
503554 application/x-chat chat
509560 application/x-csh csh
510561 application/x-debian-package udeb
511562 application/x-dgc-compressed dgc
512 application/x-director dir dcr dxr cst cct cxt w3d fgd swa
563 application/x-director cct cst cxt dcr dir dxr fgd swa w3d
513564 application/x-doom wad
514565 application/x-dtbncx+xml ncx
515566 application/x-dtbook+xml dtb
520571 application/x-font-bdf bdf
521572 application/x-font-ghostscript gsf
522573 application/x-font-linux-psf psf
523 application/x-font-otf otf
524574 application/x-font-pcf pcf
525575 application/x-font-snf snf
526 application/x-font-ttf ttf ttc
527 application/x-font-type1 pfa pfb pfm afm
576 application/x-font-type1 afm pfa pfb pfm
528577 application/x-freearc arc
529578 application/x-futuresplash spl
530579 application/x-gca-compressed gca
537586 application/x-install-instructions install
538587 application/x-java-archive-diff jardiff
539588 application/x-java-jnlp-file jnlp
589 application/x-keepass2 kdbx
540590 application/x-latex latex
541591 application/x-lua-bytecode luac
542 application/x-lzh-compressed lzh lha
592 application/x-lzh-compressed lha lzh
543593 application/x-makeself run
544594 application/x-mie mie
545 application/x-mobipocket-ebook prc mobi
595 application/x-mobipocket-ebook mobi prc
546596 application/x-ms-application application
547597 application/x-ms-shortcut lnk
548598 application/x-ms-wmd wmd
552602 application/x-msbinder obd
553603 application/x-mscardfile crd
554604 application/x-msclip clp
555 application/x-msdownload com bat
556 application/x-msmediaview mvb m13 m14
557 application/x-msmetafile wmf emf emz
605 application/x-msdownload bat com
606 application/x-msmediaview m13 m14 mvb
607 application/x-msmetafile emf emz wmf
558608 application/x-msmoney mny
559609 application/x-mspublisher pub
560610 application/x-msschedule scd
561611 application/x-msterminal trm
562612 application/x-mswrite wri
563 application/x-netcdf nc cdf
613 application/x-netcdf cdf nc
564614 application/x-ns-proxy-autoconfig pac
565615 application/x-nzb nzb
566616 application/x-perl pl pm
567617 application/x-pkcs12 p12 pfx
568618 application/x-pkcs7-certificates p7b spc
569619 application/x-pkcs7-certreqresp p7r
570 application/x-rar-compressed rar
571620 application/x-redhat-package-manager rpm
572621 application/x-research-info-systems ris
573622 application/x-sea sea
587636 application/x-tcl tcl tk
588637 application/x-tex tex
589638 application/x-tex-tfm tfm
590 application/x-texinfo texinfo texi
639 application/x-texinfo texi texinfo
591640 application/x-tgif obj
592641 application/x-ustar ustar
593642 application/x-virtualbox-hdd hdd
600649 application/x-virtualbox-vmdk vmdk
601650 application/x-wais-source src
602651 application/x-web-app-manifest+json webapp
603 application/x-x509-ca-cert der crt pem
652 application/x-x509-ca-cert crt der pem
604653 application/x-xfig fig
605654 application/x-xliff+xml xlf
606655 application/x-xpinstall xpi
607656 application/x-xz xz
608657 application/x-zmachine z1 z2 z3 z4 z5 z6 z7 z8
609658 application/xaml+xml xaml
659 application/xcap-att+xml xav
660 application/xcap-caps+xml xca
610661 application/xcap-diff+xml xdf
662 application/xcap-el+xml xel
663 application/xcap-ns+xml xns
611664 application/xenc+xml xenc
612 application/xhtml+xml xhtml xht
613 application/xml xml xsl xsd rng
665 application/xhtml+xml xht xhtml
666 application/xml rng xml xsd xsl
614667 application/xml-dtd dtd
615668 application/xop+xml xop
616669 application/xproc+xml xpl
617670 application/xslt+xml xslt
618671 application/xspf+xml xspf
619 application/xv+xml mxml xhvml xvml xvm
672 application/xv+xml mxml xhvml xvm xvml
620673 application/yang yang
621674 application/yin+xml yin
622675 application/zip zip
623676 audio/3gpp 3gpp
624677 audio/adpcm adp
678 audio/amr amr
625679 audio/basic au snd
626 audio/midi mid midi kar rmi
680 audio/midi kar mid midi rmi
681 audio/mobile-xmf mxmf
627682 audio/mp3 mp3
628683 audio/mp4 m4a mp4a
629 audio/mpeg mpga mp2 mp2a m2a m3a
630 audio/ogg oga ogg spx
684 audio/mpeg m2a m3a mp2 mp2a mpga
685 audio/ogg oga ogg opus spx
631686 audio/s3m s3m
632687 audio/silk sil
633688 audio/vnd.dece.audio uva uvva
644699 audio/wav wav
645700 audio/webm weba
646701 audio/x-aac aac
647 audio/x-aiff aif aiff aifc
702 audio/x-aiff aif aifc aiff
648703 audio/x-caf caf
649704 audio/x-flac flac
650705 audio/x-matroska mka
651706 audio/x-mpegurl m3u
652707 audio/x-ms-wax wax
653708 audio/x-ms-wma wma
654 audio/x-pn-realaudio ram ra
709 audio/x-pn-realaudio ra ram
655710 audio/x-pn-realaudio-plugin rmp
656711 audio/xm xm
657712 chemical/x-cdx cdx
660715 chemical/x-cml cml
661716 chemical/x-csml csml
662717 chemical/x-xyz xyz
718 font/collection ttc
719 font/otf otf
720 font/ttf ttf
721 font/woff woff
722 font/woff2 woff2
723 image/aces exr
663724 image/apng apng
725 image/avci avci
726 image/avcs avcs
727 image/avif avif
664728 image/bmp bmp
665729 image/cgm cgm
730 image/dicom-rle drle
731 image/fits fits
666732 image/g3fax g3
667733 image/gif gif
734 image/heic heic
735 image/heic-sequence heics
736 image/heif heif
737 image/heif-sequence heifs
738 image/hej2k hej2
739 image/hsj2 hsj2
668740 image/ief ief
669 image/jpeg jpeg jpg jpe
741 image/jls jls
742 image/jp2 jp2 jpg2
743 image/jpeg jpe jpeg jpg
744 image/jph jph
745 image/jphc jhc
746 image/jpm jpm
747 image/jpx jpf jpx
748 image/jxr jxr
749 image/jxra jxra
750 image/jxrs jxrs
751 image/jxs jxs
752 image/jxsc jxsc
753 image/jxsi jxsi
754 image/jxss jxss
670755 image/ktx ktx
756 image/ktx2 ktx2
671757 image/png png
672758 image/prs.btif btif
759 image/prs.pti pti
673760 image/sgi sgi
674761 image/svg+xml svg svgz
675 image/tiff tiff tif
762 image/t38 t38
763 image/tiff tif tiff
764 image/tiff-fx tfx
676765 image/vnd.adobe.photoshop psd
677 image/vnd.dece.graphic uvi uvvi uvg uvvg
678 image/vnd.djvu djvu djv
766 image/vnd.airzip.accelerator.azv azv
767 image/vnd.dece.graphic uvg uvi uvvg uvvi
768 image/vnd.djvu djv djvu
679769 image/vnd.dvb.subtitle sub
680770 image/vnd.dwg dwg
681771 image/vnd.dxf dxf
684774 image/vnd.fst fst
685775 image/vnd.fujixerox.edmics-mmr mmr
686776 image/vnd.fujixerox.edmics-rlc rlc
777 image/vnd.microsoft.icon ico
778 image/vnd.ms-dds dds
687779 image/vnd.ms-modi mdi
688780 image/vnd.ms-photo wdp
689781 image/vnd.net-fpx npx
782 image/vnd.pco.b16 b16
783 image/vnd.tencent.tap tap
784 image/vnd.valve.source.texture vtf
690785 image/vnd.wap.wbmp wbmp
691786 image/vnd.xiff xif
787 image/vnd.zbrush.pcx pcx
692788 image/webp webp
693789 image/x-3ds 3ds
694790 image/x-cmu-raster ras
695791 image/x-cmx cmx
696 image/x-freehand fh fhc fh4 fh5 fh7
697 image/x-icon ico
792 image/x-freehand fh fh4 fh5 fh7 fhc
698793 image/x-jng jng
699794 image/x-mrsid-image sid
700 image/x-pcx pcx
701 image/x-pict pic pct
795 image/x-pict pct pic
702796 image/x-portable-anymap pnm
703797 image/x-portable-bitmap pbm
704798 image/x-portable-graymap pgm
708802 image/x-xbitmap xbm
709803 image/x-xpixmap xpm
710804 image/x-xwindowdump xwd
805 message/disposition-notification disposition-notification
806 message/global u8msg
807 message/global-delivery-status u8dsn
808 message/global-disposition-notification u8mdn
809 message/global-headers u8hdr
711810 message/rfc822 eml mime
811 message/vnd.wfa.wsc wsc
812 model/3mf 3mf
712813 model/gltf+json gltf
713814 model/gltf-binary glb
714 model/iges igs iges
715 model/mesh msh mesh silo
815 model/iges iges igs
816 model/mesh mesh msh silo
817 model/mtl mtl
818 model/step+xml stpx
819 model/step+zip stpz
820 model/step-xml+zip stpxz
716821 model/vnd.collada+xml dae
717822 model/vnd.dwf dwf
718823 model/vnd.gdl gdl
719824 model/vnd.gtw gtw
720825 model/vnd.mts mts
826 model/vnd.opengex ogex
827 model/vnd.parasolid.transmit.binary x_b
828 model/vnd.parasolid.transmit.text x_t
829 model/vnd.sap.vds vds
830 model/vnd.usdz+zip usdz
831 model/vnd.valve.source.compiled-map bsp
721832 model/vnd.vtu vtu
722 model/vrml wrl vrml
833 model/vrml vrml wrl
723834 model/x3d+binary x3db x3dbz
724835 model/x3d+vrml x3dv x3dvz
725836 model/x3d+xml x3d x3dz
728839 text/coffeescript coffee litcoffee
729840 text/css css
730841 text/csv csv
731 text/hjson hjson
732 text/html html htm shtml
842 text/html htm html shtml
733843 text/jade jade
734844 text/jsx jsx
735845 text/less less
736846 text/markdown markdown md
737847 text/mathml mml
848 text/mdx mdx
738849 text/n3 n3
739 text/plain txt text conf def list log in ini
850 text/plain conf def in ini list log text txt
740851 text/prs.lines.tag dsc
741852 text/richtext rtx
742 text/sgml sgml sgm
853 text/sgml sgm sgml
854 text/shex shex
743855 text/slim slim slm
744 text/stylus stylus styl
856 text/spdx spdx
857 text/stylus styl stylus
745858 text/tab-separated-values tsv
746 text/troff t tr roff man me ms
859 text/troff man me ms roff t tr
747860 text/turtle ttl
748861 text/uri-list uri uris urls
749862 text/vcard vcard
751864 text/vnd.curl.dcurl dcurl
752865 text/vnd.curl.mcurl mcurl
753866 text/vnd.curl.scurl scurl
867 text/vnd.familysearch.gedcom ged
754868 text/vnd.fly fly
755869 text/vnd.fmi.flexstor flx
756870 text/vnd.graphviz gv
760874 text/vnd.wap.wml wml
761875 text/vnd.wap.wmlscript wmls
762876 text/vtt vtt
763 text/x-asm s asm
764 text/x-c c cc cxx cpp h hh dic
877 text/x-asm asm s
878 text/x-c c cc cpp cxx dic h hh
765879 text/x-component htc
766 text/x-fortran f for f77 f90
880 text/x-fortran f f77 f90 for
767881 text/x-handlebars-template hbs
768882 text/x-java-source java
769883 text/x-lua lua
786900 video/h261 h261
787901 video/h263 h263
788902 video/h264 h264
903 video/iso.segment m4s
789904 video/jpeg jpgv
790 video/jpm jpm jpgm
905 video/jpm jpgm
791906 video/mj2 mj2 mjp2
792907 video/mp2t ts
793908 video/mp4 mp4 mp4v mpg4
794 video/mpeg mpeg mpg mpe m1v m2v
909 video/mpeg m1v m2v mpe mpeg mpg
795910 video/ogg ogv
796 video/quicktime qt mov
911 video/quicktime mov qt
797912 video/vnd.dece.hd uvh uvvh
798913 video/vnd.dece.mobile uvm uvvm
799914 video/vnd.dece.pd uvp uvvp
801916 video/vnd.dece.video uvv uvvv
802917 video/vnd.dvb.file dvb
803918 video/vnd.fvt fvt
804 video/vnd.mpegurl mxu m4u
919 video/vnd.mpegurl m4u mxu
805920 video/vnd.ms-playready.media.pyv pyv
806921 video/vnd.uvvu.mp4 uvu uvvu
807922 video/vnd.vivo viv
810925 video/x-fli fli
811926 video/x-flv flv
812927 video/x-m4v m4v
813 video/x-matroska mkv mk3d mks
928 video/x-matroska mk3d mks mkv
814929 video/x-mng mng
815930 video/x-ms-asf asf asx
816931 video/x-ms-vob vob
44 include Convertible
55
66 attr_writer :dir
7 attr_accessor :site, :pager
8 attr_accessor :name, :ext, :basename
9 attr_accessor :data, :content, :output
7 attr_accessor :basename, :content, :data, :ext, :name, :output, :pager, :site
108
119 alias_method :extname, :ext
12
13 FORWARD_SLASH = "/".freeze
1410
1511 # Attributes for Liquid templates
1612 ATTRIBUTES_FOR_LIQUID = %w(
1713 content
1814 dir
15 excerpt
1916 name
2017 path
2118 url
4845 end
4946
5047 process(name)
51 read_yaml(File.join(base, dir), name)
48 read_yaml(PathManager.join(base, dir), name)
49 generate_excerpt if site.config["page_excerpts"]
5250
5351 data.default_proc = proc do |_, key|
54 site.frontmatter_defaults.find(File.join(dir, name), type, key)
52 site.frontmatter_defaults.find(relative_path, type, key)
5553 end
5654
5755 Jekyll::Hooks.trigger :pages, :post_init, self
6361 #
6462 # Returns the String destination directory.
6563 def dir
66 if url.end_with?(FORWARD_SLASH)
67 url
68 else
69 url_dir = File.dirname(url)
70 url_dir.end_with?(FORWARD_SLASH) ? url_dir : "#{url_dir}/"
71 end
64 url.end_with?("/") ? url : url_dir
7265 end
7366
7467 # The full path and filename of the post. Defined in the YAML of the post
9689 #
9790 # Returns the String url.
9891 def url
99 @url ||= URL.new({
92 @url ||= URL.new(
10093 :template => template,
10194 :placeholders => url_placeholders,
102 :permalink => permalink,
103 }).to_s
95 :permalink => permalink
96 ).to_s
10497 end
10598
10699 # Returns a hash of URL placeholder names (as symbols) mapping to the
117110 #
118111 # name - The String filename of the page file.
119112 #
113 # NOTE: `String#gsub` removes all trailing periods (in comparison to `String#chomp`)
120114 # Returns nothing.
121115 def process(name)
116 return unless name
117
122118 self.ext = File.extname(name)
123 self.basename = name[0..-ext.length - 1]
119 self.basename = name[0..-ext.length - 1].gsub(%r!\.*\z!, "")
124120 end
125121
126122 # Add any necessary layouts to this post
145141
146142 # The path to the page source file, relative to the site source
147143 def relative_path
148 File.join(*[@dir, @name].map(&:to_s).reject(&:empty?)).sub(%r!\A\/!, "")
144 @relative_path ||= PathManager.join(@dir, @name).delete_prefix("/")
149145 end
150146
151147 # Obtain destination path.
154150 #
155151 # Returns the destination file path String.
156152 def destination(dest)
157 path = site.in_dest_dir(dest, URL.unescape_path(url))
158 path = File.join(path, "index") if url.end_with?("/")
159 path << output_ext unless path.end_with? output_ext
160 path
153 @destination ||= {}
154 @destination[dest] ||= begin
155 path = site.in_dest_dir(dest, URL.unescape_path(url))
156 path = File.join(path, "index") if url.end_with?("/")
157 path << output_ext unless path.end_with? output_ext
158 path
159 end
161160 end
162161
163162 # Returns the object as a debug String.
164163 def inspect
165 "#<Jekyll::Page @name=#{name.inspect}>"
164 "#<#{self.class} @relative_path=#{relative_path.inspect}>"
166165 end
167166
168167 # Returns the Boolean of whether this Page is HTML or not.
182181 def write?
183182 true
184183 end
184
185 def excerpt_separator
186 @excerpt_separator ||= (data["excerpt_separator"] || site.config["excerpt_separator"]).to_s
187 end
188
189 def excerpt
190 return @excerpt if defined?(@excerpt)
191
192 @excerpt = data["excerpt"] ? data["excerpt"].to_s : nil
193 end
194
195 def generate_excerpt?
196 !excerpt_separator.empty? && instance_of?(Jekyll::Page) && html?
197 end
198
199 private
200
201 def generate_excerpt
202 return unless generate_excerpt?
203
204 data["excerpt"] ||= Jekyll::PageExcerpt.new(self)
205 end
206
207 def url_dir
208 @url_dir ||= begin
209 value = File.dirname(url)
210 value.end_with?("/") ? value : "#{value}/"
211 end
212 end
185213 end
186214 end
0 # frozen_string_literal: true
1
2 module Jekyll
3 class PageExcerpt < Excerpt
4 attr_reader :doc
5 alias_method :id, :relative_path
6
7 EXCERPT_ATTRIBUTES = (Page::ATTRIBUTES_FOR_LIQUID - %w(excerpt)).freeze
8 private_constant :EXCERPT_ATTRIBUTES
9
10 def to_liquid
11 @to_liquid ||= doc.to_liquid(EXCERPT_ATTRIBUTES)
12 end
13
14 def render_with_liquid?
15 return false if data["render_with_liquid"] == false
16
17 Jekyll::Utils.has_liquid_construct?(content)
18 end
19
20 def inspect
21 "#<#{self.class} id=#{id.inspect}>"
22 end
23 end
24 end
99 def read_yaml(*)
1010 @data ||= {}
1111 end
12
13 def inspect
14 "#<Jekyll:PageWithoutAFile @name=#{name.inspect}>"
15 end
1612 end
1713 end
0 # frozen_string_literal: true
1
2 module Jekyll
3 # A singleton class that caches frozen instances of path strings returned from its methods.
4 #
5 # NOTE:
6 # This class exists because `File.join` allocates an Array and returns a new String on every
7 # call using **the same arguments**. Caching the result means reduced memory usage.
8 # However, the caches are never flushed so that they can be used even when a site is
9 # regenerating. The results are frozen to deter mutation of the cached string.
10 #
11 # Therefore, employ this class only for situations where caching the result is necessary
12 # for performance reasons.
13 #
14 class PathManager
15 # This class cannot be initialized from outside
16 private_class_method :new
17
18 class << self
19 # Wraps `File.join` to cache the frozen result.
20 # Reassigns `nil`, empty strings and empty arrays to a frozen empty string beforehand.
21 #
22 # Returns a frozen string.
23 def join(base, item)
24 base = "" if base.nil? || base.empty?
25 item = "" if item.nil? || item.empty?
26 @join ||= {}
27 @join[base] ||= {}
28 @join[base][item] ||= File.join(base, item).freeze
29 end
30
31 # Ensures the questionable path is prefixed with the base directory
32 # and prepends the questionable path with the base directory if false.
33 #
34 # Returns a frozen string.
35 def sanitized_path(base_directory, questionable_path)
36 @sanitized_path ||= {}
37 @sanitized_path[base_directory] ||= {}
38 @sanitized_path[base_directory][questionable_path] ||= if questionable_path.nil?
39 base_directory.freeze
40 else
41 sanitize_and_join(
42 base_directory, questionable_path
43 ).freeze
44 end
45 end
46
47 private
48
49 def sanitize_and_join(base_directory, questionable_path)
50 clean_path = if questionable_path.start_with?("~")
51 questionable_path.dup.insert(0, "/")
52 else
53 questionable_path
54 end
55 clean_path = File.expand_path(clean_path, "/")
56 return clean_path if clean_path.eql?(base_directory)
57
58 # remove any remaining extra leading slashes not stripped away by calling
59 # `File.expand_path` above.
60 clean_path.squeeze!("/")
61 return clean_path if clean_path.start_with?(slashed_dir_cache(base_directory))
62
63 clean_path.sub!(%r!\A\w:/!, "/")
64 join(base_directory, clean_path)
65 end
66
67 def slashed_dir_cache(base_directory)
68 @slashed_dir_cache ||= {}
69 @slashed_dir_cache[base_directory] ||= base_directory.sub(%r!\z!, "/")
70 end
71 end
72 end
73 end
1212 #
1313
1414 def self.inherited(const)
15 return catch_inheritance(const) do |const_|
15 catch_inheritance(const) do |const_|
1616 catch_inheritance(const_)
1717 end
1818 end
2222 def self.catch_inheritance(const)
2323 const.define_singleton_method :inherited do |const_|
2424 (@children ||= Set.new).add const_
25 if block_given?
26 yield const_
27 end
25 yield const_ if block_given?
2826 end
2927 end
3028
4745 # Returns the Symbol priority.
4846 def self.priority(priority = nil)
4947 @priority ||= nil
50 if priority && PRIORITIES.key?(priority)
51 @priority = priority
52 end
48 @priority = priority if priority && PRIORITIES.key?(priority)
5349 @priority || :normal
5450 end
5551
6157 #
6258 # Returns the safety Boolean.
6359 def self.safe(safe = nil)
64 unless defined?(@safe) && safe.nil?
65 @safe = safe
66 end
60 @safe = safe unless defined?(@safe) && safe.nil?
6761 @safe || false
6862 end
6963
7367 #
7468 # Returns -1, 0, 1.
7569 def self.<=>(other)
76 PRIORITIES[other.priority] <=> PRIORITIES[self.priority]
70 PRIORITIES[other.priority] <=> PRIORITIES[priority]
7771 end
7872
7973 # Spaceship is priority [higher -> lower]
3636 # Returns false only if no dependencies have been specified, otherwise nothing.
3737 def require_theme_deps
3838 return false unless site.theme.runtime_dependencies
39
3940 site.theme.runtime_dependencies.each do |dep|
4041 next if dep.name == "jekyll"
42
4143 External.require_with_graceful_fail(dep.name) if plugin_allowed?(dep.name)
4244 end
4345 end
4446
4547 def self.require_from_bundler
46 if !ENV["JEKYLL_NO_BUNDLER_REQUIRE"] && File.file?("Gemfile")
48 if !ENV["JEKYLL_NO_BUNDLER_REQUIRE"] && gemfile_exists?
4749 require "bundler"
4850
4951 Bundler.setup
5658 else
5759 false
5860 end
61 end
62
63 # Check for the existence of a Gemfile.
64 #
65 # Returns true if a Gemfile exists in the places bundler will look
66 def self.gemfile_exists?
67 File.file?("Gemfile") || (ENV["BUNDLE_GEMFILE"] && File.file?(ENV["BUNDLE_GEMFILE"]))
5968 end
6069
6170 # Check whether a gem plugin is allowed to be used during this build.
103112 pagination_included = (site.config["plugins"] || []).include?("jekyll-paginate") ||
104113 defined?(Jekyll::Paginate)
105114 if site.config["paginate"] && !pagination_included
106 Jekyll::Deprecator.deprecation_message "You appear to have pagination " \
107 "turned on, but you haven't included the `jekyll-paginate` gem. " \
108 "Ensure you have `plugins: [jekyll-paginate]` in your configuration file."
115 Jekyll::Deprecator.deprecation_message <<~MSG
116 You appear to have pagination turned on, but you haven't included the `jekyll-paginate`
117 gem. Ensure you have `plugins: [jekyll-paginate]` in your configuration file.
118 MSG
109119 end
110120 end
111121 end
0 # frozen_string_literal: true
1
2 module Jekyll
3 class Profiler
4 TERMINAL_TABLE_STYLES = {
5 :alignment => :right,
6 :border_top => false,
7 :border_bottom => false,
8 }.freeze
9 private_constant :TERMINAL_TABLE_STYLES
10
11 def self.tabulate(table_rows)
12 require "terminal-table"
13
14 rows = table_rows.dup
15 header = rows.shift
16 output = +"\n"
17
18 table = Terminal::Table.new do |t|
19 t << header
20 t << :separator
21 rows.each { |row| t << row }
22 t.style = TERMINAL_TABLE_STYLES
23 t.align_column(0, :left)
24 end
25
26 output << table.to_s << "\n"
27 end
28
29 def initialize(site)
30 @site = site
31 end
32
33 def profile_process
34 profile_data = { "PHASE" => "TIME" }
35 total_time = 0
36
37 [:reset, :read, :generate, :render, :cleanup, :write].each do |method|
38 start_time = Time.now
39 @site.send(method)
40 end_time = (Time.now - start_time).round(4)
41 profile_data[method.to_s.upcase] = format("%.4f", end_time)
42 total_time += end_time
43 end
44
45 profile_data["TOTAL TIME"] = format("%.4f", total_time)
46
47 Jekyll.logger.info "\nBuild Process Summary:"
48 Jekyll.logger.info Profiler.tabulate(Array(profile_data))
49
50 Jekyll.logger.info "\nSite Render Stats:"
51 @site.print_stats
52 end
53 end
54 end
1313 def read
1414 @site.layouts = LayoutReader.new(site).read
1515 read_directories
16 read_included_excludes
1617 sort_files!
17 @site.data = DataReader.new(site).read(site.config["data_dir"])
1818 CollectionReader.new(site).read
1919 ThemeAssetsReader.new(site).read
20 read_data
21 end
22
23 # Read and merge the data files.
24 # If a theme is specified and it contains data, it will be read.
25 # Site data will overwrite theme data with the same key using the
26 # semantics of Utils.deep_merge_hashes.
27 #
28 # Returns nothing.
29 def read_data
30 @site.data = DataReader.new(site).read(site.config["data_dir"])
31 return unless site.theme&.data_path
32
33 theme_data = DataReader.new(
34 site,
35 :in_source_dir => site.method(:in_theme_dir)
36 ).read(site.theme.data_path)
37 @site.data = Jekyll::Utils.deep_merge_hashes(theme_data, @site.data)
2038 end
2139
2240 # Sorts posts, pages, and static files.
3856
3957 return unless File.directory?(base)
4058
59 dot_dirs = []
60 dot_pages = []
61 dot_static_files = []
62
4163 dot = Dir.chdir(base) { filter_entries(Dir.entries("."), base) }
42 dot_dirs = dot.select { |file| File.directory?(@site.in_source_dir(base, file)) }
43 dot_files = (dot - dot_dirs)
44 dot_pages = dot_files.select do |file|
45 Utils.has_yaml_header?(@site.in_source_dir(base, file))
46 end
47 dot_static_files = dot_files - dot_pages
64 dot.each do |entry|
65 file_path = @site.in_source_dir(base, entry)
66 if File.directory?(file_path)
67 dot_dirs << entry
68 elsif Utils.has_yaml_header?(file_path)
69 dot_pages << entry
70 else
71 dot_static_files << entry
72 end
73 end
4874
4975 retrieve_posts(dir)
5076 retrieve_dirs(base, dir, dot_dirs)
6086 # Returns nothing.
6187 def retrieve_posts(dir)
6288 return if outside_configured_directory?(dir)
89
6390 site.posts.docs.concat(post_reader.read_posts(dir))
6491 site.posts.docs.concat(post_reader.read_drafts(dir)) if site.show_drafts
6592 end
74101 def retrieve_dirs(_base, dir, dot_dirs)
75102 dot_dirs.each do |file|
76103 dir_path = site.in_source_dir(dir, file)
77 rel_path = File.join(dir, file)
78 unless @site.dest.chomp("/") == dir_path
79 @site.reader.read_directories(rel_path)
80 end
104 rel_path = PathManager.join(dir, file)
105 @site.reader.read_directories(rel_path) unless @site.dest.chomp("/") == dir_path
81106 end
82107 end
83108
125150 def get_entries(dir, subfolder)
126151 base = site.in_source_dir(dir, subfolder)
127152 return [] unless File.exist?(base)
153
128154 entries = Dir.chdir(base) { filter_entries(Dir["**/*"], base) }
129155 entries.delete_if { |e| File.directory?(site.in_source_dir(base, e)) }
130156 end
149175 def post_reader
150176 @post_reader ||= PostReader.new(site)
151177 end
178
179 def read_included_excludes
180 entry_filter = EntryFilter.new(site)
181
182 site.include.each do |entry|
183 entry_path = site.in_source_dir(entry)
184 next if File.directory?(entry_path)
185 next if entry_filter.symlink?(entry_path)
186
187 read_included_file(entry_path) if File.file?(entry_path)
188 end
189 end
190
191 def read_included_file(entry_path)
192 if Utils.has_yaml_header?(entry_path)
193 conditionally_generate_entry(entry_path, site.pages, PageReader)
194 else
195 conditionally_generate_entry(entry_path, site.static_files, StaticFileReader)
196 end
197 end
198
199 def conditionally_generate_entry(entry_path, container, reader)
200 return if container.find { |item| site.in_source_dir(item.relative_path) == entry_path }
201
202 dir, files = File.split(entry_path)
203 dir.sub!(site.source, "")
204 files = Array(files)
205 container.concat(reader.new(site, dir).read(files))
206 end
152207 end
153208 end
44 SPECIAL_COLLECTIONS = %w(posts data).freeze
55
66 attr_reader :site, :content
7
78 def initialize(site)
89 @site = site
910 @content = {}
22 module Jekyll
33 class DataReader
44 attr_reader :site, :content
5 def initialize(site)
5
6 def initialize(site, in_source_dir: nil)
67 @site = site
78 @content = {}
89 @entry_filter = EntryFilter.new(site)
10 @in_source_dir = in_source_dir || @site.method(:in_source_dir)
11 @source_dir = @in_source_dir.call("/")
912 end
1013
1114 # Read all the files in <dir> and adds them to @content
1518 # Returns @content, a Hash of the .yaml, .yml,
1619 # .json, and .csv files in the base directory
1720 def read(dir)
18 base = site.in_source_dir(dir)
21 base = @in_source_dir.call(dir)
1922 read_data_to(base, @content)
2023 @content
2124 end
3538 end
3639
3740 entries.each do |entry|
38 path = @site.in_source_dir(dir, entry)
41 path = @in_source_dir.call(dir, entry)
3942 next if @entry_filter.symlink?(path)
4043
4144 if File.directory?(path)
5154 #
5255 # Returns the contents of the data file.
5356 def read_data_file(path)
57 Jekyll.logger.debug "Reading:", path.sub(@source_dir, "")
58
5459 case File.extname(path).downcase
5560 when ".csv"
56 CSV.read(path, {
57 :headers => true,
58 :encoding => site.config["encoding"],
59 }).map(&:to_hash)
61 CSV.read(path, **csv_config).map { |row| convert_row(row) }
6062 when ".tsv"
61 CSV.read(path, {
62 :col_sep => "\t",
63 :headers => true,
64 :encoding => site.config["encoding"],
65 }).map(&:to_hash)
63 CSV.read(path, **tsv_config).map { |row| convert_row(row) }
6664 else
6765 SafeYAML.load_file(path)
6866 end
7270 name.gsub(%r![^\w\s-]+|(?<=^|\b\s)\s+(?=$|\s?\b)!, "")
7371 .gsub(%r!\s+!, "_")
7472 end
73
74 private
75
76 # @return [Hash]
77 def csv_config
78 @csv_config ||= read_config("csv_reader")
79 end
80
81 # @return [Hash]
82 def tsv_config
83 @tsv_config ||= read_config("tsv_reader", { :col_sep => "\t" })
84 end
85
86 # @param config_key [String]
87 # @param overrides [Hash]
88 # @return [Hash]
89 # @see https://ruby-doc.org/stdlib-2.5.0/libdoc/csv/rdoc/CSV.html#Converters
90 def read_config(config_key, overrides = {})
91 reader_config = config[config_key] || {}
92
93 defaults = {
94 :converters => reader_config.fetch("csv_converters", []).map(&:to_sym),
95 :headers => reader_config.fetch("headers", true),
96 :encoding => reader_config.fetch("encoding", config["encoding"]),
97 }
98
99 defaults.merge(overrides)
100 end
101
102 def config
103 @config ||= site.config
104 end
105
106 # @param row [Array, CSV::Row]
107 # @return [Array, Hash]
108 def convert_row(row)
109 row.instance_of?(CSV::Row) ? row.to_hash : row
110 end
75111 end
76112 end
22 module Jekyll
33 class LayoutReader
44 attr_reader :site
5
56 def initialize(site)
67 @site = site
78 @layouts = {}
2223 end
2324
2425 def layout_directory
25 @layout_directory ||= (layout_directory_in_cwd || layout_directory_inside_source)
26 @layout_directory ||= site.in_source_dir(site.config["layouts_dir"])
2627 end
2728
2829 def theme_layout_directory
5354
5455 def within(directory)
5556 return unless File.exist?(directory)
57
5658 Dir.chdir(directory) { yield }
57 end
58
59 def layout_directory_inside_source
60 site.in_source_dir(site.config["layouts_dir"])
61 end
62
63 def layout_directory_in_cwd
64 dir = Jekyll.sanitized_path(Dir.pwd, site.config["layouts_dir"])
65 if File.directory?(dir) && !site.safe
66 dir
67 end
6859 end
6960 end
7061 end
22 module Jekyll
33 class PageReader
44 attr_reader :site, :dir, :unfiltered_content
5
56 def initialize(site, dir)
67 @site = site
78 @dir = dir
89 @unfiltered_content = []
910 end
1011
11 # Read all the files in <source>/<dir>/ for Yaml header and create a new Page
12 # object for each file.
12 # Create a new `Jekyll::Page` object for each entry in a given array.
1313 #
14 # dir - The String relative path of the directory to read.
14 # files - An array of file names inside `@dir`
1515 #
16 # Returns an array of static pages.
16 # Returns an array of publishable `Jekyll::Page` objects.
1717 def read(files)
18 files.map do |page|
18 files.each do |page|
1919 @unfiltered_content << Page.new(@site, @site.source, @dir, page)
2020 end
2121 @unfiltered_content.select { |page| site.publisher.publish?(page) }
22 module Jekyll
33 class PostReader
44 attr_reader :site, :unfiltered_content
5
56 def initialize(site)
67 @site = site
78 end
3334 #
3435 # Returns nothing.
3536 def read_publishable(dir, magic_dir, matcher)
36 read_content(dir, magic_dir, matcher).tap { |docs| docs.each(&:read) }
37 .select do |doc|
38 if doc.content.valid_encoding?
39 site.publisher.publish?(doc).tap do |will_publish|
40 if !will_publish && site.publisher.hidden_in_the_future?(doc)
41 Jekyll.logger.debug "Skipping:", "#{doc.relative_path} has a future date"
42 end
43 end
44 else
45 Jekyll.logger.debug "Skipping:", "#{doc.relative_path} is not valid UTF-8"
46 false
47 end
48 end
37 read_content(dir, magic_dir, matcher)
38 .tap { |docs| docs.each(&:read) }
39 .select { |doc| processable?(doc) }
4940 end
5041
5142 # Read all the content files from <source>/<dir>/magic_dir
5950 # Returns klass type of content files
6051 def read_content(dir, magic_dir, matcher)
6152 @site.reader.get_entries(dir, magic_dir).map do |entry|
62 next unless entry =~ matcher
53 next unless matcher.match?(entry)
54
6355 path = @site.in_source_dir(File.join(dir, magic_dir, entry))
64 Document.new(path, {
65 :site => @site,
66 :collection => @site.posts,
67 })
68 end.reject(&:nil?)
56 Document.new(path,
57 :site => @site,
58 :collection => @site.posts)
59 end.tap(&:compact!)
60 end
61
62 private
63
64 def processable?(doc)
65 if doc.content.nil?
66 Jekyll.logger.debug "Skipping:", "Content in #{doc.relative_path} is nil"
67 false
68 elsif !doc.content.valid_encoding?
69 Jekyll.logger.debug "Skipping:", "#{doc.relative_path} is not valid UTF-8"
70 false
71 else
72 publishable?(doc)
73 end
74 end
75
76 def publishable?(doc)
77 site.publisher.publish?(doc).tap do |will_publish|
78 if !will_publish && site.publisher.hidden_in_the_future?(doc)
79 Jekyll.logger.warn "Skipping:", "#{doc.relative_path} has a future date"
80 end
81 end
6982 end
7083 end
7184 end
22 module Jekyll
33 class StaticFileReader
44 attr_reader :site, :dir, :unfiltered_content
5
56 def initialize(site, dir)
67 @site = site
78 @dir = dir
89 @unfiltered_content = []
910 end
1011
11 # Read all the files in <source>/<dir>/ for Yaml header and create a new Page
12 # object for each file.
12 # Create a new StaticFile object for every entry in a given list of basenames.
1313 #
14 # dir - The String relative path of the directory to read.
14 # files - an array of file basenames.
1515 #
1616 # Returns an array of static files.
1717 def read(files)
18 files.map do |file|
18 files.each do |file|
1919 @unfiltered_content << StaticFile.new(@site, @site.source, @dir, file)
2020 end
2121 @unfiltered_content
22 module Jekyll
33 class ThemeAssetsReader
44 attr_reader :site
5
56 def initialize(site)
67 @site = site
78 end
89
910 def read
10 return unless site.theme && site.theme.assets_path
11 return unless site.theme&.assets_path
1112
1213 Find.find(site.theme.assets_path) do |path|
1314 next if File.directory?(path)
15
1416 if File.symlink?(path)
1517 Jekyll.logger.warn "Theme reader:", "Ignored symlinked asset: #{path}"
1618 else
2022 end
2123
2224 private
25
2326 def read_theme_asset(path)
2427 base = site.theme.root
2528 dir = File.dirname(path.sub("#{site.theme.root}/", ""))
2730
2831 if Utils.has_yaml_header?(path)
2932 append_unless_exists site.pages,
30 Jekyll::Page.new(site, base, dir, name)
33 Jekyll::Page.new(site, base, dir, name)
3134 else
3235 append_unless_exists site.static_files,
33 Jekyll::StaticFile.new(site, base, "/#{dir}", name)
36 Jekyll::StaticFile.new(site, base, "/#{dir}", name)
3437 end
3538 end
3639
3740 def append_unless_exists(haystack, new_item)
3841 if haystack.any? { |file| file.relative_path == new_item.relative_path }
3942 Jekyll.logger.debug "Theme:",
40 "Ignoring #{new_item.relative_path} in theme due to existing file " \
41 "with that path in site."
43 "Ignoring #{new_item.relative_path} in theme due to existing file " \
44 "with that path in site."
4245 return
4346 end
4447
2020 # Returns a boolean.
2121 def regenerate?(document)
2222 return true if disabled
23
2324 case document
2425 when Page
2526 regenerate_page?(document)
2728 regenerate_document?(document)
2829 else
2930 source_path = document.respond_to?(:path) ? document.path : nil
30 dest_path = if document.respond_to?(:destination)
31 document.destination(@site.dest)
32 end
31 dest_path = document.destination(@site.dest) if document.respond_to?(:destination)
3332 source_modified_or_dest_missing?(source_path, dest_path)
3433 end
3534 end
8887 return true if path.nil?
8988
9089 # Check for path in cache
91 if cache.key? path
92 return cache[path]
93 end
90 return cache[path] if cache.key? path
9491
9592 if metadata[path]
9693 # If we have seen this file before,
164161 end
165162 end
166163
167 private
168164 def regenerate_page?(document)
169165 document.asset_file? || document.data["regenerate"] ||
170166 source_modified_or_dest_missing?(
172168 )
173169 end
174170
175 private
176171 def regenerate_document?(document)
177172 !document.write? || document.data["regenerate"] ||
178173 source_modified_or_dest_missing?(
180175 )
181176 end
182177
183 private
184178 def existing_file_modified?(path)
185179 # If one of this file dependencies have been modified,
186180 # set the regeneration bit for both the dependency and the file to true
187181 metadata[path]["deps"].each do |dependency|
188 if modified?(dependency)
189 return cache[dependency] = cache[path] = true
190 end
182 return cache[dependency] = cache[path] = true if modified?(dependency)
191183 end
192184
193185 if File.exist?(path) && metadata[path]["mtime"].eql?(File.mtime(path))
4545 end
4646
4747 def most_recent_posts
48 @most_recent_posts ||= (site.posts.docs.last(11).reverse - [post]).first(10)
48 @most_recent_posts ||= (site.posts.docs.last(11).reverse! - [post]).first(10)
4949 end
5050 end
5151 end
88 @site = site
99 @document = document
1010 @payload = site_payload
11 @layouts = nil
1112 end
1213
1314 # Fetches the payload used in Liquid rendering.
3435 #
3536 # Returns Array of Converter instances.
3637 def converters
37 @converters ||= site.converters.select { |c| c.matches(document.extname) }.sort
38 @converters ||= site.converters.select { |c| c.matches(document.extname) }.tap(&:sort!)
3839 end
3940
4041 # Determine the extname the outputted file should have
6465 # Render the document.
6566 #
6667 # Returns String rendered document output
67 # rubocop: disable AbcSize
68 # rubocop: disable Metrics/AbcSize, Metrics/MethodLength
6869 def render_document
6970 info = {
7071 :registers => { :site => site, :page => payload["page"] },
8283 output = convert(output.to_s)
8384 document.content = output
8485
86 Jekyll.logger.debug "Post-Convert Hooks:", document.relative_path
87 document.trigger_hooks(:post_convert)
88 output = document.content
89
8590 if document.place_in_layout?
8691 Jekyll.logger.debug "Rendering Layout:", document.relative_path
8792 output = place_in_layouts(output, payload, info)
8994
9095 output
9196 end
92 # rubocop: enable AbcSize
97 # rubocop: enable Metrics/AbcSize, Metrics/MethodLength
9398
9499 # Convert the document using the converters which match this renderer's document.
95100 #
96101 # Returns String the converted content.
97102 def convert(content)
98103 converters.reduce(content) do |output, converter|
99 begin
100 converter.convert output
101 rescue StandardError => e
102 Jekyll.logger.error "Conversion error:",
103 "#{converter.class} encountered an error while "\
104 "converting '#{document.relative_path}':"
105 Jekyll.logger.error("", e.to_s)
106 raise e
107 end
104 converter.convert output
105 rescue StandardError => e
106 Jekyll.logger.error "Conversion error:",
107 "#{converter.class} encountered an error while " \
108 "converting '#{document.relative_path}':"
109 Jekyll.logger.error("", e.to_s)
110 raise e
108111 end
109112 end
110113
120123 template = site.liquid_renderer.file(path).parse(content)
121124 template.warnings.each do |e|
122125 Jekyll.logger.warn "Liquid Warning:",
123 LiquidRenderer.format_error(e, path || document.relative_path)
126 LiquidRenderer.format_error(e, path || document.relative_path)
124127 end
125128 template.render!(payload, info)
126 # rubocop: disable RescueException
129 # rubocop: disable Lint/RescueException
127130 rescue Exception => e
128131 Jekyll.logger.error "Liquid Exception:",
129 LiquidRenderer.format_error(e, path || document.relative_path)
132 LiquidRenderer.format_error(e, path || document.relative_path)
130133 raise e
131134 end
132 # rubocop: enable RescueException
135 # rubocop: enable Lint/RescueException
133136
134137 # Checks if the layout specified in the document actually exists
135138 #
157160 output = render_layout(output, layout, info)
158161 add_regenerator_dependencies(layout)
159162
160 if (layout = site.layouts[layout.data["layout"]])
161 break if used.include?(layout)
162 used << layout
163 end
163 next unless (layout = site.layouts[layout.data["layout"]])
164 break if used.include?(layout)
165
166 used << layout
164167 end
165168 output
166169 end
167170
171 private
172
168173 # Checks if the layout specified in the document actually exists
169174 #
170175 # layout - the layout to check
171176 # Returns nothing
172 private
173177 def validate_layout(layout)
174 if invalid_layout?(layout)
175 Jekyll.logger.warn(
176 "Build Warning:",
177 "Layout '#{document.data["layout"]}' requested "\
178 "in #{document.relative_path} does not exist."
179 )
180 elsif !layout.nil?
181 layout_source = layout.path.start_with?(site.source) ? :site : :theme
182 Jekyll.logger.debug "Layout source:", layout_source
183 end
178 return unless invalid_layout?(layout)
179
180 Jekyll.logger.warn "Build Warning:", "Layout '#{document.data["layout"]}' requested " \
181 "in #{document.relative_path} does not exist."
184182 end
185183
186184 # Render layout content into document.output
187185 #
188186 # Returns String rendered content
189 private
190187 def render_layout(output, layout, info)
191188 payload["content"] = output
192189 payload["layout"] = Utils.deep_merge_hashes(layout.data, payload["layout"] || {})
195192 layout.content,
196193 payload,
197194 info,
198 layout.relative_path
195 layout.path
199196 )
200197 end
201198
202 private
203199 def add_regenerator_dependencies(layout)
204200 return unless document.write?
201
205202 site.regenerator.add_dependency(
206203 site.in_source_dir(document.path),
207204 layout.path
211208 # Set page content to payload and assign pager if document has one.
212209 #
213210 # Returns nothing
214 private
215211 def assign_pages!
216212 payload["page"] = document.to_liquid
217 payload["paginator"] = if document.respond_to?(:pager)
218 document.pager.to_liquid
219 end
213 payload["paginator"] = (document.pager.to_liquid if document.respond_to?(:pager))
220214 end
221215
222216 # Set related posts to payload if document is a post.
223217 #
224218 # Returns nothing
225 private
226219 def assign_current_document!
227220 payload["site"].current_document = document
228221 end
230223 # Set highlighter prefix and suffix
231224 #
232225 # Returns nothing
233 private
234226 def assign_highlighter_options!
235227 payload["highlighter_prefix"] = converters.first.highlighter_prefix
236228 payload["highlighter_suffix"] = converters.first.highlighter_suffix
237229 end
238230
239 private
240231 def assign_layout_data!
241232 layout = layouts[document.data["layout"]]
242 if layout
243 payload["layout"] = Utils.deep_merge_hashes(layout.data, payload["layout"] || {})
244 end
245 end
246
247 private
233 payload["layout"] = Utils.deep_merge_hashes(layout.data, payload["layout"] || {}) if layout
234 end
235
248236 def permalink_ext
249237 document_permalink = document.permalink
250238 if document_permalink && !document_permalink.end_with?("/")
253241 end
254242 end
255243
256 private
257244 def converter_output_ext
258245 if output_exts.size == 1
259246 output_exts.last
262249 end
263250 end
264251
265 private
266252 def output_exts
267253 @output_exts ||= converters.map do |c|
268254 c.output_ext(document.extname)
269 end.compact
270 end
271
272 private
255 end.tap(&:compact!)
256 end
257
273258 def liquid_options
274259 @liquid_options ||= site.config["liquid"]
275260 end
11
22 module Jekyll
33 class Site
4 attr_reader :source, :dest, :config
5 attr_accessor :layouts, :pages, :static_files, :drafts,
6 :exclude, :include, :lsi, :highlighter, :permalink_style,
7 :time, :future, :unpublished, :safe, :plugins, :limit_posts,
8 :show_drafts, :keep_files, :baseurl, :data, :file_read_opts,
9 :gems, :plugin_manager, :theme
10
11 attr_accessor :converters, :generators, :reader
12 attr_reader :regenerator, :liquid_renderer, :includes_load_paths
4 attr_accessor :baseurl, :converters, :data, :drafts, :exclude,
5 :file_read_opts, :future, :gems, :generators, :highlighter,
6 :include, :inclusions, :keep_files, :layouts, :limit_posts,
7 :lsi, :pages, :permalink_style, :plugin_manager, :plugins,
8 :reader, :safe, :show_drafts, :static_files, :theme, :time,
9 :unpublished
10
11 attr_reader :cache_dir, :config, :dest, :filter_cache, :includes_load_paths,
12 :liquid_renderer, :profiler, :regenerator, :source
1313
1414 # Public: Initialize a new Site.
1515 #
2121
2222 self.config = config
2323
24 @cache_dir = in_source_dir(config["cache_dir"])
25 @filter_cache = {}
26
2427 @reader = Reader.new(self)
28 @profiler = Profiler.new(self)
2529 @regenerator = Regenerator.new(self)
2630 @liquid_renderer = LiquidRenderer.new(self)
2731
4347 @config = config.clone
4448
4549 %w(safe lsi highlighter baseurl exclude include future unpublished
46 show_drafts limit_posts keep_files).each do |opt|
47 self.send("#{opt}=", config[opt])
50 show_drafts limit_posts keep_files).each do |opt|
51 send("#{opt}=", config[opt])
4852 end
4953
5054 # keep using `gems` to avoid breaking change
5155 self.gems = config["plugins"]
5256
57 configure_cache
5358 configure_plugins
5459 configure_theme
5560 configure_include_paths
5762
5863 self.permalink_style = config["permalink"].to_sym
5964
65 # Read in a _config.yml from the current theme-gem at the very end.
66 @config = load_theme_configuration(config) if theme
6067 @config
6168 end
6269
6471 #
6572 # Returns nothing.
6673 def process
74 return profiler.profile_process if config["profile"]
75
6776 reset
6877 read
6978 generate
7079 render
7180 cleanup
7281 write
73 print_stats if config["profile"]
7482 end
7583
7684 def print_stats
7785 Jekyll.logger.info @liquid_renderer.stats_table
7886 end
7987
88 # rubocop:disable Metrics/AbcSize
89 # rubocop:disable Metrics/MethodLength
90 #
8091 # Reset Site details.
8192 #
8293 # Returns nothing
8798 Time.now
8899 end
89100 self.layouts = {}
101 self.inclusions = {}
90102 self.pages = []
91103 self.static_files = []
92104 self.data = {}
105 @post_attr_hash = {}
93106 @site_data = nil
94107 @collections = nil
108 @documents = nil
95109 @docs_to_write = nil
96110 @regenerator.clear_cache
97111 @liquid_renderer.reset
98112 @site_cleaner = nil
99
100 if limit_posts < 0
101 raise ArgumentError, "limit_posts must be a non-negative number"
102 end
103
113 frontmatter_defaults.reset
114
115 raise ArgumentError, "limit_posts must be a non-negative number" if limit_posts.negative?
116
117 Jekyll::Cache.clear_if_config_changed config
104118 Jekyll::Hooks.trigger :site, :after_reset, self
105 end
119 nil
120 end
121 # rubocop:enable Metrics/MethodLength
122 # rubocop:enable Metrics/AbcSize
106123
107124 # Load necessary libraries, plugins, converters, and generators.
108125 #
123140 Pathname.new(source).ascend do |path|
124141 if path == dest_pathname
125142 raise Errors::FatalException,
126 "Destination directory cannot be or contain the Source directory."
143 "Destination directory cannot be or contain the Source directory."
127144 end
128145 end
129146 end
134151 #
135152 # Returns a Hash containing collection name-to-instance pairs.
136153 def collections
137 @collections ||= Hash[collection_names.map do |coll|
138 [coll, Jekyll::Collection.new(self, coll)]
139 end]
154 @collections ||= collection_names.each_with_object({}) do |name, hsh|
155 hsh[name] = Jekyll::Collection.new(self, name)
156 end
140157 end
141158
142159 # The list of collection names.
163180 reader.read
164181 limit_posts!
165182 Jekyll::Hooks.trigger :site, :post_read, self
183 nil
166184 end
167185
168186 # Run each of the Generators.
173191 start = Time.now
174192 generator.generate(self)
175193 Jekyll.logger.debug "Generating:",
176 "#{generator.class} finished in #{Time.now - start} seconds."
177 end
194 "#{generator.class} finished in #{Time.now - start} seconds."
195 end
196 nil
178197 end
179198
180199 # Render the site to the destination.
191210 render_pages(payload)
192211
193212 Jekyll::Hooks.trigger :site, :post_render, self, payload
213 nil
194214 end
195215
196216 # Remove orphaned files and empty directories in destination.
198218 # Returns nothing.
199219 def cleanup
200220 site_cleaner.cleanup!
221 nil
201222 end
202223
203224 # Write static files, pages, and posts.
204225 #
205226 # Returns nothing.
206227 def write
228 Jekyll::Commands::Doctor.conflicting_urls(self)
207229 each_site_file do |item|
208230 item.write(dest) if regenerator.regenerate?(item)
209231 end
210232 regenerator.write_metadata
211233 Jekyll::Hooks.trigger :site, :post_write, self
234 nil
212235 end
213236
214237 def posts
231254 def post_attr_hash(post_attr)
232255 # Build a hash map based on the specified post attribute ( post attr =>
233256 # array of posts ) then sort each array in reverse order.
234 hash = Hash.new { |h, key| h[key] = [] }
235 posts.docs.each do |p|
236 p.data[post_attr].each { |t| hash[t] << p } if p.data[post_attr]
237 end
238 hash.each_value { |posts| posts.sort!.reverse! }
239 hash
257 @post_attr_hash[post_attr] ||= begin
258 hash = Hash.new { |h, key| h[key] = [] }
259 posts.docs.each do |p|
260 p.data[post_attr]&.each { |t| hash[t] << p }
261 end
262 hash.each_value { |posts| posts.sort!.reverse! }
263 hash
264 end
240265 end
241266
242267 def tags
278303 # klass - The Class of the Converter to fetch.
279304 def find_converter_instance(klass)
280305 @find_converter_instance ||= {}
281 @find_converter_instance[klass] ||= begin
282 converters.find { |converter| converter.instance_of?(klass) } || \
283 raise("No Converters found for #{klass}")
284 end
306 @find_converter_instance[klass] ||= converters.find do |converter|
307 converter.instance_of?(klass)
308 end || \
309 raise("No Converters found for #{klass}")
285310 end
286311
287312 # klass - class or module containing the subclasses.
290315 # passed in as argument.
291316
292317 def instantiate_subclasses(klass)
293 klass.descendants.select { |c| !safe || c.safe }.sort.map do |c|
294 c.new(config)
318 klass.descendants.select { |c| !safe || c.safe }.tap do |result|
319 result.sort!
320 result.map! { |c| c.new(config) }
295321 end
296322 end
297323
301327 # Returns
302328 def relative_permalinks_are_deprecated
303329 if config["relative_permalinks"]
304 Jekyll.logger.abort_with "Since v3.0, permalinks for pages" \
305 " in subfolders must be relative to the" \
306 " site source directory, not the parent" \
307 " directory. Check https://jekyllrb.com/docs/upgrading/"\
308 " for more info."
330 Jekyll.logger.abort_with "Since v3.0, permalinks for pages " \
331 "in subfolders must be relative to the " \
332 "site source directory, not the parent " \
333 "directory. Check https://jekyllrb.com/docs/upgrading/ " \
334 "for more info."
309335 end
310336 end
311337
314340 # Returns an Array of Documents which should be written
315341 def docs_to_write
316342 documents.select(&:write?)
343 end
344
345 # Get the to be written static files
346 #
347 # Returns an Array of StaticFiles which should be written
348 def static_files_to_write
349 static_files.select(&:write?)
317350 end
318351
319352 # Get all the documents
326359 end
327360
328361 def each_site_file
329 %w(pages static_files docs_to_write).each do |type|
362 seen_files = []
363 %w(pages static_files_to_write docs_to_write).each do |type|
330364 send(type).each do |item|
365 next if seen_files.include?(item)
366
331367 yield item
368 seen_files << item
332369 end
333370 end
334371 end
376413 # Returns a path which is prefixed with the theme root directory.
377414 def in_theme_dir(*paths)
378415 return nil unless theme
416
379417 paths.reduce(theme.root) do |base, path|
380418 Jekyll.sanitized_path(base, path)
381419 end
393431 end
394432 end
395433
434 # Public: Prefix a given path with the cache directory.
435 #
436 # paths - (optional) path elements to a file or directory within the
437 # cache directory
438 #
439 # Returns a path which is prefixed with the cache directory.
440 def in_cache_dir(*paths)
441 paths.reduce(cache_dir) do |base, path|
442 Jekyll.sanitized_path(base, path)
443 end
444 end
445
396446 # Public: The full path to the directory that houses all the collections registered
397447 # with the current site.
398448 #
402452 @collections_path ||= dir_str.empty? ? source : in_source_dir(dir_str)
403453 end
404454
455 # Public
456 #
457 # Returns the object as a debug String.
458 def inspect
459 "#<#{self.class} @source=#{@source}>"
460 end
461
462 private
463
464 def load_theme_configuration(config)
465 return config if config["ignore_theme_config"] == true
466
467 theme_config_file = in_theme_dir("_config.yml")
468 return config unless File.exist?(theme_config_file)
469
470 # Bail out if the theme_config_file is a symlink file irrespective of safe mode
471 return config if File.symlink?(theme_config_file)
472
473 theme_config = SafeYAML.load_file(theme_config_file)
474 return config unless theme_config.is_a?(Hash)
475
476 Jekyll.logger.info "Theme Config file:", theme_config_file
477
478 # theme_config should not be overriding Jekyll's defaults
479 theme_config.delete_if { |key, _| Configuration::DEFAULTS.key?(key) }
480
481 # Override theme_config with existing config and return the result.
482 # Additionally ensure we return a `Jekyll::Configuration` instance instead of a Hash.
483 Utils.deep_merge_hashes(theme_config, config)
484 .each_with_object(Jekyll::Configuration.new) do |(key, value), conf|
485 conf[key] = value
486 end
487 end
488
405489 # Limits the current posts; removes the posts which exceed the limit_posts
406490 #
407491 # Returns nothing
408 private
409492 def limit_posts!
410 if limit_posts > 0
493 if limit_posts.positive?
411494 limit = posts.docs.length < limit_posts ? posts.docs.length : limit_posts
412 self.posts.docs = posts.docs[-limit, limit]
495 posts.docs = posts.docs[-limit, limit]
413496 end
414497 end
415498
417500 # already exist.
418501 #
419502 # Returns The Cleaner
420 private
421503 def site_cleaner
422504 @site_cleaner ||= Cleaner.new(self)
423505 end
424506
425 private
507 def hide_cache_dir_from_git
508 @cache_gitignore_path ||= in_source_dir(config["cache_dir"], ".gitignore")
509 return if File.exist?(@cache_gitignore_path)
510
511 cache_dir_path = in_source_dir(config["cache_dir"])
512 FileUtils.mkdir_p(cache_dir_path) unless File.directory?(cache_dir_path)
513
514 File.open(@cache_gitignore_path, "wb") do |file|
515 file.puts("# ignore everything in this directory\n*")
516 end
517 end
518
519 # Disable Marshaling cache to disk in Safe Mode
520 def configure_cache
521 Jekyll::Cache.cache_dir = in_source_dir(config["cache_dir"], "Jekyll/Cache")
522 if safe || config["disable_disk_cache"]
523 Jekyll::Cache.disable_disk_cache!
524 else
525 hide_cache_dir_from_git
526 end
527 end
528
426529 def configure_plugins
427530 self.plugin_manager = Jekyll::PluginManager.new(self)
428531 self.plugins = plugin_manager.plugins_path
429532 end
430533
431 private
432534 def configure_theme
433535 self.theme = nil
434536 return if config["theme"].nil?
437539 if config["theme"].is_a?(String)
438540 Jekyll::Theme.new(config["theme"])
439541 else
440 Jekyll.logger.warn "Theme:", "value of 'theme' in config should be " \
441 "String to use gem-based themes, but got #{config["theme"].class}"
542 Jekyll.logger.warn "Theme:", "value of 'theme' in config should be String to use " \
543 "gem-based themes, but got #{config["theme"].class}"
442544 nil
443545 end
444546 end
445547
446 private
447548 def configure_include_paths
448549 @includes_load_paths = Array(in_source_dir(config["includes_dir"].to_s))
449 @includes_load_paths << theme.includes_path if theme && theme.includes_path
450 end
451
452 private
550 @includes_load_paths << theme.includes_path if theme&.includes_path
551 end
552
453553 def configure_file_read_opts
454554 self.file_read_opts = {}
455 self.file_read_opts[:encoding] = config["encoding"] if config["encoding"]
555 file_read_opts[:encoding] = config["encoding"] if config["encoding"]
456556 self.file_read_opts = Jekyll::Utils.merged_file_read_opts(self, {})
457557 end
458558
459 private
460559 def render_docs(payload)
461560 collections.each_value do |collection|
462561 collection.docs.each do |document|
465564 end
466565 end
467566
468 private
469567 def render_pages(payload)
470 pages.flatten.each do |page|
568 pages.each do |page|
471569 render_regenerated(page, payload)
472570 end
473571 end
474572
475 private
476573 def render_regenerated(document, payload)
477574 return unless regenerator.regenerate?(document)
478 document.output = Jekyll::Renderer.new(self, document, payload).run
575
576 document.renderer.payload = payload
577 document.output = document.renderer.run
479578 document.trigger_hooks(:post_render)
480579 end
481580 end
33 class StaticFile
44 extend Forwardable
55
6 attr_reader :relative_path, :extname, :name, :data
6 attr_reader :relative_path, :extname, :name
77
88 def_delegator :to_liquid, :to_json, :to_json
99
2424 # base - The String path to the <source>.
2525 # dir - The String path between <source> and the file.
2626 # name - The String filename of the file.
27 # rubocop: disable ParameterLists
27 # rubocop: disable Metrics/ParameterLists
2828 def initialize(site, base, dir, name, collection = nil)
2929 @site = site
3030 @base = base
3333 @collection = collection
3434 @relative_path = File.join(*[@dir, @name].compact)
3535 @extname = File.extname(@name)
36 @data = @site.frontmatter_defaults.all(relative_path, type)
37 end
38 # rubocop: enable ParameterLists
36 end
37 # rubocop: enable Metrics/ParameterLists
3938
4039 # Returns source file path.
4140 def path
42 # Static file is from a collection inside custom collections directory
43 if !@collection.nil? && !@site.config["collections_dir"].empty?
44 File.join(*[@base, @site.config["collections_dir"], @dir, @name].compact)
45 else
46 File.join(*[@base, @dir, @name].compact)
47 end
41 @path ||= if !@collection.nil? && !@site.config["collections_dir"].empty?
42 File.join(*[@base, @site.config["collections_dir"], @dir, @name].compact)
43 else
44 File.join(*[@base, @dir, @name].compact)
45 end
4846 end
4947
5048 # Obtain destination path.
5351 #
5452 # Returns destination file path.
5553 def destination(dest)
56 @site.in_dest_dir(*[dest, destination_rel_dir, @name].compact)
54 @destination ||= {}
55 @destination[dest] ||= @site.in_dest_dir(dest, Jekyll::URL.unescape_path(url))
5756 end
5857
5958 def destination_rel_dir
8584 # Returns true unless the defaults for the destination path from
8685 # _config.yml contain `published: false`.
8786 def write?
88 defaults.fetch("published", true)
87 publishable = defaults.fetch("published", true)
88 return publishable unless @collection
89
90 publishable && @collection.write?
8991 end
9092
9193 # Write the static file to the destination directory (if modified).
9597 # Returns false if the file was not modified since last time (no-op).
9698 def write(dest)
9799 dest_path = destination(dest)
98
99100 return false if File.exist?(dest_path) && !modified?
101
100102 self.class.mtimes[path] = mtime
101103
102104 FileUtils.mkdir_p(File.dirname(dest_path))
106108 true
107109 end
108110
111 def data
112 @data ||= @site.frontmatter_defaults.all(relative_path, type)
113 end
114
109115 def to_liquid
110116 @to_liquid ||= Drops::StaticFileDrop.new(self)
111117 end
112118
119 # Generate "basename without extension" and strip away any trailing periods.
120 # NOTE: `String#gsub` removes all trailing periods (in comparison to `String#chomp`)
113121 def basename
114 File.basename(name, extname)
122 @basename ||= File.basename(name, extname).gsub(%r!\.*\z!, "")
115123 end
116124
117125 def placeholders
118126 {
119127 :collection => @collection.label,
120 :path => relative_path[
121 @collection.relative_directory.size..relative_path.size],
128 :path => cleaned_relative_path,
122129 :output_ext => "",
123 :name => "",
130 :name => basename,
124131 :title => "",
125132 }
126133 end
127134
135 # Similar to Jekyll::Document#cleaned_relative_path.
136 # Generates a relative path with the collection's directory removed when applicable
137 # and additionally removes any multiple periods in the string.
138 #
139 # NOTE: `String#gsub!` removes all trailing periods (in comparison to `String#chomp!`)
140 #
141 # Examples:
142 # When `relative_path` is "_methods/site/my-cool-avatar...png":
143 # cleaned_relative_path
144 # # => "/site/my-cool-avatar"
145 #
146 # Returns the cleaned relative path of the static file.
147 def cleaned_relative_path
148 @cleaned_relative_path ||= begin
149 cleaned = relative_path[0..-extname.length - 1]
150 cleaned.gsub!(%r!\.*\z!, "")
151 cleaned.sub!(@collection.relative_directory, "") if @collection
152 cleaned
153 end
154 end
155
128156 # Applies a similar URL-building technique as Jekyll::Document that takes
129157 # the collection's URL template into account. The default URL template can
130 # be overriden in the collection's configuration in _config.yml.
158 # be overridden in the collection's configuration in _config.yml.
131159 def url
132 @url ||= if @collection.nil?
133 relative_path
160 @url ||= begin
161 base = if @collection.nil?
162 cleaned_relative_path
134163 else
135 ::Jekyll::URL.new({
164 Jekyll::URL.new(
136165 :template => @collection.url_template,
137 :placeholders => placeholders,
138 })
166 :placeholders => placeholders
167 )
139168 end.to_s.chomp("/")
169 base << extname
170 end
140171 end
141172
142173 # Returns the type of the collection if present, nil otherwise.
150181 @defaults ||= @site.frontmatter_defaults.all url, type
151182 end
152183
184 # Returns a debug string on inspecting the static file.
185 # Includes only the relative path of the object.
186 def inspect
187 "#<#{self.class} @relative_path=#{relative_path.inspect}>"
188 end
189
153190 private
191
154192 def copy_file(dest_path)
155193 if @site.safe || Jekyll.env == "production"
156194 FileUtils.cp(path, dest_path)
1515 severity ||= UNKNOWN
1616 @logdev = logdevice(severity)
1717
18 if @logdev.nil? || severity < @level
19 return true
20 end
18 return true if @logdev.nil? || severity < @level
19
2120 progname ||= @progname
2221 if message.nil?
2322 if block_given?
99 # forms: name, name=value, or name="<quoted list>"
1010 #
1111 # <quoted list> is a space-separated list of numbers
12 SYNTAX = %r!^([a-zA-Z0-9.+#_-]+)((\s+\w+(=(\w+|"([0-9]+\s)*[0-9]+"))?)*)$!
12 SYNTAX = %r!^([a-zA-Z0-9.+#_-]+)((\s+\w+(=(\w+|"([0-9]+\s)*[0-9]+"))?)*)$!.freeze
1313
1414 def initialize(tag_name, markup, tokens)
1515 super
1717 @lang = Regexp.last_match(1).downcase
1818 @highlight_options = parse_options(Regexp.last_match(2))
1919 else
20 raise SyntaxError, <<-MSG
21 Syntax Error in tag 'highlight' while parsing the following markup:
20 raise SyntaxError, <<~MSG
21 Syntax Error in tag 'highlight' while parsing the following markup:
2222
23 #{markup}
23 #{markup}
2424
25 Valid syntax: highlight <lang> [linenos]
26 MSG
25 Valid syntax: highlight <lang> [linenos]
26 MSG
2727 end
2828 end
29
30 LEADING_OR_TRAILING_LINE_TERMINATORS = %r!\A(\n|\r)+|(\n|\r)+\z!.freeze
2931
3032 def render(context)
3133 prefix = context["highlighter_prefix"] || ""
3234 suffix = context["highlighter_suffix"] || ""
33 code = super.to_s.gsub(%r!\A(\n|\r)+|(\n|\r)+\z!, "")
34
35 is_safe = !!context.registers[:site].safe
35 code = super.to_s.gsub(LEADING_OR_TRAILING_LINE_TERMINATORS, "")
3636
3737 output =
3838 case context.registers[:site].highlighter
39 when "pygments"
40 render_pygments(code, is_safe)
4139 when "rouge"
4240 render_rouge(code)
41 when "pygments"
42 render_pygments(code, context)
4343 else
4444 render_codehighlighter(code)
4545 end
4848 prefix + rendered_output + suffix
4949 end
5050
51 def sanitized_opts(opts, is_safe)
52 if is_safe
53 Hash[[
54 [:startinline, opts.fetch(:startinline, nil)],
55 [:hl_lines, opts.fetch(:hl_lines, nil)],
56 [:linenos, opts.fetch(:linenos, nil)],
57 [:encoding, opts.fetch(:encoding, "utf-8")],
58 [:cssclass, opts.fetch(:cssclass, nil)],
59 ].reject { |f| f.last.nil? }]
60 else
61 opts
62 end
63 end
64
6551 private
6652
67 OPTIONS_REGEX = %r!(?:\w="[^"]*"|\w=\w|\w)+!
53 OPTIONS_REGEX = %r!(?:\w="[^"]*"|\w=\w|\w)+!.freeze
6854
6955 def parse_options(input)
7056 options = {}
7460 input.scan(OPTIONS_REGEX) do |opt|
7561 key, value = opt.split("=")
7662 # If a quoted list, convert to array
77 if value && value.include?('"')
63 if value&.include?('"')
7864 value.delete!('"')
7965 value = value.split
8066 end
8571 options
8672 end
8773
88 def render_pygments(code, is_safe)
89 Jekyll::External.require_with_graceful_fail("pygments") unless defined?(Pygments)
90
91 highlighted_code = Pygments.highlight(
92 code,
93 :lexer => @lang,
94 :options => sanitized_opts(@highlight_options, is_safe)
95 )
96
97 if highlighted_code.nil?
98 Jekyll.logger.error <<-MSG
99 There was an error highlighting your code:
100
101 #{code}
102
103 While attempting to convert the above code, Pygments.rb returned an unacceptable value.
104 This is usually a timeout problem solved by running `jekyll build` again.
105 MSG
106 raise ArgumentError, "Pygments.rb returned an unacceptable value "\
107 "when attempting to highlight some code."
108 end
109
110 highlighted_code.sub('<div class="highlight"><pre>', "").sub("</pre></div>", "")
74 def render_pygments(code, _context)
75 Jekyll.logger.warn "Warning:", "Highlight Tag no longer supports rendering with Pygments."
76 Jekyll.logger.warn "", "Using the default highlighter, Rouge, instead."
77 render_rouge(code)
11178 end
11279
11380 def render_rouge(code)
114 formatter = Jekyll::Utils::Rouge.html_formatter(
115 :line_numbers => @highlight_options[:linenos],
116 :wrap => false,
117 :css_class => "highlight",
118 :gutter_class => "gutter",
119 :code_class => "code"
120 )
81 require "rouge"
82 formatter = ::Rouge::Formatters::HTML.new
83 if @highlight_options[:linenos]
84 formatter = ::Rouge::Formatters::HTMLTable.new(
85 formatter,
86 {
87 :css_class => "highlight",
88 :gutter_class => "gutter",
89 :code_class => "code",
90 }
91 )
92 end
12193 lexer = ::Rouge::Lexer.find_fancy(@lang, code) || Rouge::Lexers::PlainText
12294 formatter.format(lexer.lex(code))
12395 end
131103 "class=\"language-#{@lang.to_s.tr("+", "-")}\"",
132104 "data-lang=\"#{@lang}\"",
133105 ].join(" ")
134 "<figure class=\"highlight\"><pre><code #{code_attributes}>"\
135 "#{code.chomp}</code></pre></figure>"
106 "<figure class=\"highlight\"><pre><code #{code_attributes}>" \
107 "#{code.chomp}</code></pre></figure>"
136108 end
137109 end
138110 end
11
22 module Jekyll
33 module Tags
4 class IncludeTagError < StandardError
5 attr_accessor :path
6
7 def initialize(msg, path)
8 super(msg)
9 @path = path
10 end
11 end
12
134 class IncludeTag < Liquid::Tag
145 VALID_SYNTAX = %r!
156 ([\w-]+)\s*=\s*
16 (?:"([^"\\]*(?:\\.[^"\\]*)*)"|'([^'\\]*(?:\\.[^'\\]*)*)'|([\w\.-]+))
17 !x
7 (?:"([^"\\]*(?:\\.[^"\\]*)*)"|'([^'\\]*(?:\\.[^'\\]*)*)'|([\w.-]+))
8 !x.freeze
189 VARIABLE_SYNTAX = %r!
19 (?<variable>[^{]*(\{\{\s*[\w\-\.]+\s*(\|.*)?\}\}[^\s{}]*)+)
10 (?<variable>[^{]*(\{\{\s*[\w\-.]+\s*(\|.*)?\}\}[^\s{}]*)+)
2011 (?<params>.*)
21 !mx
22
23 FULL_VALID_SYNTAX = %r!\A\s*(?:#{VALID_SYNTAX}(?=\s|\z)\s*)*\z!
24 VALID_FILENAME_CHARS = %r!^[\w/\.-]+$!
25 INVALID_SEQUENCES = %r![./]{2,}!
12 !mx.freeze
13
14 FULL_VALID_SYNTAX = %r!\A\s*(?:#{VALID_SYNTAX}(?=\s|\z)\s*)*\z!.freeze
15 VALID_FILENAME_CHARS = %r!^[\w/.\-()+~\#@]+$!.freeze
16 INVALID_SEQUENCES = %r![./]{2,}!.freeze
2617
2718 def initialize(tag_name, markup, tokens)
2819 super
29 matched = markup.strip.match(VARIABLE_SYNTAX)
20 markup = markup.strip
21 matched = markup.match(VARIABLE_SYNTAX)
3022 if matched
3123 @file = matched["variable"].strip
3224 @params = matched["params"].strip
3325 else
34 @file, @params = markup.strip.split(%r!\s+!, 2)
26 @file, @params = markup.split(%r!\s+!, 2)
3527 end
3628 validate_params if @params
3729 @tag_name = tag_name
4335
4436 def parse_params(context)
4537 params = {}
46 markup = @params
47
48 while (match = VALID_SYNTAX.match(markup))
49 markup = markup[match.end(0)..-1]
50
51 value = if match[2]
52 match[2].gsub(%r!\\"!, '"')
53 elsif match[3]
54 match[3].gsub(%r!\\'!, "'")
55 elsif match[4]
56 context[match[4]]
38 @params.scan(VALID_SYNTAX) do |key, d_quoted, s_quoted, variable|
39 value = if d_quoted
40 d_quoted.include?('\\"') ? d_quoted.gsub('\\"', '"') : d_quoted
41 elsif s_quoted
42 s_quoted.include?("\\'") ? s_quoted.gsub("\\'", "'") : s_quoted
43 elsif variable
44 context[variable]
5745 end
5846
59 params[match[1]] = value
47 params[key] = value
6048 end
6149 params
6250 end
6351
6452 def validate_file_name(file)
65 if file =~ INVALID_SEQUENCES || file !~ VALID_FILENAME_CHARS
66 raise ArgumentError, <<-MSG
67 Invalid syntax for include tag. File contains invalid characters or sequences:
68
69 #{file}
70
71 Valid syntax:
72
73 #{syntax_example}
74
75 MSG
53 if INVALID_SEQUENCES.match?(file) || !VALID_FILENAME_CHARS.match?(file)
54 raise ArgumentError, <<~MSG
55 Invalid syntax for include tag. File contains invalid characters or sequences:
56
57 #{file}
58
59 Valid syntax:
60
61 #{syntax_example}
62
63 MSG
7664 end
7765 end
7866
7967 def validate_params
80 unless @params =~ FULL_VALID_SYNTAX
81 raise ArgumentError, <<-MSG
82 Invalid syntax for include tag:
83
84 #{@params}
85
86 Valid syntax:
87
88 #{syntax_example}
89
90 MSG
68 unless FULL_VALID_SYNTAX.match?(@params)
69 raise ArgumentError, <<~MSG
70 Invalid syntax for include tag:
71
72 #{@params}
73
74 Valid syntax:
75
76 #{syntax_example}
77
78 MSG
9179 end
9280 end
9381
9886
9987 # Render the variable if required
10088 def render_variable(context)
101 if @file =~ VARIABLE_SYNTAX
102 partial = context.registers[:site]
103 .liquid_renderer
104 .file("(variable)")
105 .parse(@file)
106 partial.render!(context)
107 end
89 Liquid::Template.parse(@file).render(context) if VARIABLE_SYNTAX.match?(@file)
10890 end
10991
11092 def tag_includes_dirs(context)
11496 def locate_include_file(context, file, safe)
11597 includes_dirs = tag_includes_dirs(context)
11698 includes_dirs.each do |dir|
117 path = File.join(dir.to_s, file.to_s)
99 path = PathManager.join(dir, file)
118100 return path if valid_include_file?(path, dir.to_s, safe)
119101 end
120102 raise IOError, could_not_locate_message(file, includes_dirs, safe)
146128 end
147129
148130 def add_include_to_dependency(site, path, context)
149 if context.registers[:page] && context.registers[:page].key?("path")
131 if context.registers[:page]&.key?("path")
150132 site.regenerator.add_dependency(
151133 site.in_source_dir(context.registers[:page]["path"]),
152134 path
196178 private
197179
198180 def could_not_locate_message(file, includes_dirs, safe)
199 message = "Could not locate the included file '#{file}' in any of "\
200 "#{includes_dirs}. Ensure it exists in one of those directories and"
181 message = "Could not locate the included file '#{file}' in any of #{includes_dirs}. " \
182 "Ensure it exists in one of those directories and"
201183 message + if safe
202184 " is not a symlink as those are not allowed in safe mode."
203185 else
206188 end
207189 end
208190
191 # Do not inherit from this class.
192 # TODO: Merge into the `Jekyll::Tags::IncludeTag` in v5.0
193 class OptimizedIncludeTag < IncludeTag
194 def render(context)
195 @site ||= context.registers[:site]
196
197 file = render_variable(context) || @file
198 validate_file_name(file)
199
200 @site.inclusions[file] ||= locate_include_file(file)
201 inclusion = @site.inclusions[file]
202
203 add_include_to_dependency(inclusion, context) if @site.config["incremental"]
204
205 context.stack do
206 context["include"] = parse_params(context) if @params
207 inclusion.render(context)
208 end
209 end
210
211 private
212
213 def locate_include_file(file)
214 @site.includes_load_paths.each do |dir|
215 path = PathManager.join(dir, file)
216 return Inclusion.new(@site, dir, file) if valid_include_file?(path, dir)
217 end
218 raise IOError, could_not_locate_message(file, @site.includes_load_paths, @site.safe)
219 end
220
221 def valid_include_file?(path, dir)
222 File.file?(path) && !outside_scope?(path, dir)
223 end
224
225 def outside_scope?(path, dir)
226 @site.safe && !realpath_prefixed_with?(path, dir)
227 end
228
229 def realpath_prefixed_with?(path, dir)
230 File.realpath(path).start_with?(dir)
231 rescue StandardError
232 false
233 end
234
235 def add_include_to_dependency(inclusion, context)
236 page = context.registers[:page]
237 return unless page&.key?("path")
238
239 absolute_path = \
240 if page["collection"]
241 @site.in_source_dir(@site.config["collections_dir"], page["path"])
242 else
243 @site.in_source_dir(page["path"])
244 end
245
246 @site.regenerator.add_dependency(absolute_path, inclusion.path)
247 end
248 end
249
209250 class IncludeRelativeTag < IncludeTag
210251 def tag_includes_dirs(context)
211252 Array(page_path(context)).freeze
212253 end
213254
214255 def page_path(context)
215 if context.registers[:page].nil?
216 context.registers[:site].source
217 else
218 site = context.registers[:site]
219 page_payload = context.registers[:page]
220 resource_path = \
221 if page_payload["collection"].nil?
222 page_payload["path"]
223 else
224 File.join(site.config["collections_dir"], page_payload["path"])
225 end
226 resource_path.sub!(%r!/#excerpt\z!, "")
227 site.in_source_dir File.dirname(resource_path)
228 end
256 page, site = context.registers.values_at(:page, :site)
257 return site.source unless page
258
259 site.in_source_dir File.dirname(resource_path(page, site))
260 end
261
262 private
263
264 def resource_path(page, site)
265 path = page["path"]
266 path = File.join(site.config["collections_dir"], path) if page["collection"]
267 path.delete_suffix("/#excerpt")
229268 end
230269 end
231270 end
232271 end
233272
234 Liquid::Template.register_tag("include", Jekyll::Tags::IncludeTag)
273 Liquid::Template.register_tag("include", Jekyll::Tags::OptimizedIncludeTag)
235274 Liquid::Template.register_tag("include_relative", Jekyll::Tags::IncludeRelativeTag)
22 module Jekyll
33 module Tags
44 class Link < Liquid::Tag
5 include Jekyll::Filters::URLFilters
6
57 class << self
68 def tag_name
7 self.name.split("::").last.downcase
9 name.split("::").last.downcase
810 end
911 end
1012
1517 end
1618
1719 def render(context)
20 @context = context
1821 site = context.registers[:site]
22 relative_path = Liquid::Template.parse(@relative_path).render(context)
23 relative_path_with_leading_slash = PathManager.join("", relative_path)
1924
2025 site.each_site_file do |item|
21 return item.url if item.relative_path == @relative_path
26 return relative_url(item) if item.relative_path == relative_path
2227 # This takes care of the case for static files that have a leading /
23 return item.url if item.relative_path == "/#{@relative_path}"
28 return relative_url(item) if item.relative_path == relative_path_with_leading_slash
2429 end
2530
26 raise ArgumentError, <<-MSG
27 Could not find document '#{@relative_path}' in tag '#{self.class.tag_name}'.
31 raise ArgumentError, <<~MSG
32 Could not find document '#{relative_path}' in tag '#{self.class.tag_name}'.
2833
29 Make sure the document exists and the path is correct.
30 MSG
34 Make sure the document exists and the path is correct.
35 MSG
3136 end
3237 end
3338 end
22 module Jekyll
33 module Tags
44 class PostComparer
5 MATCHER = %r!^(.+/)*(\d+-\d+-\d+)-(.*)$!
5 MATCHER = %r!^(.+/)*(\d+-\d+-\d+)-(.*)$!.freeze
66
77 attr_reader :path, :date, :slug, :name
88
1212 all, @path, @date, @slug = *name.sub(%r!^/!, "").match(MATCHER)
1313 unless all
1414 raise Jekyll::Errors::InvalidPostNameError,
15 "'#{name}' does not contain valid date and/or title."
15 "'#{name}' does not contain valid date and/or title."
1616 end
1717
18 escaped_slug = Regexp.escape(slug)
19 @name_regex = %r!^_posts/#{path}#{date}-#{escaped_slug}\.[^.]+|
20 ^#{path}_posts/?#{date}-#{escaped_slug}\.[^.]+!x
18 basename_pattern = "#{date}-#{Regexp.escape(slug)}\\.[^.]+"
19 @name_regex = %r!^_posts/#{path}#{basename_pattern}|^#{path}_posts/?#{basename_pattern}!
2120 end
2221
2322 def post_date
24 @post_date ||= Utils.parse_date(date,
25 "\"#{date}\" does not contain valid date and/or title.")
23 @post_date ||= Utils.parse_date(
24 date,
25 "'#{date}' does not contain valid date and/or title."
26 )
2627 end
2728
2829 def ==(other)
3738 end
3839
3940 private
41
4042 # Construct the directory-aware post slug for a Jekyll::Post
4143 #
4244 # other - the Jekyll::Post
4749 if path.nil? || path == ""
4850 other.data["slug"]
4951 else
50 path + "/" + other.data["slug"]
52 "#{path}/#{other.data["slug"]}"
5153 end
5254 end
5355 end
5456
5557 class PostUrl < Liquid::Tag
58 include Jekyll::Filters::URLFilters
59
5660 def initialize(tag_name, post, tokens)
5761 super
5862 @orig_post = post.strip
5963 begin
6064 @post = PostComparer.new(@orig_post)
6165 rescue StandardError => e
62 raise Jekyll::Errors::PostURLError, <<-MSG
63 Could not parse name of post "#{@orig_post}" in tag 'post_url'.
64
65 Make sure the post exists and the name is correct.
66
67 #{e.class}: #{e.message}
68 MSG
66 raise Jekyll::Errors::PostURLError, <<~MSG
67 Could not parse name of post "#{@orig_post}" in tag 'post_url'.
68 Make sure the post exists and the name is correct.
69 #{e.class}: #{e.message}
70 MSG
6971 end
7072 end
7173
7274 def render(context)
75 @context = context
7376 site = context.registers[:site]
7477
75 site.posts.docs.each do |p|
76 return p.url if @post == p
78 site.posts.docs.each do |document|
79 return relative_url(document) if @post == document
7780 end
7881
7982 # New matching method did not match, fall back to old method
8083 # with deprecation warning if this matches
8184
82 site.posts.docs.each do |p|
83 next unless @post.deprecated_equality p
84 Jekyll::Deprecator.deprecation_message "A call to "\
85 "'{% post_url #{@post.name} %}' did not match " \
86 "a post using the new matching method of checking name " \
87 "(path-date-slug) equality. Please make sure that you " \
88 "change this tag to match the post's name exactly."
89 return p.url
85 site.posts.docs.each do |document|
86 next unless @post.deprecated_equality document
87
88 Jekyll::Deprecator.deprecation_message(
89 "A call to '{% post_url #{@post.name} %}' did not match a post using the new " \
90 "matching method of checking name (path-date-slug) equality. Please make sure " \
91 "that you change this tag to match the post's name exactly."
92 )
93 return relative_url(document)
9094 end
9195
92 raise Jekyll::Errors::PostURLError, <<-MSG
93 Could not find post "#{@orig_post}" in tag 'post_url'.
94
95 Make sure the post exists and the name is correct.
96 MSG
96 raise Jekyll::Errors::PostURLError, <<~MSG
97 Could not find post "#{@orig_post}" in tag 'post_url'.
98 Make sure the post exists and the name is correct.
99 MSG
97100 end
98101 end
99102 end
22 module Jekyll
33 class Theme
44 extend Forwardable
5 attr_reader :name
5 attr_reader :name
6
67 def_delegator :gemspec, :version, :version
78
89 def initialize(name)
910 @name = name.downcase.strip
1011 Jekyll.logger.debug "Theme:", name
1112 Jekyll.logger.debug "Theme source:", root
12 configure_sass
1313 end
1414
1515 def root
1717 # Otherwise, Jekyll.sanitized path with prepend the unresolved root
1818 @root ||= File.realpath(gemspec.full_gem_path)
1919 rescue Errno::ENOENT, Errno::EACCES, Errno::ELOOP
20 raise "Path #{gemspec.full_gem_path} does not exist, is not accessible "\
21 "or includes a symbolic link loop"
20 raise "Path #{gemspec.full_gem_path} does not exist, is not accessible or includes " \
21 "a symbolic link loop"
22 end
23
24 # The name of theme directory
25 def basename
26 @basename ||= File.basename(root)
2227 end
2328
2429 def includes_path
25 @includes_path ||= path_for "_includes".freeze
30 @includes_path ||= path_for "_includes"
2631 end
2732
2833 def layouts_path
29 @layouts_path ||= path_for "_layouts".freeze
34 @layouts_path ||= path_for "_layouts"
3035 end
3136
3237 def sass_path
33 @sass_path ||= path_for "_sass".freeze
38 @sass_path ||= path_for "_sass"
3439 end
3540
3641 def assets_path
37 @assets_path ||= path_for "assets".freeze
42 @assets_path ||= path_for "assets"
3843 end
3944
40 def configure_sass
41 return unless sass_path
42 External.require_with_graceful_fail("sass") unless defined?(Sass)
43 Sass.load_paths << sass_path
45 def data_path
46 @data_path ||= path_for "_data"
4447 end
4548
4649 def runtime_dependencies
5558 end
5659
5760 def realpath_for(folder)
58 # This resolves all symlinks for the theme subfolder and then ensures
59 # that the directory remains inside the theme root. This prevents the
60 # use of symlinks for theme subfolders to escape the theme root.
61 # This resolves all symlinks for the theme subfolder and then ensures that the directory
62 # remains inside the theme root. This prevents the use of symlinks for theme subfolders to
63 # escape the theme root.
6164 # However, symlinks are allowed to point to other directories within the theme.
6265 Jekyll.sanitized_path(root, File.realpath(Jekyll.sanitized_path(root, folder.to_s)))
6366 rescue Errno::ENOENT, Errno::EACCES, Errno::ELOOP => e
7275 when Errno::EACCES
7376 Jekyll.logger.error "Theme error:", "Directory '#{folder}' is not accessible."
7477 when Errno::ELOOP
75 Jekyll.logger.error "Theme error:",
76 "Directory '#{folder}' includes a symbolic link loop."
78 Jekyll.logger.error "Theme error:", "Directory '#{folder}' includes a symbolic link loop."
7779 end
7880 end
7981
8183 @gemspec ||= Gem::Specification.find_by_name(name)
8284 rescue Gem::LoadError
8385 raise Jekyll::Errors::MissingDependencyException,
84 "The #{name} theme could not be found."
86 "The #{name} theme could not be found."
8587 end
8688 end
8789 end
00 # frozen_string_literal: true
11
2 class Jekyll::ThemeBuilder
3 SCAFFOLD_DIRECTORIES = %w(
4 assets _layouts _includes _sass
5 ).freeze
2 module Jekyll
3 class ThemeBuilder
4 SCAFFOLD_DIRECTORIES = %w(
5 assets _data _layouts _includes _sass
6 ).freeze
67
7 attr_reader :name, :path, :code_of_conduct
8 attr_reader :name, :path, :code_of_conduct
89
9 def initialize(theme_name, opts)
10 @name = theme_name.to_s.tr(" ", "_").squeeze("_")
11 @path = Pathname.new(File.expand_path(name, Dir.pwd))
12 @code_of_conduct = !!opts["code_of_conduct"]
13 end
14
15 def create!
16 create_directories
17 create_starter_files
18 create_gemspec
19 create_accessories
20 initialize_git_repo
21 end
22
23 def user_name
24 @user_name ||= `git config user.name`.chomp
25 end
26
27 def user_email
28 @user_email ||= `git config user.email`.chomp
29 end
30
31 private
32
33 def root
34 @root ||= Pathname.new(File.expand_path("../", __dir__))
35 end
36
37 def template_file(filename)
38 [
39 root.join("theme_template", "#{filename}.erb"),
40 root.join("theme_template", filename.to_s),
41 ].find(&:exist?)
42 end
43
44 def template(filename)
45 erb.render(template_file(filename).read)
46 end
47
48 def erb
49 @erb ||= ERBRenderer.new(self)
50 end
51
52 def mkdir_p(directories)
53 Array(directories).each do |directory|
54 full_path = path.join(directory)
55 Jekyll.logger.info "create", full_path.to_s
56 FileUtils.mkdir_p(full_path)
57 end
58 end
59
60 def write_file(filename, contents)
61 full_path = path.join(filename)
62 Jekyll.logger.info "create", full_path.to_s
63 File.write(full_path, contents)
64 end
65
66 def create_directories
67 mkdir_p(SCAFFOLD_DIRECTORIES)
68 end
69
70 def create_starter_files
71 %w(page post default).each do |layout|
72 write_file("_layouts/#{layout}.html", template("_layouts/#{layout}.html"))
73 end
74 end
75
76 def create_gemspec
77 write_file("Gemfile", template("Gemfile"))
78 write_file("#{name}.gemspec", template("theme.gemspec"))
79 end
80
81 def create_accessories
82 accessories = %w(README.md LICENSE.txt)
83 accessories << "CODE_OF_CONDUCT.md" if code_of_conduct
84 accessories.each do |filename|
85 write_file(filename, template(filename))
86 end
87 end
88
89 def initialize_git_repo
90 Jekyll.logger.info "initialize", path.join(".git").to_s
91 Dir.chdir(path.to_s) { `git init` }
92 write_file(".gitignore", template("gitignore"))
93 end
94
95 class ERBRenderer
96 extend Forwardable
97
98 def_delegator :@theme_builder, :name, :theme_name
99 def_delegator :@theme_builder, :user_name, :user_name
100 def_delegator :@theme_builder, :user_email, :user_email
101
102 def initialize(theme_builder)
103 @theme_builder = theme_builder
10 def initialize(theme_name, opts)
11 @name = theme_name.to_s.tr(" ", "_").squeeze("_")
12 @path = Pathname.new(File.expand_path(name, Dir.pwd))
13 @code_of_conduct = !!opts["code_of_conduct"]
10414 end
10515
106 def jekyll_version_with_minor
107 Jekyll::VERSION.split(".").take(2).join(".")
16 def create!
17 create_directories
18 create_starter_files
19 create_gemspec
20 create_accessories
21 initialize_git_repo
10822 end
10923
110 def theme_directories
111 SCAFFOLD_DIRECTORIES
24 def user_name
25 @user_name ||= `git config user.name`.chomp
11226 end
11327
114 def render(contents)
115 ERB.new(contents).result binding
28 def user_email
29 @user_email ||= `git config user.email`.chomp
30 end
31
32 private
33
34 def root
35 @root ||= Pathname.new(File.expand_path("../", __dir__))
36 end
37
38 def template_file(filename)
39 [
40 root.join("theme_template", "#{filename}.erb"),
41 root.join("theme_template", filename.to_s),
42 ].find(&:exist?)
43 end
44
45 def template(filename)
46 erb.render(template_file(filename).read)
47 end
48
49 def erb
50 @erb ||= ERBRenderer.new(self)
51 end
52
53 def mkdir_p(directories)
54 Array(directories).each do |directory|
55 full_path = path.join(directory)
56 Jekyll.logger.info "create", full_path.to_s
57 FileUtils.mkdir_p(full_path)
58 end
59 end
60
61 def write_file(filename, contents)
62 full_path = path.join(filename)
63 Jekyll.logger.info "create", full_path.to_s
64 File.write(full_path, contents)
65 end
66
67 def create_directories
68 mkdir_p(SCAFFOLD_DIRECTORIES)
69 end
70
71 def create_starter_files
72 %w(page post default).each do |layout|
73 write_file("_layouts/#{layout}.html", template("_layouts/#{layout}.html"))
74 end
75 end
76
77 def create_gemspec
78 write_file("Gemfile", template("Gemfile"))
79 write_file("#{name}.gemspec", template("theme.gemspec"))
80 end
81
82 def create_accessories
83 accessories = %w(README.md LICENSE.txt)
84 accessories << "CODE_OF_CONDUCT.md" if code_of_conduct
85 accessories.each do |filename|
86 write_file(filename, template(filename))
87 end
88 end
89
90 def initialize_git_repo
91 Jekyll.logger.info "initialize", path.join(".git").to_s
92 Dir.chdir(path.to_s) { `git init` }
93 write_file(".gitignore", template("gitignore"))
94 end
95
96 class ERBRenderer
97 extend Forwardable
98
99 def_delegator :@theme_builder, :name, :theme_name
100 def_delegator :@theme_builder, :user_name, :user_name
101 def_delegator :@theme_builder, :user_email, :user_email
102
103 def initialize(theme_builder)
104 @theme_builder = theme_builder
105 end
106
107 def jekyll_version_with_minor
108 Jekyll::VERSION.split(".").take(2).join(".")
109 end
110
111 def theme_directories
112 SCAFFOLD_DIRECTORIES
113 end
114
115 def render(contents)
116 ERB.new(contents).result binding
117 end
116118 end
117119 end
118120 end
6767 def generate_url_from_hash(template)
6868 @placeholders.inject(template) do |result, token|
6969 break result if result.index(":").nil?
70
7071 if token.last.nil?
7172 # Remove leading "/" to avoid generating urls with `//`
7273 result.gsub("/:#{token.first}", "")
9293
9394 def generate_url_from_drop(template)
9495 template.gsub(%r!:([a-z_]+)!) do |match|
95 pool = possible_keys(match.sub(":", ""))
96 name = Regexp.last_match(1)
97 pool = name.end_with?("_") ? [name, name.chomp!("_")] : [name]
9698
9799 winner = pool.find { |key| @placeholders.key?(key) }
98100 if winner.nil?
99101 raise NoMethodError,
100 "The URL template doesn't have #{pool.join(" or ")} keys. "\
101 "Check your permalink template!"
102 "The URL template doesn't have #{pool.join(" or ")} keys. " \
103 "Check your permalink template!"
102104 end
103105
104106 value = @placeholders[winner]
105107 value = "" if value.nil?
106108 replacement = self.class.escape_path(value)
107109
108 match.sub(":#{winner}", replacement)
109 end.squeeze("/")
110 match.sub!(":#{winner}", replacement)
111 end
110112 end
111113
112114 # Returns a sanitized String URL, stripping "../../" and multiples of "/",
113115 # as well as the beginning "/" so we can enforce and ensure it.
114
115116 def sanitize_url(str)
116 "/#{str}".gsub("..", "/").gsub("./", "").squeeze("/")
117 "/#{str}".gsub("..", "/").tap do |result|
118 result.gsub!("./", "")
119 result.squeeze!("/")
120 end
117121 end
118122
119123 # Escapes a path to be a valid URL path segment
127131 #
128132 # Returns the escaped path.
129133 def self.escape_path(path)
134 return path if path.empty? || %r!^[a-zA-Z0-9./-]+$!.match?(path)
135
130136 # Because URI.escape doesn't escape "?", "[" and "]" by default,
131137 # specify unsafe string (except unreserved, sub-delims, ":", "@" and "/").
132138 #
137143 # pct-encoded = "%" HEXDIG HEXDIG
138144 # sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
139145 # / "*" / "+" / "," / ";" / "="
140 path = Addressable::URI.encode(path)
141 path.encode("utf-8").sub("#", "%23")
146 Addressable::URI.encode(path).encode("utf-8").sub("#", "%23")
142147 end
143148
144149 # Unescapes a URL path segment
152157 #
153158 # Returns the unescaped path.
154159 def self.unescape_path(path)
155 Addressable::URI.unencode(path.encode("utf-8"))
160 path = path.encode("utf-8")
161 return path unless path.include?("%")
162
163 Addressable::URI.unencode(path)
156164 end
157165 end
158166 end
0 # Frozen-string-literal: true
0 # frozen_string_literal: true
11
22 module Jekyll
33 module Utils
55 extend self
66
77 ESCAPE = format("%c", 27)
8 MATCH = %r!#{ESCAPE}\[(?:\d+)(?:;\d+)*(j|k|m|s|u|A|B|G)|\e\(B\e\[m!ix
8 MATCH = %r!#{ESCAPE}\[(?:\d+)(?:;\d+)*(j|k|m|s|u|A|B|G)|\e\(B\e\[m!ix.freeze
99 COLORS = {
1010 :red => 31,
1111 :green => 32,
2020 [stdin, stdout, stderr].each(&:close)
2121 [process.value, out + err]
2222 end
23
2423 end
2524 end
2625 end
22 module Jekyll
33 module Utils
44 module Internet
5
65 # Public: Determine whether the present device has a connection to
76 # the Internet. This allows plugin writers which require the outside
87 # world to have a neat fallback mechanism for offline building.
1716 # end
1817 #
1918 # Returns true if a DNS call can successfully be made, or false if not.
19
2020 module_function
21
2122 def connected?
2223 !dns("example.com").nil?
2324 end
2425
25 private
26 module_function
2726 def dns(domain)
2827 require "resolv"
2928 Resolv::DNS.open do |resolver|
3231 rescue Resolv::ResolvError, Resolv::ResolvTimeout
3332 nil
3433 end
35
3634 end
3735 end
3836 end
44 module Platforms
55 extend self
66
7 # Provides jruby? and mri? which respectively detect these two types of
8 # tested Engines we support, in the future we might probably support the
9 # other one that everyone used to talk about.
10
11 { :jruby? => "jruby", :mri? => "ruby" }.each do |k, v|
12 define_method k do
13 ::RUBY_ENGINE == v
14 end
7 def jruby?
8 RUBY_ENGINE == "jruby"
159 end
1610
17 # --
18 # Allows you to detect "real" Windows, or what we would consider
19 # "real" Windows. That is, that we can pass the basic test and the
20 # /proc/version returns nothing to us.
21 # --
22
23 def vanilla_windows?
24 RbConfig::CONFIG["host_os"] =~ %r!mswin|mingw|cygwin!i && \
25 !proc_version
11 def mri?
12 RUBY_ENGINE == "ruby"
2613 end
27
28 # --
29 # XXX: Remove in 4.0
30 # --
31
32 alias_method :really_windows?, \
33 :vanilla_windows?
34
35 #
36
37 def bash_on_windows?
38 RbConfig::CONFIG["host_os"] =~ %r!linux! && \
39 proc_version =~ %r!microsoft!i
40 end
41
42 #
4314
4415 def windows?
4516 vanilla_windows? || bash_on_windows?
4617 end
4718
48 #
19 # Not a Windows Subsystem for Linux (WSL)
20 def vanilla_windows?
21 rbconfig_host.match?(%r!mswin|mingw|cygwin!) && proc_version.empty?
22 end
23 alias_method :really_windows?, :vanilla_windows?
24
25 # Determine if Windows Subsystem for Linux (WSL)
26 def bash_on_windows?
27 linux_os? && microsoft_proc_version?
28 end
4929
5030 def linux?
51 RbConfig::CONFIG["host_os"] =~ %r!linux! && \
52 proc_version !~ %r!microsoft!i
31 linux_os? && !microsoft_proc_version?
5332 end
5433
55 # Provides windows?, linux?, osx?, unix? so that we can detect
56 # platforms. This is mostly useful for `jekyll doctor` and for testing
57 # where we kick off certain tests based on the platform.
58
59 { :osx? => %r!darwin|mac os!, :unix? => %r!solaris|bsd! }.each do |k, v|
60 define_method k do
61 !!(
62 RbConfig::CONFIG["host_os"] =~ v
63 )
64 end
34 def osx?
35 rbconfig_host.match?(%r!darwin|mac os!)
6536 end
6637
67 #
38 def unix?
39 rbconfig_host.match?(%r!solaris|bsd!)
40 end
6841
6942 private
43
7044 def proc_version
71 @proc_version ||= begin
72 Pathutil.new(
73 "/proc/version"
74 ).read
75 rescue Errno::ENOENT
76 nil
77 end
45 @proc_version ||= \
46 begin
47 File.read("/proc/version").downcase
48 rescue Errno::ENOENT, Errno::EACCES
49 ""
50 end
51 end
52
53 def rbconfig_host
54 @rbconfig_host ||= RbConfig::CONFIG["host_os"].downcase
55 end
56
57 def linux_os?
58 rbconfig_host.include?("linux")
59 end
60
61 def microsoft_proc_version?
62 proc_version.include?("microsoft")
7863 end
7964 end
8065 end
+0
-22
lib/jekyll/utils/rouge.rb less more
0 # frozen_string_literal: true
1
2 Jekyll::External.require_with_graceful_fail("rouge")
3
4 module Jekyll
5 module Utils
6 module Rouge
7
8 def self.html_formatter(*args)
9 if old_api?
10 ::Rouge::Formatters::HTML.new(*args)
11 else
12 ::Rouge::Formatters::HTMLLegacy.new(*args)
13 end
14 end
15
16 def self.old_api?
17 ::Rouge.version.to_s < "2"
18 end
19 end
20 end
21 end
00 # frozen_string_literal: true
1
2 require "thread"
31
42 module Jekyll
53 module Utils
2422
2523 def wait
2624 @lock.synchronize do
27 unless @flag
28 @cond.wait(@lock)
29 end
25 @cond.wait(@lock) unless @flag
3026 end
3127 end
3228 end
1010 # timezone - the IANA Time Zone specified in "_config.yml"
1111 #
1212 # Returns a string that ultimately re-defines ENV["TZ"] in Windows
13 def calculate(timezone)
13 def calculate(timezone, now = Time.now)
1414 External.require_with_graceful_fail("tzinfo") unless defined?(TZInfo)
1515 tz = TZInfo::Timezone.get(timezone)
16 difference = Time.now.to_i - tz.now.to_i
16
17 #
18 # Use period_for_utc and utc_total_offset instead of
19 # period_for and observed_utc_offset for compatibility with tzinfo v1.
20 offset = tz.period_for_utc(now.getutc).utc_total_offset
21
1722 #
1823 # POSIX style definition reverses the offset sign.
1924 # e.g. Eastern Standard Time (EST) that is 5Hrs. to the 'west' of Prime Meridian
2025 # is denoted as:
2126 # EST+5 (or) EST+05:00
22 # Reference: http://www.gnu.org/software/libc/manual/html_node/TZ-Variable.html
23 sign = difference < 0 ? "-" : "+"
24 offset = sign == "-" ? "+" : "-" unless difference.zero?
27 # Reference: https://www.gnu.org/software/libc/manual/html_node/TZ-Variable.html
28 sign = offset.positive? ? "-" : "+"
29
30 rational_hours = offset.abs.to_r / 3600
31 hours = rational_hours.to_i
32 minutes = ((rational_hours - hours) * 60).to_i
33
2534 #
26 # convert the difference (in seconds) to hours, as a rational number, and perform
27 # a modulo operation on it.
28 modulo = modulo_of(rational_hour(difference))
29 #
30 # Format the hour as a two-digit number.
31 # Establish the minutes based on modulo expression.
32 hh = format("%02d", absolute_hour(difference).ceil)
33 mm = modulo.zero? ? "00" : "30"
35 # Format the hours and minutes as two-digit numbers.
36 time = format("%<hours>02d:%<minutes>02d", :hours => hours, :minutes => minutes)
3437
35 Jekyll.logger.debug "Timezone:", "#{timezone} #{offset}#{hh}:#{mm}"
38 Jekyll.logger.debug "Timezone:", "#{timezone} #{sign}#{time}"
3639 #
3740 # Note: The 3-letter-word below doesn't have a particular significance.
38 "WTZ#{sign}#{hh}:#{mm}"
39 end
40
41 private
42
43 # Private: Convert given seconds to an hour as a rational number.
44 #
45 # seconds - supplied as an integer, it is converted to a rational number.
46 # 3600 - no. of seconds in an hour.
47 #
48 # Returns a rational number.
49 def rational_hour(seconds)
50 seconds.to_r / 3600
51 end
52
53 # Private: Convert given seconds to an hour as an absolute number.
54 #
55 # seconds - supplied as an integer, it is converted to its absolute.
56 # 3600 - no. of seconds in an hour.
57 #
58 # Returns an integer.
59 def absolute_hour(seconds)
60 seconds.abs / 3600
61 end
62
63 # Private: Perform a modulo operation on a given fraction.
64 #
65 # fraction - supplied as a rational number, its numerator is divided
66 # by its denominator and the remainder returned.
67 #
68 # Returns an integer.
69 def modulo_of(fraction)
70 fraction.numerator % fraction.denominator
41 "WTZ#{sign}#{time}"
7142 end
7243 end
7344 end
66 autoload :Exec, "jekyll/utils/exec"
77 autoload :Internet, "jekyll/utils/internet"
88 autoload :Platforms, "jekyll/utils/platforms"
9 autoload :Rouge, "jekyll/utils/rouge"
109 autoload :ThreadEvent, "jekyll/utils/thread_event"
1110 autoload :WinTZ, "jekyll/utils/win_tz"
1211
1312 # Constants for use in #slugify
1413 SLUGIFY_MODES = %w(raw default pretty ascii latin).freeze
1514 SLUGIFY_RAW_REGEXP = Regexp.new('\\s+').freeze
16 SLUGIFY_DEFAULT_REGEXP = Regexp.new("[^[:alnum:]]+").freeze
17 SLUGIFY_PRETTY_REGEXP = Regexp.new("[^[:alnum:]._~!$&'()+,;=@]+").freeze
15 SLUGIFY_DEFAULT_REGEXP = Regexp.new("[^\\p{M}\\p{L}\\p{Nd}]+").freeze
16 SLUGIFY_PRETTY_REGEXP = Regexp.new("[^\\p{M}\\p{L}\\p{Nd}._~!$&'()+,;=@]+").freeze
1817 SLUGIFY_ASCII_REGEXP = Regexp.new("[^[A-Za-z0-9]]+").freeze
1918
20 # Takes an indented string and removes the preceding spaces on each line
21
22 def strip_heredoc(str)
23 str.gsub(%r!^[ \t]{#{(str.scan(%r!^[ \t]*(?=\S)!).min || "").size}}!, "")
24 end
25
2619 # Takes a slug and turns it into a simple title.
27
2820 def titleize_slug(slug)
2921 slug.split("-").map!(&:capitalize).join(" ")
3022 end
7567 #
7668 # Returns an array
7769 def pluralized_array_from_hash(hash, singular_key, plural_key)
78 [].tap do |array|
79 value = value_from_singular_key(hash, singular_key)
80 value ||= value_from_plural_key(hash, plural_key)
81 array << value
82 end.flatten.compact
70 array = []
71 value = value_from_singular_key(hash, singular_key)
72 value ||= value_from_plural_key(hash, plural_key)
73
74 array << value
75 array.flatten!
76 array.compact!
77 array
8378 end
8479
8580 def value_from_singular_key(hash, key)
132127 # Returns the parsed date if successful, throws a FatalException
133128 # if not
134129 def parse_date(input, msg = "Input could not be parsed.")
135 Time.parse(input).localtime
130 @parse_date_cache ||= {}
131 @parse_date_cache[input] ||= Time.parse(input).localtime
136132 rescue ArgumentError
137133 raise Errors::InvalidDateError, "Invalid date '#{input}': #{msg}"
138134 end
140136 # Determines whether a given file has
141137 #
142138 # Returns true if the YAML front matter is present.
143 # rubocop: disable PredicateName
139 # rubocop: disable Naming/PredicateName
144140 def has_yaml_header?(file)
145 !!(File.open(file, "rb", &:readline) =~ %r!\A---\s*\r?\n!)
141 File.open(file, "rb", &:readline).match? %r!\A---\s*\r?\n!
146142 rescue EOFError
147143 false
148144 end
149145
150 # Determine whether the given content string contains Liquid Tags or Vaiables
146 # Determine whether the given content string contains Liquid Tags or Variables
151147 #
152148 # Returns true is the string contains sequences of `{%` or `{{`
153149 def has_liquid_construct?(content)
154150 return false if content.nil? || content.empty?
151
155152 content.include?("{%") || content.include?("{{")
156153 end
157 # rubocop: enable PredicateName
154 # rubocop: enable Naming/PredicateName
158155
159156 # Slugify a filename or title.
160157 #
218215 slug = replace_character_sequence_with_hyphen(string, :mode => mode)
219216
220217 # Remove leading/trailing hyphen
221 slug.gsub!(%r!^\-|\-$!i, "")
218 slug.gsub!(%r!^-|-$!i, "")
222219
223220 slug.downcase! unless cased
221 Jekyll.logger.warn("Warning:", "Empty `slug` generated for '#{string}'.") if slug.empty?
224222 slug
225223 end
226224
267265 template
268266 end
269267
270 # Work the same way as Dir.glob but seperating the input into two parts
268 # Work the same way as Dir.glob but separating the input into two parts
271269 # ('dir' + '/' + 'pattern') to make sure the first part('dir') does not act
272270 # as a pattern.
273271 #
289287 # patterns - the patterns (or the pattern) which will be applied under the dir
290288 # flags - the flags which will be applied to the pattern
291289 #
292 # Returns matched pathes
290 # Returns matched paths
293291 def safe_glob(dir, patterns, flags = 0)
294292 return [] unless Dir.exist?(dir)
293
295294 pattern = File.join(Array(patterns))
296295 return [dir] if pattern.empty?
296
297297 Dir.chdir(dir) do
298298 Dir.glob(pattern, flags).map { |f| File.join(dir, f) }
299299 end
303303 # and a given param
304304 def merged_file_read_opts(site, opts)
305305 merged = (site ? site.file_read_opts : {}).merge(opts)
306 if merged[:encoding] && !merged[:encoding].start_with?("bom|")
306
307 # always use BOM when reading UTF-encoded files
308 if merged[:encoding]&.downcase&.start_with?("utf-")
307309 merged[:encoding] = "bom|#{merged[:encoding]}"
308310 end
309 if merged["encoding"] && !merged["encoding"].start_with?("bom|")
311 if merged["encoding"]&.downcase&.start_with?("utf-")
310312 merged["encoding"] = "bom|#{merged["encoding"]}"
311313 end
314
312315 merged
313316 end
314317
315318 private
319
316320 def merge_values(target, overwrite)
317321 target.merge!(overwrite) do |_key, old_val, new_val|
318322 if new_val.nil?
325329 end
326330 end
327331
328 private
329332 def merge_default_proc(target, overwrite)
330333 if target.is_a?(Hash) && overwrite.is_a?(Hash) && target.default_proc.nil?
331334 target.default_proc = overwrite.default_proc
332335 end
333336 end
334337
335 private
336338 def duplicate_frozen_values(target)
337339 target.each do |key, val|
338340 target[key] = val.dup if val.frozen? && duplicable?(val)
343345 #
344346 # See Utils#slugify for a description of the character sequence specified
345347 # by each mode.
346 private
347348 def replace_character_sequence_with_hyphen(string, mode: "default")
348349 replaceable_char =
349350 case mode
00 # frozen_string_literal: true
11
22 module Jekyll
3 VERSION = "3.9.0".freeze
3 VERSION = "4.3.1"
44 end
4949 autoload :EntryFilter, "jekyll/entry_filter"
5050 autoload :Errors, "jekyll/errors"
5151 autoload :Excerpt, "jekyll/excerpt"
52 autoload :PageExcerpt, "jekyll/page_excerpt"
5253 autoload :External, "jekyll/external"
5354 autoload :FrontmatterDefaults, "jekyll/frontmatter_defaults"
5455 autoload :Hooks, "jekyll/hooks"
5556 autoload :Layout, "jekyll/layout"
57 autoload :Inclusion, "jekyll/inclusion"
58 autoload :Cache, "jekyll/cache"
5659 autoload :CollectionReader, "jekyll/readers/collection_reader"
5760 autoload :DataReader, "jekyll/readers/data_reader"
5861 autoload :LayoutReader, "jekyll/readers/layout_reader"
6366 autoload :LogAdapter, "jekyll/log_adapter"
6467 autoload :Page, "jekyll/page"
6568 autoload :PageWithoutAFile, "jekyll/page_without_a_file"
69 autoload :PathManager, "jekyll/path_manager"
6670 autoload :PluginManager, "jekyll/plugin_manager"
6771 autoload :Publisher, "jekyll/publisher"
72 autoload :Profiler, "jekyll/profiler"
6873 autoload :Reader, "jekyll/reader"
6974 autoload :Regenerator, "jekyll/regenerator"
7075 autoload :RelatedPosts, "jekyll/related_posts"
142147
143148 # Public: Set the log writer.
144149 # New log writer must respond to the same methods
145 # as Ruby's interal Logger.
150 # as Ruby's internal Logger.
146151 #
147152 # writer - the new Logger-compatible log transport
148153 #
167172 # Returns the sanitized path.
168173 def sanitized_path(base_directory, questionable_path)
169174 return base_directory if base_directory.eql?(questionable_path)
175 return base_directory if questionable_path.nil?
170176
171 clean_path = questionable_path.dup
172 clean_path.insert(0, "/") if clean_path.start_with?("~")
173 clean_path = File.expand_path(clean_path, "/")
174
175 return clean_path if clean_path.eql?(base_directory)
176
177 if clean_path.start_with?(base_directory.sub(%r!\z!, "/"))
178 clean_path
179 else
180 clean_path.sub!(%r!\A\w:/!, "/")
181 File.join(base_directory, clean_path)
182 end
177 +Jekyll::PathManager.sanitized_path(base_directory, questionable_path)
183178 end
184179
185180 # Conditional optimizations
186 Jekyll::External.require_if_present("liquid-c")
181 Jekyll::External.require_if_present("liquid/c")
187182 end
188183 end
189184
00 _site
11 .sass-cache
2 .jekyll-cache
23 .jekyll-metadata
4 vendor
00 ---
1 permalink: /404.html
12 layout: default
23 ---
34
66 #
77 # For technical reasons, this file is *NOT* reloaded automatically when you use
88 # 'bundle exec jekyll serve'. If you change this file, please restart the server process.
9
9 #
10 # If you need help with YAML syntax, here are some quick references for you:
11 # https://learn-the-web.algonquindesign.ca/topics/markdown-yaml-cheat-sheet/#yaml
12 # https://learnxinyminutes.com/docs/yaml/
13 #
1014 # Site settings
1115 # These are used to personalize your new site. If you look in the HTML files,
1216 # you will see them accessed via {{ site.title }}, {{ site.email }}, and so on.
1317 # You can create any custom variable you would like, and they will be accessible
1418 # in the templates via {{ site.myvariable }}.
19
1520 title: Your awesome title
1621 email: your-email@example.com
1722 description: >- # this means to ignore newlines until "baseurl:"
2429 github_username: jekyll
2530
2631 # Build settings
27 markdown: kramdown
2832 theme: minima
2933 plugins:
3034 - jekyll-feed
3135
3236 # Exclude from processing.
33 # The following items will not be processed, by default. Create a custom list
34 # to override the default setting.
37 # The following items will not be processed, by default.
38 # Any item listed under the `exclude:` key here will be automatically added to
39 # the internal "default list".
40 #
41 # Excluded items can be processed by explicitly listing the directories or
42 # their entries' file path in the `include:` list.
43 #
3544 # exclude:
45 # - .sass-cache/
46 # - .jekyll-cache/
47 # - gemfiles/
3648 # - Gemfile
3749 # - Gemfile.lock
38 # - node_modules
50 # - node_modules/
3951 # - vendor/bundle/
4052 # - vendor/cache/
4153 # - vendor/gems/
55 ---
66 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.
77
8 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.
8 Jekyll requires blog post files to be named according to the following format:
9
10 `YEAR-MONTH-DAY-title.MARKUP`
11
12 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.
913
1014 Jekyll also offers powerful support for code snippets:
1115
0 ---
1 layout: page
2 title: About
3 permalink: /about/
4 ---
5
6 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/)
7
8 You can find the source code for Minima at GitHub:
9 [jekyll][jekyll-organization] /
10 [minima](https://github.com/jekyll/minima)
11
12 You can find the source code for Jekyll at GitHub:
13 [jekyll][jekyll-organization] /
14 [jekyll](https://github.com/jekyll/jekyll)
15
16
17 [jekyll-organization]: https://github.com/jekyll
+0
-18
lib/site_template/about.md less more
0 ---
1 layout: page
2 title: About
3 permalink: /about/
4 ---
5
6 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/)
7
8 You can find the source code for Minima at GitHub:
9 [jekyll][jekyll-organization] /
10 [minima](https://github.com/jekyll/minima)
11
12 You can find the source code for Jekyll at GitHub:
13 [jekyll][jekyll-organization] /
14 [jekyll](https://github.com/jekyll/jekyll)
15
16
17 [jekyll-organization]: https://github.com/jekyll
0 ---
1 # Feel free to add content and custom Front Matter to this file.
2 # To modify the layout, see https://jekyllrb.com/docs/themes/#overriding-theme-defaults
3
4 layout: home
5 ---
+0
-6
lib/site_template/index.md less more
0 ---
1 # Feel free to add content and custom Front Matter to this file.
2 # To modify the layout, see https://jekyllrb.com/docs/themes/#overriding-theme-defaults
3
4 layout: home
5 ---
44 To experiment with this code, add some sample content and run `bundle exec jekyll serve` – this directory is setup just like a Jekyll site!
55
66 TODO: Delete this and the text above, and describe your gem
7
87
98 ## Installation
109
3433
3534 ## Contributing
3635
37 Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/hello. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
36 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.
3837
3938 ## Development
4039
4847 ## License
4948
5049 The theme is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
51
00 *.gem
11 .bundle
2 .jekyll-cache
23 .sass-cache
34 _site
45 Gemfile.lock
99 spec.homepage = "TODO: Put your gem's website or public repo URL here."
1010 spec.license = "MIT"
1111
12 spec.files = `git ls-files -z`.split("\x0").select { |f| f.match(%r!^(<%= theme_directories.join("|") %>|LICENSE|README)!i) }
12 spec.files = `git ls-files -z`.split("\x0").select { |f| f.match(%r!^(<%= theme_directories.join("|") %>|LICENSE|README|_config\.yml)!i) }
1313
1414 spec.add_runtime_dependency "jekyll", "~> <%= jekyll_version_with_minor %>"
15
16 spec.add_development_dependency "bundler", "~> 1.16"
17 spec.add_development_dependency "rake", "~> 12.0"
1815 end
+0
-25
rake/docs.rake less more
0 # frozen_string_literal: true
1
2 #############################################################################
3 #
4 # Packaging tasks for jekyll-docs
5 #
6 #############################################################################
7
8 namespace :docs do
9 desc "Release #{docs_name} v#{version}"
10 task :release => :build do
11 unless `git branch` =~ %r!^\* master$!
12 puts "You must be on the master branch to release!"
13 exit!
14 end
15 sh "gem push pkg/#{docs_name}-#{version}.gem"
16 end
17
18 desc "Build #{docs_name} v#{version} into pkg/"
19 task :build do
20 mkdir_p "pkg"
21 sh "gem build #{docs_name}.gemspec"
22 sh "mv #{docs_name}-#{version}.gem pkg"
23 end
24 end
0 # frozen_string_literal: true
1
2 require "jekyll"
3
4 namespace :profile do
5 desc "Profile allocations from a build session"
6 task :memory, [:file, :mode] do |_t, args|
7 args.with_defaults(file: "memprof.txt", mode: "lite")
8
9 build_phases = [:reset, :read, :generate, :render, :cleanup, :write]
10 safe_mode = false
11
12 if args.mode == "lite"
13 build_phases -= [:render, :generate]
14 safe_mode = true
15 end
16
17 require "memory_profiler"
18
19 report = MemoryProfiler.report do
20 site = Jekyll::Site.new(
21 Jekyll.configuration(
22 "source" => File.expand_path("../docs", __dir__),
23 "destination" => File.expand_path("../docs/_site", __dir__),
24 "safe" => safe_mode
25 )
26 )
27
28 Jekyll.logger.info "Source:", site.source
29 Jekyll.logger.info "Destination:", site.dest
30 Jekyll.logger.info "Plugins and Cache:", site.safe ? "disabled" : "enabled"
31 Jekyll.logger.info "Profiling phases:", build_phases.join(", ").cyan
32 Jekyll.logger.info "Profiling..."
33
34 build_phases.each { |phase| site.send phase }
35
36 Jekyll.logger.info "", "and done. Generating results.."
37 Jekyll.logger.info ""
38 end
39
40 if ENV["CI"]
41 report.pretty_print(scale_bytes: true, color_output: false, normalize_paths: true)
42 else
43 FileUtils.mkdir_p("tmp")
44 report_file = File.join("tmp", args.file)
45
46 total_allocated_output = report.scale_bytes(report.total_allocated_memsize)
47 total_retained_output = report.scale_bytes(report.total_retained_memsize)
48
49 Jekyll.logger.info "Total allocated: #{total_allocated_output} (#{report.total_allocated} objects)".cyan
50 Jekyll.logger.info "Total retained: #{total_retained_output} (#{report.total_retained} objects)".cyan
51
52 report.pretty_print(to_file: report_file, scale_bytes: true, normalize_paths: true)
53 Jekyll.logger.info "\nDetailed Report saved into:", report_file.cyan
54 end
55 end
56 end
1717 sh "git push origin #{current_branch}"
1818 sh "git push origin v#{version}"
1919 sh "gem push pkg/#{name}-#{version}.gem"
20 puts "Do not forget to build and release the docs gem as well."
21 puts "https://github.com/jekyll/jekyll-docs#releasing"
2022 end
2123
2224 desc "Build #{name} v#{version} into pkg/"
66 #############################################################################
77
88 namespace :site do
9 task :generated_pages => [:history, :latest_version, :conduct, :contributing, :support]
9 task :generated_pages => [:history, :latest_version, :conduct, :contributing, :security, :support]
1010
1111 desc "Generate and view the site locally"
1212 task :preview => :generated_pages do
7070 "redirect_from" => "/conduct/index.html",
7171 "editable" => false,
7272 }
73 siteify_file("CODE_OF_CONDUCT.markdown", front_matter)
73 siteify_file(".github/CODE_OF_CONDUCT.markdown", front_matter)
7474 end
7575
7676 desc "Copy the contributing file"
8383 siteify_file(".github/SUPPORT.markdown", "title" => "Support")
8484 end
8585
86 desc "Copy the security policy"
87 task :security do
88 siteify_file(".github/SECURITY.markdown", "title" => "Security Policy")
89 end
90
8691 desc "Write the latest Jekyll version"
8792 task :latest_version do
88 return if version =~ %r!(beta|rc|alpha)!i
93 next if version =~ %r!(beta|rc|alpha)!i
8994 require "safe_yaml/load"
9095 config_file = File.join(docs_folder, "_config.yml")
9196 config = SafeYAML.load_file(config_file)
108113 post.puts("date: #{Time.new.strftime("%Y-%m-%d %H:%M:%S %z")}")
109114 post.puts("author: ")
110115 post.puts("version: #{release}")
111 post.puts("categories: [release]")
116 post.puts("category: release")
112117 post.puts("---")
113118 post.puts
114119 post.puts
0 # frozen_string_literal: true
1
2 module RuboCop
3 module Cop
4 module Jekyll
5 # Checks for `assert_equal(exp, act, msg = nil)` calls containing literal values as
6 # second argument. The second argument should ideally be a method called on the tested
7 # instance.
8 #
9 # @example
10 # # bad
11 # assert_equal @foo.bar, "foobar"
12 # assert_equal @alpha.beta, { "foo" => "bar", "lorem" => "ipsum" }
13 # assert_equal @alpha.omega, ["foobar", "lipsum"]
14 #
15 # # good
16 # assert_equal "foobar", @foo.bar
17 #
18 # assert_equal(
19 # { "foo" => "bar", "lorem" => "ipsum" },
20 # @alpha.beta
21 # )
22 #
23 # assert_equal(
24 # ["foobar", "lipsum"],
25 # @alpha.omega
26 # )
27 #
28 class AssertEqualLiteralActual < Cop
29 MSG = "Provide the 'expected value' as the first argument to `assert_equal`.".freeze
30
31 SIMPLE_LITERALS = %i(
32 true
33 false
34 nil
35 int
36 float
37 str
38 sym
39 complex
40 rational
41 regopt
42 ).freeze
43
44 COMPLEX_LITERALS = %i(
45 array
46 hash
47 pair
48 irange
49 erange
50 regexp
51 ).freeze
52
53 def_node_matcher :literal_actual?, <<-PATTERN
54 (send nil? :assert_equal $(send ...) $#literal?)
55 PATTERN
56
57 def_node_matcher :literal_actual_with_msg?, <<-PATTERN
58 (send nil? :assert_equal $(send ...) $#literal? $#opt_msg?)
59 PATTERN
60
61 def on_send(node)
62 return unless literal_actual?(node) || literal_actual_with_msg?(node)
63 add_offense(node, location: :expression)
64 end
65
66 def autocorrect(node)
67 lambda do |corrector|
68 corrector.replace(node.loc.expression, replacement(node))
69 end
70 end
71
72 private
73
74 def opt_msg?(node)
75 node&.source
76 end
77
78 # This is not implement using a NodePattern because it seems
79 # to not be able to match against an explicit (nil) sexp
80 def literal?(node)
81 node && (simple_literal?(node) || complex_literal?(node))
82 end
83
84 def simple_literal?(node)
85 SIMPLE_LITERALS.include?(node.type)
86 end
87
88 def complex_literal?(node)
89 COMPLEX_LITERALS.include?(node.type) &&
90 node.each_child_node.all?(&method(:literal?))
91 end
92
93 def replacement(node)
94 _, _, first_param, second_param, optional_param = *node
95
96 replaced_text = \
97 if second_param.type == :hash
98 replace_hash_with_variable(first_param.source, second_param.source)
99 elsif second_param.type == :array && second_param.source != "[]"
100 replace_array_with_variable(first_param.source, second_param.source)
101 else
102 replace_based_on_line_length(first_param.source, second_param.source)
103 end
104
105 return "#{replaced_text}, #{optional_param.source}" if optional_param
106 replaced_text
107 end
108
109 def replace_based_on_line_length(first_expression, second_expression)
110 result = "assert_equal #{second_expression}, #{first_expression}"
111 return result if result.length < 80
112
113 # fold long lines independent of Rubocop configuration for better readability
114 <<~TEXT
115 assert_equal(
116 #{second_expression},
117 #{first_expression}
118 )
119 TEXT
120 end
121
122 def replace_hash_with_variable(first_expression, second_expression)
123 expect_expression = if second_expression.start_with?("{")
124 second_expression
125 else
126 "{#{second_expression}}"
127 end
128 <<~TEXT
129 expected = #{expect_expression}
130 assert_equal expected, #{first_expression}
131 TEXT
132 end
133
134 def replace_array_with_variable(first_expression, second_expression)
135 expect_expression = if second_expression.start_with?("%")
136 second_expression
137 else
138 Array(second_expression)
139 end
140 <<~TEXT
141 expected = #{expect_expression}
142 assert_equal expected, #{first_expression}
143 TEXT
144 end
145 end
146 end
147 end
148 end
00 #!/usr/bin/env bash
11
22 time ruby -S bundle exec cucumber \
3 --format progress "$@"
3 --format progress --publish "$@"
1212
1313 echo "$0: respecifying the jekyll install location"
1414 ruby -e "contents = File.read('Gemfile'); File.write('Gemfile', contents.sub(/gem \"jekyll\".*\\n/, 'gem \"jekyll\", path: \"../../\"'))"
15 if [ -n "$KRAMDOWN_VERSION" ]; then
16 echo "$0: respecifying the kramdown version"
17 ruby -e "contents = File.read('Gemfile'); File.write('Gemfile', contents.sub(/gem \"kramdown-parser-gfm\".*\\n/, ''))"
18 echo "gem 'kramdown', '~> $KRAMDOWN_VERSION'" >> Gemfile
19 fi
20 if [ -n "$ROUGE_VERSION" ]; then
21 echo "$0: respecifying the rouge version"
22 echo "gem 'rouge', '~> $ROUGE_VERSION'" >> Gemfile
23 fi
2415 echo "$0: installing default site dependencies"
2516 BUNDLE_GEMFILE=Gemfile bundle install
2617 echo "$0: building the default site"
00 #!/bin/bash
1 echo "Rubocop $(bundle exec rubocop --version)"
2 bundle exec rubocop -D $@
1 echo "RuboCop $(bundle exec rubocop --version)"
2 bundle exec rubocop -D --disable-pending-cops $@
33 success=$?
44 if ((success != 0)); then
55 echo -e "\nTry running \`script/fmt -a\` to automatically fix errors"
0 #!/bin/bash
1
2 file="memprof.txt"
3 mode="core"
4 bundle exec rake profile:memory[$file,$mode]
0 #!/bin/sh
0 #!/bin/bash
11 # Usage: script/travis [ruby-version [file]]
22 # Example: script/travis 2.0 test/failing_test.rb
33 # Example: script/travis 2.3.0
11 # Vendors the MIME type config from the mime-db list
22 # usage: script/vendor-mimes
33
4 require 'colorator'
45 require 'json'
56 require 'open-uri'
67
7 config = File.expand_path "../lib/jekyll/mime.types", __dir__
8 # ---- Helpers ----
89
9 # Create an array of vendored mimetype => [extensions]
10 mimes = {}
11 json = open('https://raw.githubusercontent.com/jshttp/mime-db/master/db.json').read
10 {
11 :info => :cyan,
12 :success => :green,
13 :error => :red,
14 }.each do |type, color|
15 define_method("log_#{type}") do |msg|
16 puts " #{msg}".send(color)
17 end
18 end
19
20 # ----
21
22 json = begin
23 log_info "Reading remote data.."
24 URI.open("https://raw.githubusercontent.com/jshttp/mime-db/master/db.json").read
25 rescue StandardError => e
26 log_error "Error reading remote data!"
27 log_error e.message
28 log_error "Aborting."
29 exit 1
30 end
31
32 log_info "Parsing remote data.."
1233 data = JSON.parse(json)
1334 data.reject! { |mime, meta| meta["extensions"].nil? || meta["extensions"].empty? }
35
36 log_info "Generating interim mime data-hashes.."
37 mimes = {}
38 charset_data = {}
1439 data.each do |mime, meta|
1540 # Normalize extensions and mime-types
1641 mime = mime.downcase.strip
2247 next if extensions.empty?
2348 mimes[mime] = [] if mimes[mime].nil?
2449 mimes[mime].concat extensions
50
51 # Extract mime-types with "charset" metadata
52 charset_data[mime] = meta["charset"] if meta.key?("charset")
53
54 # Assign `UTF-8` charset for mime-types under the `text` domain if not already assigned upstream
55 charset_data[mime] ||= "UTF-8" if mime.start_with?("text/")
2556 end
2657
58 log_info "Formatting primary hash and writing to file.."
2759 strlen = mimes.keys.max_by(&:length).length
2860 output = ""
2961 output << "# Woah there. Do not edit this file directly.\n"
3062 output << "# This file is generated automatically by script/vendor-mimes.\n\n"
3163 mimes = mimes.sort_by { |k,v| k }
32 output << mimes.map { |mime,extensions| "#{mime.ljust(strlen)} #{extensions.join(" ")}" }.join("\n")
64 output << mimes.map { |mime,extensions| "#{mime.ljust(strlen)} #{extensions.sort.join(" ")}" }.join("\n")
3365
66 config = File.expand_path "../lib/jekyll/mime.types", __dir__
3467 File.write(config, output)
68 log_info "Done! See: #{config.inspect.white}"
69
70 # --- Generate JSON file from charset_data ----
71 puts
72
73 log_info "Dumping mimetype-charset mapping as JSON.."
74 json_file = File.expand_path "../lib/jekyll/commands/serve/mime_types_charset.json", __dir__
75 File.write(json_file, JSON.pretty_generate(charset_data) + "\n")
76 log_success "and done! See: #{json_file.inspect.white}"
0 ---
1 render_with_liquid: false
2 ---
3 {% raw %}{% endraw %}
0 id,field_a
1 1,"foo"
2 2,"bar"
0 id field_a
1 1 "foo"
2 2 "bar"
0 title: Hello World
1 baseurl: "/test-theme"
2 include: ["_extras/banner.md"]
3 exclude:
4 - README.md
5 - CHANGELOG.md
6 - Rakefile
7 - test/**/*
8
9 # theme-specific settings
10 test_theme:
11 skin: aero # aero / chrome / dark / neon
12 date_format: "%b -d %Y" # any format supported by strftime
13 header_links: true # generate header links automatically
0 manufacturer: Mercedes
1 models:
2 - model: A-Klasse
3 price: 32,000.00
4 - model: B-Klasse
5 price: 35,000.00
0 name: Cheese Dairy
1 products:
2 - name: spread cheese
3 price: 1.2
4 - name: cheddar cheese
5 price: 4.5
0 foo: "Hello! I’m bar. What’s up so far?"
0 header: Testimonials
1 footer: Design by FTC
0 <section class="testimonials">
1 <h3>{{ site.data.i18n.testimonials.header }}</h3>
2 <!-- for testimonial in site.data.testimonial }} -->
3
4 <!-- endfor -->
5 <footer class="testimonials-footer">
6 {{ site.data.i18n.testimonials.footer }}
7 </footer>
8 </section>
00 ---
11 ---
2 @import "test-theme-{{ site.theme-color | default: "red" }}";
2 @import "test-theme-{{ site.theme-color | default: 'red' }}";
0 <!DOCTYPE html>
1 <html>
2 <head>
3 <meta charset="UTF-8">
4 <title>Skinny</title>
5 </head>
6 <body>
7 <h1>Hello World</h1>
8 {{ content }}
9 </body>
10 </html>
0 # frozen_string_literal: true
1
2 Gem::Specification.new do |s|
3 s.name = "test-theme-w-empty-data"
4 s.version = "0.1.0"
5 s.licenses = ["MIT"]
6 s.summary = "This is a theme with just one layout and an empty _data folder used to test Jekyll"
7 s.authors = ["Jekyll"]
8 s.files = ["lib/example.rb"]
9 s.homepage = "https://github.com/jekyll/jekyll"
10 end
0 <html xmlns="http://www.w3.org/1999/xhtml">Content of foo.xhtml</html>
2929 require "minitest/reporters"
3030 require "minitest/profile"
3131 require "rspec/mocks"
32 require_relative "../lib/jekyll.rb"
32 require_relative "../lib/jekyll"
3333
3434 Jekyll.logger = Logger.new(StringIO.new, :error)
35
36 unless jruby?
37 require "rdiscount"
38 require "redcarpet"
39 end
4035
4136 require "kramdown"
4237 require "shoulda"
5550 module Minitest::Assertions
5651 def assert_exist(filename, msg = nil)
5752 msg = message(msg) { "Expected '#{filename}' to exist" }
58 assert File.exist?(filename), msg
53 assert_path_exists(filename, msg)
5954 end
6055
6156 def refute_exist(filename, msg = nil)
6257 msg = message(msg) { "Expected '#{filename}' not to exist" }
63 refute File.exist?(filename), msg
58 refute_path_exists(filename, msg)
6459 end
6560 end
6661
8378
8479 def test_dir(*subdirs)
8580 root_dir("test", *subdirs)
81 end
82
83 def temp_dir(*subdirs)
84 if Utils::Platforms.vanilla_windows?
85 drive = Dir.pwd.sub(%r!^([^/]+).*!, '\1')
86 temp_root = File.join(drive, "tmp")
87 else
88 temp_root = "/tmp"
89 end
90
91 File.join(temp_root, *subdirs)
8692 end
8793 end
8894
98104 end
99105
100106 def mocks_expect(*args)
101 RSpec::Mocks::ExampleMethods::ExpectHost.instance_method(:expect)\
102 .bind(self).call(*args)
107 RSpec::Mocks::ExampleMethods::ExpectHost.instance_method(:expect).bind(self).call(*args)
103108 end
104109
105110 def before_setup
115120 end
116121
117122 def fixture_document(relative_path)
118 site = fixture_site({
123 site = fixture_site(
119124 "collections" => {
120125 "methods" => {
121126 "output" => true,
122127 },
123 },
124 })
128 }
129 )
125130 site.read
126131 matching_doc = site.collections["methods"].docs.find do |doc|
127132 doc.relative_path == relative_path
142147 end
143148
144149 def site_configuration(overrides = {})
145 full_overrides = build_configs(overrides, build_configs({
146 "destination" => dest_dir,
147 "incremental" => false,
148 }))
149 Configuration.from(full_overrides.merge({
150 "source" => source_dir,
151 }))
150 full_overrides = build_configs(overrides, build_configs(
151 "destination" => dest_dir,
152 "incremental" => false
153 ))
154 Configuration.from(full_overrides.merge(
155 "source" => source_dir
156 ))
152157 end
153158
154159 def clear_dest
159164 def directory_with_contents(path)
160165 FileUtils.rm_rf(path)
161166 FileUtils.mkdir(path)
162 File.open("#{path}/index.html", "w") { |f| f.write("I was previously generated.") }
167 File.write("#{path}/index.html", "I was previously generated.")
163168 end
164169
165170 def with_env(key, value)
200205 rescue Errno::EACCES
201206 skip "Permission denied for creating a symlink to #{target.inspect} " \
202207 "on this machine".magenta
203 rescue NotImplementedError => error
204 skip error.to_s.magenta
208 rescue NotImplementedError => e
209 skip e.to_s.magenta
205210 end
206211 end
207212
210215 end
211216
212217 module TestWEBrick
213
214218 module_function
215219
216220 def mount_server(&block)
218222
219223 begin
220224 server.mount("/", Jekyll::Commands::Serve::Servlet, document_root,
221 document_root_options)
225 document_root_options)
222226
223227 server.start
224228 addr = server.listeners[0].addr
239243 :ServerType => Thread,
240244 :Logger => WEBrick::Log.new(logger),
241245 :AccessLog => [[logger, ""]],
246 :MimeTypesCharset => Jekyll::Commands::Serve.send(:mime_types_charset),
242247 :JekyllOptions => {},
243248 }
244249 end
248253 end
249254
250255 def document_root_options
251 WEBrick::Config::FileHandler.merge({
256 WEBrick::Config::FileHandler.merge(
252257 :FancyIndexing => true,
253258 :NondisclosureName => [
254259 ".ht*", "~*",
255 ],
256 })
257 end
258 end
260 ]
261 )
262 end
263 end
0 foo: "Hello! I’m foo. And who are you?"
0 testimonials:
1 header: Kundenstimmen
2 # footer omitted by design
0 <span id='include-param'>{{include.param}}</span>
1
2 <ul id='param-list'>
3 {% for param in include %}
4 <li>{{param[0]}} = {{param[1]}}</li>
5 {% endfor %}
6 </ul>
0 ---
1 title: Ellipsis Path
2 ---
0 ---
1 layout: default
2 title: This file extension is skipped
3 category: test_post_reader
4 ---
5
6 `jekyll serve` used to crash if there's a post (document) with a wrong file extension.
7 This file is used in `./test/test_post_reader.rb` to verify that this doesn't happen anymore.
0 ---
1 title: What Am I?
2 ---
3
4 I am not a post.
5 Am I a document then..?
0 ---
1 title: Ellipsis Path
2 ---
3
4 Lorem ipsum dolor sit amet
0 ---
1 name: launcher
2 ---
3
4 `name` defined in front matter.
0 ---
1 ---
2
3 No `name` in front matter.
44
55 Jekyll is a simple, blog-aware, static site generator. It takes a template
66 directory containing raw text files in various formats, runs it through
7 [Markdown](http://daringfireball.net/projects/markdown/) (or
8 [Textile](http://redcloth.org/textile)) and
7 [Markdown](https://daringfireball.net/projects/markdown/) (or
8 [Textile](https://www.promptworks.com/textile)) and
99 [Liquid](https://help.shopify.com/themes/liquid/basics)
1010 converters, and spits out a complete, ready-to-publish static website suitable
1111 for serving with your favorite web server. Jekyll also happens to be the engine
0 ---
1 title: "Dive-In and Publish Already!"
2 lesson: 3
3 approx_time: 30 mins
4 ---
5
6 Jekyll converts Markdown documents to HTML by default. Don't know what's Markdown?
7 Read this [documentation](https://daringfireball.net/projects/markdown/)
8 While you're at it, might as well learn about [Kramdown](https://kramdown.gettalong.org/)
0 ---
1 title: "Extending with Plugins"
2 lesson: 5
3 approx_time: 1 min
4 ---
5
6 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.
7
8 Check this [documentation page](https://jekyllrb.com/docs/plugins/) dedicated to working with plugins.
0 ---
1 title: "Getting Started"
2 lesson: 1
3 approx_time: 10 mins
4 ---
5
6 The first thing you need is a working installation of Ruby. Install from [the official website](https://www.ruby-lang.org/en/documentation/installation/).
0 ---
1 title: "Graduation Day"
2 lesson: 6
3 approx_time: 10 mins
4 ---
5
6 Congratulations! You now know enough to start Jekylling!
7
8 Want to report a bug you found? Or give something back to the community?
9 Head over to the [Jekyll Repo](https://github.com/jekyll/jekyll) at GitHub
0 ---
1 title: "Let's Roll!"
2 lesson: 2
3 approx_time: 1 min
4 ---
5
6 Now that you have installed Ruby, Jekyll and Bundler, lets get Jekylling!
7 Enter the following in your terminal:
8
9 $ jekyll new my blog
10
11 Then preview your new project in your browser right away by entering the following and pointing your browser to `http://localhost:4000` :
12
13 $ bundle exec jekyll serve
14
15 Go ahead. Try it.
0 ---
1 title: "Tip of the Iceberg"
2 lesson: 4
3 ---
4
5 Now that you know some of the basics, learn more about working with [Jekyll](https://jekyllrb.com).
0 ---
1 ---
2
3 @import "{{ site.skin | default: 'grid' }}";
0 ---
1 title: Contact Information
2 ---
3
4 ## {{ page.title }}
5
6 In case of emergency, contact Mr. John Doe, 1234, Foo Road, Foo.
0 ---
1 title: Ellipsis Path
2 ---
99
1010 Jekyll::Utils::Ansi::COLORS.each_key do |color|
1111 should "respond_to? #{color}" do
12 assert @subject.respond_to?(color)
12 assert_respond_to(@subject, color)
1313 end
1414 end
1515
1616 should "be able to strip colors" do
17 assert_equal @subject.strip(@subject.yellow(@subject.red("hello"))), "hello"
17 assert_equal "hello", @subject.strip(@subject.yellow(@subject.red("hello")))
1818 end
1919
2020 should "be able to detect colors" do
6060 end
6161
6262 should "keep the file in the directory in keep_files" do
63 assert File.exist?(File.join(dest_dir(".git"), "index.html"))
63 assert_path_exists(File.join(dest_dir(".git"), "index.html"))
6464 end
6565
6666 should "delete the file in the directory not in keep_files" do
67 assert !File.exist?(File.join(dest_dir("username.github.io"), "index.html"))
67 refute_path_exists(File.join(dest_dir("username.github.io"), "index.html"))
6868 end
6969
7070 should "delete the directory not in keep_files" do
71 assert !File.exist?(dest_dir("username.github.io"))
71 refute_path_exists(dest_dir("username.github.io"))
7272 end
7373 end
7474
88 @site = fixture_site
99 @site.process
1010 @test_coffeescript_file = dest_dir("js/coffeescript.js")
11 @js_output = <<-JS
12 (function() {
13 $(function() {
14 var cube, cubes, list, num, square;
15 list = [1, 2, 3, 4, 5];
16 square = function(x) {
17 return x * x;
18 };
19 cube = function(x) {
20 return square(x) * x;
21 };
22 cubes = (function() {
23 var i, len, results;
24 results = [];
25 for (i = 0, len = list.length; i < len; i++) {
26 num = list[i];
27 results.push(math.cube(num));
28 }
29 return results;
30 })();
31 if (typeof elvis !== "undefined" && elvis !== null) {
32 return alert("I knew it!");
33 }
34 });
11 @js_output = <<~JS
12 (function() {
13 $(function() {
14 var cube, cubes, list, num, square;
15 list = [1, 2, 3, 4, 5];
16 square = function(x) {
17 return x * x;
18 };
19 cube = function(x) {
20 return square(x) * x;
21 };
22 cubes = (function() {
23 var i, len, results;
24 results = [];
25 for (i = 0, len = list.length; i < len; i++) {
26 num = list[i];
27 results.push(math.cube(num));
28 }
29 return results;
30 })();
31 if (typeof elvis !== "undefined" && elvis !== null) {
32 return alert("I knew it!");
33 }
34 });
3535
36 }).call(this);
37 JS
36 }).call(this);
37 JS
3838 end
3939
4040 should "write a JS file in place" do
88 end
99
1010 should "sanitize the label name" do
11 assert_equal @collection.label, "....etcpassword"
11 assert_equal "....etcpassword", @collection.label
1212 end
1313
1414 should "have a sanitized relative path name" do
15 assert_equal @collection.relative_directory, "_....etcpassword"
15 assert_equal "_....etcpassword", @collection.relative_directory
1616 end
1717
1818 should "have a sanitized full path" do
2626 end
2727
2828 should "sanitize the label name" do
29 assert_equal @collection.label, "methods"
29 assert_equal "methods", @collection.label
3030 end
3131
3232 should "have default URL template" do
33 assert_equal @collection.url_template, "/:collection/:path:output_ext"
33 assert_equal "/:collection/:path:output_ext", @collection.url_template
3434 end
3535
3636 should "contain no docs when initialized" do
3838 end
3939
4040 should "know its relative directory" do
41 assert_equal @collection.relative_directory, "_methods"
41 assert_equal "_methods", @collection.relative_directory
4242 end
4343
4444 should "know the full path to itself on the filesystem" do
4747
4848 context "when turned into Liquid" do
4949 should "have a label attribute" do
50 assert_equal @collection.to_liquid["label"], "methods"
50 assert_equal "methods", @collection.to_liquid["label"]
5151 end
5252
5353 should "have a docs attribute" do
54 assert_equal @collection.to_liquid["docs"], []
54 assert_equal [], @collection.to_liquid["docs"]
5555 end
5656
5757 should "have a files attribute" do
58 assert_equal @collection.to_liquid["files"], []
58 assert_equal [], @collection.to_liquid["files"]
5959 end
6060
6161 should "have a directory attribute" do
6363 end
6464
6565 should "have a relative_directory attribute" do
66 assert_equal @collection.to_liquid["relative_directory"], "_methods"
66 assert_equal "_methods", @collection.to_liquid["relative_directory"]
6767 end
6868
6969 should "have a output attribute" do
70 assert_equal @collection.to_liquid["output"], false
70 refute @collection.to_liquid["output"]
7171 end
7272 end
7373
7474 should "know whether it should be written or not" do
75 assert_equal @collection.write?, false
75 refute @collection.write?
7676 @collection.metadata["output"] = true
77 assert_equal @collection.write?, true
77 assert @collection.write?
7878 @collection.metadata.delete "output"
7979 end
8080 end
8686 end
8787
8888 should "contain only the default collections" do
89 refute_equal @site.collections, {}
89 expected = {}
90 refute_equal expected, @site.collections
9091 refute_nil @site.collections
9192 end
9293 end
9394
9495 context "a collection with permalink" do
9596 setup do
96 @site = fixture_site({
97 @site = fixture_site(
9798 "collections" => {
9899 "methods" => {
99100 "permalink" => "/awesome/:path/",
100101 },
101 },
102 })
102 }
103 )
103104 @site.process
104105 @collection = @site.collections["methods"]
105106 end
106107
107108 should "have custom URL template" do
108 assert_equal @collection.url_template, "/awesome/:path/"
109 assert_equal "/awesome/:path/", @collection.url_template
109110 end
110111 end
111112
112113 context "with a collection" do
113114 setup do
114 @site = fixture_site({
115 "collections" => ["methods"],
116 })
115 @site = fixture_site(
116 "collections" => ["methods"]
117 )
117118 @site.process
118119 @collection = @site.collections["methods"]
119120 end
128129 assert @site.collections["methods"].docs.is_a? Array
129130 @site.collections["methods"].docs.each do |doc|
130131 assert doc.is_a? Jekyll::Document
132 # rubocop:disable Style/WordArray
131133 assert_includes %w(
132134 _methods/configuration.md
133135 _methods/sanitized_path.md
138140 _methods/escape-+\ #%20[].md
139141 _methods/yaml_with_dots.md
140142 _methods/3940394-21-9393050-fifif1323-test.md
143 _methods/trailing-dots...md
141144 ), doc.relative_path
145 # rubocop:enable Style/WordArray
142146 end
143147 end
144148
153157 should "not include the underscored files in the list of docs" do
154158 refute_includes @collection.docs.map(&:relative_path), "_methods/_do_not_read_me.md"
155159 refute_includes @collection.docs.map(&:relative_path),
156 "_methods/site/_dont_include_me_either.md"
160 "_methods/site/_dont_include_me_either.md"
157161 end
158162 end
159163
160164 context "with a collection with metadata" do
161165 setup do
162 @site = fixture_site({
166 @site = fixture_site(
163167 "collections" => {
164168 "methods" => {
165169 "foo" => "bar",
166170 "baz" => "whoo",
167171 },
168 },
169 })
172 }
173 )
170174 @site.process
171175 @collection = @site.collections["methods"]
172176 end
173177
174178 should "extract the configuration collection information as metadata" do
175 assert_equal @collection.metadata, { "foo" => "bar", "baz" => "whoo" }
179 expected = { "foo" => "bar", "baz" => "whoo" }
180 assert_equal expected, @collection.metadata
181 end
182 end
183
184 context "with a collection with metadata to sort items by attribute" do
185 setup do
186 @site = fixture_site(
187 "collections" => {
188 "methods" => {
189 "output" => true,
190 },
191 "tutorials" => {
192 "output" => true,
193 "sort_by" => "lesson",
194 },
195 }
196 )
197 @site.process
198 @tutorials_collection = @site.collections["tutorials"]
199
200 @actual_array = @tutorials_collection.docs.map(&:relative_path)
201 end
202
203 should "sort documents in a collection with 'sort_by' metadata set to a " \
204 "FrontMatter key 'lesson'" do
205 default_tutorials_array = %w(
206 _tutorials/dive-in-and-publish-already.md
207 _tutorials/extending-with-plugins.md
208 _tutorials/getting-started.md
209 _tutorials/graduation-day.md
210 _tutorials/lets-roll.md
211 _tutorials/tip-of-the-iceberg.md
212 )
213 tutorials_sorted_by_lesson_array = %w(
214 _tutorials/getting-started.md
215 _tutorials/lets-roll.md
216 _tutorials/dive-in-and-publish-already.md
217 _tutorials/tip-of-the-iceberg.md
218 _tutorials/extending-with-plugins.md
219 _tutorials/graduation-day.md
220 )
221 refute_equal default_tutorials_array, @actual_array
222 assert_equal tutorials_sorted_by_lesson_array, @actual_array
223 end
224 end
225
226 context "with a collection with metadata to rearrange items" do
227 setup do
228 @site = fixture_site(
229 "collections" => {
230 "methods" => {
231 "output" => true,
232 },
233 "tutorials" => {
234 "output" => true,
235 "order" => [
236 "getting-started.md",
237 "lets-roll.md",
238 "dive-in-and-publish-already.md",
239 "tip-of-the-iceberg.md",
240 "graduation-day.md",
241 "extending-with-plugins.md",
242 ],
243 },
244 }
245 )
246 @site.process
247 @tutorials_collection = @site.collections["tutorials"]
248
249 @actual_array = @tutorials_collection.docs.map(&:relative_path)
250 end
251
252 should "sort documents in a collection in the order outlined in the config file" do
253 default_tutorials_array = %w(
254 _tutorials/dive-in-and-publish-already.md
255 _tutorials/extending-with-plugins.md
256 _tutorials/getting-started.md
257 _tutorials/graduation-day.md
258 _tutorials/lets-roll.md
259 _tutorials/tip-of-the-iceberg.md
260 )
261 tutorials_rearranged_in_config_array = %w(
262 _tutorials/getting-started.md
263 _tutorials/lets-roll.md
264 _tutorials/dive-in-and-publish-already.md
265 _tutorials/tip-of-the-iceberg.md
266 _tutorials/graduation-day.md
267 _tutorials/extending-with-plugins.md
268 )
269 refute_equal default_tutorials_array, @actual_array
270 assert_equal tutorials_rearranged_in_config_array, @actual_array
176271 end
177272 end
178273
179274 context "in safe mode" do
180275 setup do
181 @site = fixture_site({
276 @site = fixture_site(
182277 "collections" => ["methods"],
183 "safe" => true,
184 })
278 "safe" => true
279 )
185280 @site.process
186281 @collection = @site.collections["methods"]
187282 end
201296
202297 context "with dots in the filenames" do
203298 setup do
204 @site = fixture_site({
299 @site = fixture_site(
205300 "collections" => ["with.dots"],
206 "safe" => true,
207 })
301 "safe" => true
302 )
208303 @site.process
209304 @collection = @site.collections["with.dots"]
210305 end
230325
231326 context "a collection with included dotfiles" do
232327 setup do
233 @site = fixture_site({
328 @site = fixture_site(
234329 "collections" => {
235330 "methods" => {
236331 "permalink" => "/awesome/:path/",
237332 },
238333 },
239 "include" => %w(.htaccess .gitignore),
240 })
334 "include" => %w(.htaccess .gitignore)
335 )
241336 @site.process
242337 @collection = @site.collections["methods"]
243338 end
1414 context "when fatal error occurs" do
1515 should "exit with non-zero error code" do
1616 site = Object.new
17 def site.process; raise Jekyll::Errors::FatalException; end
17 def site.process
18 raise Jekyll::Errors::FatalException
19 end
1820 error = assert_raises(SystemExit) { Command.process_site(site) }
1921 refute_equal 0, error.status
2022 end
44 require "helper"
55 require "httpclient"
66 require "openssl"
7 require "thread"
87 require "tmpdir"
98
109 class TestCommandsServe < JekyllUnitTest
4342
4443 context "using LiveReload" do
4544 setup do
45 skip_if_windows "EventMachine support on Windows is limited"
46 skip("Refinements are not fully supported in JRuby") if jruby?
47
4648 @temp_dir = Dir.mktmpdir("jekyll_livereload_test")
4749 @destination = File.join(@temp_dir, "_site")
4850 Dir.mkdir(@destination) || flunk("Could not make directory #{@destination}")
7274 </html>
7375 HTML
7476
75 File.open(File.join(@destination, "hello.html"), "w") do |f|
76 f.write(simple_page)
77 end
77 File.write(File.join(@destination, "hello.html"), simple_page)
7878 allow(Jekyll::Site).to receive(:new).and_return(site)
7979 end
8080
9393 end
9494
9595 should "serve livereload.js over HTTP on the default LiveReload port" do
96 skip_if_windows "EventMachine support on Windows is limited"
9796 opts = serve(@standard_options)
9897 content = @client.get_content(
9998 "http://#{opts["host"]}:#{opts["livereload_port"]}/livereload.js"
102101 end
103102
104103 should "serve nothing else over HTTP on the default LiveReload port" do
105 skip_if_windows "EventMachine support on Windows is limited"
106104 opts = serve(@standard_options)
107105 res = @client.get("http://#{opts["host"]}:#{opts["livereload_port"]}/")
108106 assert_equal(400, res.status_code)
110108 end
111109
112110 should "insert the LiveReload script tags" do
113 skip_if_windows "EventMachine support on Windows is limited"
114111 opts = serve(@standard_options)
115112 content = @client.get_content(
116113 "http://#{opts["host"]}:#{opts["port"]}/#{opts["baseurl"]}/hello.html"
123120 end
124121
125122 should "apply the max and min delay options" do
126 skip_if_windows "EventMachine support on Windows is limited"
127123 opts = serve(@standard_options.merge(
128 "livereload_max_delay" => "1066",
129 "livereload_min_delay" => "3"
130 ))
124 "livereload_max_delay" => "1066",
125 "livereload_min_delay" => "3"
126 ))
131127 content = @client.get_content(
132128 "http://#{opts["host"]}:#{opts["port"]}/#{opts["baseurl"]}/hello.html"
133129 )
154150 end
155151
156152 should "label itself" do
157 assert_equal(
158 @merc.name, :serve
159 )
153 assert_equal :serve, @merc.name
160154 end
161155
162156 should "have aliases" do
190184 end
191185
192186 should "use user destinations" do
193 assert_equal "foo", custom_opts({ "destination" => "foo" })[
187 assert_equal "foo", custom_opts("destination" => "foo")[
194188 :DocumentRoot
195189 ]
196190 end
197191
198192 should "use user port" do
199193 # WHAT?!?!1 Over 9000? That's impossible.
200 assert_equal 9001, custom_opts({ "port" => 9001 })[
194 assert_equal 9001, custom_opts("port" => 9001)[
201195 :Port
202196 ]
203197 end
204198
205199 should "use empty directory index list when show_dir_listing is true" do
206200 opts = { "show_dir_listing" => true }
207 assert custom_opts(opts)[:DirectoryIndex].empty?
201 assert_empty custom_opts(opts)[:DirectoryIndex]
208202 end
209203
210204 should "keep config between build and serve" do
236230 expect(Jekyll::Commands::Serve).to receive(:start_up_webrick)
237231 end
238232 should "set the site url by default to `http://localhost:4000`" do
239 @merc.execute(:serve, { "watch" => false, "url" => "https://jekyllrb.com/" })
233 @merc.execute(:serve, "watch" => false, "url" => "https://jekyllrb.com/")
240234
241235 assert_equal 1, Jekyll.sites.count
242236 assert_equal "http://localhost:4000", Jekyll.sites.first.config["url"]
243237 end
244238
245239 should "take `host`, `port` and `ssl` into consideration if set" do
246 @merc.execute(:serve, {
247 "watch" => false,
248 "host" => "example.com",
249 "port" => "9999",
250 "url" => "https://jekyllrb.com/",
251 "ssl_cert" => "foo",
252 "ssl_key" => "bar",
253 })
240 @merc.execute(:serve,
241 "watch" => false,
242 "host" => "example.com",
243 "port" => "9999",
244 "url" => "https://jekyllrb.com/",
245 "ssl_cert" => "foo",
246 "ssl_key" => "bar")
254247
255248 assert_equal 1, Jekyll.sites.count
256249 assert_equal "https://example.com:9999", Jekyll.sites.first.config["url"]
261254 should "not update the site url" do
262255 expect(Jekyll).to receive(:env).and_return("production")
263256 expect(Jekyll::Commands::Serve).to receive(:start_up_webrick)
264 @merc.execute(:serve, { "watch" => false, "url" => "https://jekyllrb.com/" })
257 @merc.execute(:serve, "watch" => false, "url" => "https://jekyllrb.com/")
265258
266259 assert_equal 1, Jekyll.sites.count
267260 assert_equal "https://jekyllrb.com/", Jekyll.sites.first.config["url"]
270263
271264 context "verbose" do
272265 should "debug when verbose" do
273 assert_equal custom_opts({ "verbose" => true })[:Logger].level, 5
266 assert_equal 5, custom_opts("verbose" => true)[:Logger].level
274267 end
275268
276269 should "warn when not verbose" do
277 assert_equal custom_opts({})[:Logger].level, 3
270 assert_equal 3, custom_opts({})[:Logger].level
278271 end
279272 end
280273
281274 context "enabling SSL" do
282275 should "raise if enabling without key or cert" do
283276 assert_raises RuntimeError do
284 custom_opts({
285 "ssl_key" => "foo",
286 })
277 custom_opts(
278 "ssl_key" => "foo"
279 )
287280 end
288281
289282 assert_raises RuntimeError do
290 custom_opts({
291 "ssl_key" => "foo",
292 })
283 custom_opts(
284 "ssl_key" => "foo"
285 )
293286 end
294287 end
295288
298291 expect(OpenSSL::X509::Certificate).to receive(:new).and_return("c1")
299292 allow(File).to receive(:read).and_return("foo")
300293
301 result = custom_opts({
294 result = custom_opts(
302295 "ssl_cert" => "foo",
303296 "source" => "bar",
304297 "enable_ssl" => true,
305 "ssl_key" => "bar",
306 })
298 "ssl_key" => "bar"
299 )
307300
308301 assert result[:SSLEnable]
309 assert_equal result[:SSLPrivateKey], "c2"
310 assert_equal result[:SSLCertificate], "c1"
302 assert_equal "c2", result[:SSLPrivateKey]
303 assert_equal "c1", result[:SSLCertificate]
311304 end
312305 end
313306 end
316309 allow(Jekyll::Commands::Serve).to receive(:start_up_webrick)
317310
318311 expect(Jekyll).to receive(:configuration).once.and_call_original
319 @merc.execute(:serve, { "watch" => false })
312 @merc.execute(:serve, "watch" => false)
320313 end
321314 end
322315 end
3333 assert_equal("404", response.code)
3434 end
3535 end
36
37 should "find xhtml file" do
38 get("/bar/foo") do |response|
39 assert_equal("200", response.code)
40 assert_equal(
41 '<html xmlns="http://www.w3.org/1999/xhtml">Content of foo.xhtml</html>',
42 response.body.strip
43 )
44 end
45 end
3646 end
3747 end
1414 end
1515
1616 should "merge input over defaults" do
17 result = Configuration.from({ "source" => "blah" })
17 result = Configuration.from("source" => "blah")
1818 refute_equal result["source"], Configuration::DEFAULTS["source"]
19 assert_equal result["source"], "blah"
19 assert_equal "blah", result["source"]
2020 end
2121
2222 should "return a valid Configuration instance" do
23 assert_instance_of Configuration, Configuration.from({}).fix_common_issues
23 assert_instance_of Configuration, Configuration.from({})
2424 end
2525
2626 should "add default collections" do
2727 result = Configuration.from({})
28 assert_equal(
29 result["collections"],
30 {
31 "posts" => {
32 "output" => true,
33 "permalink" => "/:categories/:year/:month/:day/:title:output_ext",
34 },
35 }
36 )
28 expected = { "posts" => {
29 "output" => true,
30 "permalink" => "/:categories/:year/:month/:day/:title:output_ext",
31 } }
32 assert_equal expected, result["collections"]
3733 end
3834
3935 should "NOT backwards-compatibilize" do
4440 end
4541 end
4642
47 context "the defaults" do
48 should "exclude node_modules" do
49 assert_includes Configuration.from({})["exclude"], "node_modules"
50 end
51
52 should "exclude ruby vendor directories" do
53 exclude = Configuration.from({})["exclude"]
43 context "the effective site configuration" do
44 setup do
45 @config = Configuration.from(
46 "exclude" => %w(
47 README.md Licence
48 )
49 )
50 end
51
52 should "always exclude node_modules" do
53 assert_includes @config["exclude"], "node_modules"
54 end
55
56 should "always exclude Gemfile and related paths" do
57 exclude = @config["exclude"]
5458 assert_includes exclude, "Gemfile"
5559 assert_includes exclude, "Gemfile.lock"
60 assert_includes exclude, "gemfiles"
61 end
62
63 should "always exclude ruby vendor directories" do
64 exclude = @config["exclude"]
5665 assert_includes exclude, "vendor/bundle/"
5766 assert_includes exclude, "vendor/cache/"
5867 assert_includes exclude, "vendor/gems/"
5968 assert_includes exclude, "vendor/ruby/"
6069 end
70
71 should "always exclude default cache directories" do
72 exclude = @config["exclude"]
73 assert_includes exclude, ".sass-cache"
74 assert_includes exclude, ".jekyll-cache"
75 end
6176 end
6277
6378 context "#add_default_collections" do
6984 should "turn an array into a hash" do
7085 result = Configuration[{ "collections" => %w(methods) }].add_default_collections
7186 assert_instance_of Hash, result["collections"]
72 assert_equal(
73 result["collections"],
74 { "posts" => { "output" => true }, "methods" => {} }
75 )
87 expected = { "posts" => { "output" => true }, "methods" => {} }
88 assert_equal expected, result["collections"]
7689 end
7790
7891 should "only assign collections.posts.permalink if a permalink is specified" do
7992 result = Configuration[{ "permalink" => "pretty", "collections" => {} }]
8093 .add_default_collections
81 assert_equal(
82 result["collections"],
83 {
84 "posts" => {
85 "output" => true,
86 "permalink" => "/:categories/:year/:month/:day/:title/",
87 },
88 }
89 )
90
91 result = Configuration[{ "permalink" => nil, "collections" => {} }]
92 .add_default_collections
93 assert_equal result["collections"], { "posts" => { "output" => true } }
94
95 expected = {
96 "posts" => {
97 "output" => true,
98 "permalink" => "/:categories/:year/:month/:day/:title/",
99 },
100 }
101
102 assert_equal expected, result["collections"]
103
104 result = Configuration[{ "permalink" => nil, "collections" => {} }].add_default_collections
105 expected = { "posts" => { "output" => true } }
106
107 assert_equal expected, result["collections"]
94108 end
95109
96110 should "forces posts to output" do
97111 result = Configuration[{ "collections" => { "posts" => { "output" => false } } }]
98112 .add_default_collections
99 assert_equal result["collections"]["posts"]["output"], true
113 assert result["collections"]["posts"]["output"]
100114 end
101115 end
102116
109123 :include => [".htaccess"],
110124 :source => "./",
111125 }]
126
112127 @string_keys = Configuration[{
113128 "markdown" => "kramdown",
114129 "permalink" => "date",
117132 "source" => "./",
118133 }]
119134 end
135
120136 should "stringify symbol keys" do
121137 assert_equal @string_keys, @mixed_keys.stringify_keys
122138 end
139
123140 should "not mess with keys already strings" do
124141 assert_equal @string_keys, @string_keys.stringify_keys
125142 end
126143 end
144
127145 context "#config_files" do
128146 setup do
129147 @config = Configuration[{ "source" => source_dir }]
139157 assert @config.config_files(@one_config_file).is_a?(Array)
140158 assert @config.config_files(@multiple_files).is_a?(Array)
141159 end
160
142161 should "return the default config path if no config files are specified" do
143162 assert_equal [source_dir("_config.yml")], @config.config_files(@no_override)
144163 end
164
145165 should "return .yaml if it exists but .yml does not" do
146166 allow(File).to receive(:exist?).with(source_dir("_config.yml")).and_return(false)
147167 allow(File).to receive(:exist?).with(source_dir("_config.yaml")).and_return(true)
148168 assert_equal [source_dir("_config.yaml")], @config.config_files(@no_override)
149169 end
170
150171 should "return .yml if both .yml and .yaml exist" do
151172 allow(File).to receive(:exist?).with(source_dir("_config.yml")).and_return(true)
152173 assert_equal [source_dir("_config.yml")], @config.config_files(@no_override)
153174 end
175
176 should "return .toml if that exists" do
177 allow(File).to receive(:exist?).with(source_dir("_config.yml")).and_return(false)
178 allow(File).to receive(:exist?).with(source_dir("_config.yaml")).and_return(false)
179 allow(File).to receive(:exist?).with(source_dir("_config.toml")).and_return(true)
180 assert_equal [source_dir("_config.toml")], @config.config_files(@no_override)
181 end
182
183 should "return .yml if both .yml and .toml exist" do
184 allow(File).to receive(:exist?).with(source_dir("_config.yml")).and_return(true)
185 allow(File).to receive(:exist?).with(source_dir("_config.toml")).and_return(true)
186 assert_equal [source_dir("_config.yml")], @config.config_files(@no_override)
187 end
188
154189 should "return the config if given one config file" do
155190 assert_equal %w(config.yml), @config.config_files(@one_config_file)
156191 end
192
157193 should "return an array of the config files if given many config files" do
158194 assert_equal(
159195 %w(config/site.yml config/deploy.toml configuration.yml),
168204 end
169205
170206 should "not raise an error on empty files" do
171 allow(SafeYAML).to receive(:load_file).with("empty.yml").and_return(false)
207 allow(SafeYAML).to receive(:load_file).with(File.expand_path("empty.yml")).and_return(false)
172208 Jekyll.logger.log_level = :warn
173209 @config.read_config_file("empty.yml")
174210 Jekyll.logger.log_level = :info
181217 end
182218
183219 should "continue to read config files if one is empty" do
184 allow(SafeYAML).to receive(:load_file).with("empty.yml").and_return(false)
185 allow(SafeYAML)
186 .to receive(:load_file)
187 .with("not_empty.yml")
188 .and_return({ "foo" => "bar", "include" => "", "exclude" => "" })
220 allow(SafeYAML).to receive(:load_file).with(File.expand_path("empty.yml")).and_return(false)
221 allow(SafeYAML).to receive(:load_file).with(File.expand_path("not_empty.yml")).and_return(
222 "foo" => "bar"
223 )
189224 Jekyll.logger.log_level = :warn
190 read_config = @config.read_config_files(["empty.yml", "not_empty.yml"])
225 read_config = @config.read_config_files(%w(empty.yml not_empty.yml))
191226 Jekyll.logger.log_level = :info
192227 assert_equal "bar", read_config["foo"]
193228 end
194229 end
195 context "#backwards_compatibilize" do
230
231 context "#validate" do
196232 setup do
197233 @config = Configuration[{
198234 "auto" => true,
199235 "watch" => true,
200236 "server" => true,
201 "exclude" => "READ-ME.md, Gemfile,CONTRIBUTING.hello.markdown",
202 "include" => "STOP_THE_PRESSES.txt,.heloses, .git",
203237 "pygments" => true,
204238 "layouts" => true,
205239 "data_source" => true,
206240 "gems" => [],
207241 }]
208242 end
209 should "unset 'auto' and 'watch'" do
210 assert @config.key?("auto")
211 assert @config.key?("watch")
212 assert !@config.backwards_compatibilize.key?("auto")
213 assert !@config.backwards_compatibilize.key?("watch")
214 end
215 should "unset 'server'" do
216 assert @config.key?("server")
217 assert !@config.backwards_compatibilize.key?("server")
218 end
219 should "transform string exclude into an array" do
220 assert @config.key?("exclude")
221 assert @config.backwards_compatibilize.key?("exclude")
222 assert_equal(
223 @config.backwards_compatibilize["exclude"],
224 %w(READ-ME.md Gemfile CONTRIBUTING.hello.markdown)
225 )
226 end
227 should "transform string include into an array" do
228 assert @config.key?("include")
229 assert @config.backwards_compatibilize.key?("include")
230 assert_equal(
231 @config.backwards_compatibilize["include"],
232 %w(STOP_THE_PRESSES.txt .heloses .git)
233 )
234 end
235 should "set highlighter to pygments" do
236 assert @config.key?("pygments")
237 assert !@config.backwards_compatibilize.key?("pygments")
238 assert_equal @config.backwards_compatibilize["highlighter"], "pygments"
239 end
240 should "adjust directory names" do
241 assert @config.key?("layouts")
242 assert !@config.backwards_compatibilize.key?("layouts")
243 assert @config.backwards_compatibilize["layouts_dir"]
244 assert @config.key?("data_source")
245 assert !@config.backwards_compatibilize.key?("data_source")
246 assert @config.backwards_compatibilize["data_dir"]
247 end
243
244 should "raise an error if `exclude` key is a string" do
245 config = Configuration[{ "exclude" => "READ-ME.md, Gemfile,CONTRIBUTING.hello.markdown" }]
246 assert_raises(Jekyll::Errors::InvalidConfigurationError) { config.validate }
247 end
248
249 should "raise an error if `include` key is a string" do
250 config = Configuration[{ "include" => "STOP_THE_PRESSES.txt,.heloses, .git" }]
251 assert_raises(Jekyll::Errors::InvalidConfigurationError) { config.validate }
252 end
253
248254 should "raise an error if `plugins` key is a string" do
249255 config = Configuration[{ "plugins" => "_plugin" }]
250 assert_raises Jekyll::Errors::InvalidConfigurationError do
251 config.backwards_compatibilize
252 end
253 end
254 should "set the `gems` config to `plugins`" do
256 assert_raises(Jekyll::Errors::InvalidConfigurationError) { config.validate }
257 end
258
259 should "not rename configuration keys" do
260 assert @config.key?("layouts")
261 assert @config.validate.key?("layouts")
262 refute @config.validate.key?("layouts_dir")
263
264 assert @config.key?("data_source")
265 assert @config.validate.key?("data_source")
266 refute @config.validate.key?("data_dir")
267
255268 assert @config.key?("gems")
256 assert !@config.backwards_compatibilize["gems"]
257 assert @config.backwards_compatibilize["plugins"]
258 end
259 end
269 assert @config.validate.key?("gems")
270 refute @config.validate.key?("plugins")
271 end
272 end
273
260274 context "loading configuration" do
261275 setup do
262276 @path = source_dir("_config.yml")
300314 allow($stderr)
301315 .to receive(:puts)
302316 .with(Colorator.red(
303 "Fatal: ".rjust(20) + \
304 "The configuration file '#{@user_config}' could not be found."
305 ))
317 "Fatal: ".rjust(20) + \
318 "The configuration file '#{@user_config}' could not be found."
319 ))
306320 assert_raises LoadError do
307 Jekyll.configuration({ "config" => [@user_config] })
321 Jekyll.configuration("config" => [@user_config])
308322 end
309323 end
310324
313327 # as opposed to: assert_equal ':foo', SafeYAML.load(':foo')
314328 end
315329 end
330
316331 context "loading config from external file" do
317332 setup do
318333 @paths = {
333348 allow(SafeYAML)
334349 .to receive(:load_file)
335350 .with(@paths[:other])
336 .and_return({ "baseurl" => "http://example.com" })
351 .and_return("baseurl" => "http://example.com")
337352 allow($stdout).to receive(:puts).with("Configuration file: #{@paths[:other]}")
338353 assert_equal \
339 site_configuration({
354 site_configuration(
340355 "baseurl" => "http://example.com",
341 "config" => @paths[:other],
342 }),
343 Jekyll.configuration(test_config.merge({ "config" => @paths[:other] }))
356 "config" => @paths[:other]
357 ),
358 Jekyll.configuration(test_config.merge("config" => @paths[:other]))
344359 end
345360
346361 should "load different config if specified with symbol key" do
348363 allow(SafeYAML)
349364 .to receive(:load_file)
350365 .with(@paths[:other])
351 .and_return({ "baseurl" => "http://example.com" })
366 .and_return("baseurl" => "http://example.com")
352367 allow($stdout).to receive(:puts).with("Configuration file: #{@paths[:other]}")
353368 assert_equal \
354 site_configuration({
369 site_configuration(
355370 "baseurl" => "http://example.com",
356 "config" => @paths[:other],
357 }),
358 Jekyll.configuration(test_config.merge({ :config => @paths[:other] }))
371 "config" => @paths[:other]
372 ),
373 Jekyll.configuration(test_config.merge(:config => @paths[:other]))
359374 end
360375
361376 should "load default config if path passed is empty" do
362377 allow(SafeYAML).to receive(:load_file).with(@paths[:default]).and_return({})
363378 allow($stdout).to receive(:puts).with("Configuration file: #{@paths[:default]}")
364379 assert_equal \
365 site_configuration({ "config" => [@paths[:empty]] }),
366 Jekyll.configuration(test_config.merge({ "config" => [@paths[:empty]] }))
380 site_configuration("config" => [@paths[:empty]]),
381 Jekyll.configuration(test_config.merge("config" => [@paths[:empty]]))
367382 end
368383
369384 should "successfully load a TOML file" do
370385 Jekyll.logger.log_level = :warn
371386 assert_equal \
372 site_configuration({
387 site_configuration(
373388 "baseurl" => "/you-beautiful-blog-you",
374389 "title" => "My magnificent site, wut",
375 "config" => [@paths[:toml]],
376 }),
377 Jekyll.configuration(test_config.merge({ "config" => [@paths[:toml]] }))
390 "config" => [@paths[:toml]]
391 ),
392 Jekyll.configuration(test_config.merge("config" => [@paths[:toml]]))
378393 Jekyll.logger.log_level = :info
379394 end
380395
388403 allow($stdout).to receive(:puts).with("Configuration file: #{@paths[:other]}")
389404 allow($stdout).to receive(:puts).with("Configuration file: #{@paths[:toml]}")
390405 assert_equal(
391 site_configuration({
392 "config" => [@paths[:default], @paths[:other], @paths[:toml]],
393 }),
406 site_configuration(
407 "config" => [@paths[:default], @paths[:other], @paths[:toml]]
408 ),
394409 Jekyll.configuration(
395410 test_config.merge(
396 { "config" => [@paths[:default], @paths[:other], @paths[:toml]] }
411 "config" => [@paths[:default], @paths[:other], @paths[:toml]]
397412 )
398413 )
399414 )
403418 allow(SafeYAML)
404419 .to receive(:load_file)
405420 .with(@paths[:default])
406 .and_return({ "baseurl" => "http://example.dev" })
421 .and_return("baseurl" => "http://example.dev")
407422 allow(SafeYAML)
408423 .to receive(:load_file)
409424 .with(@paths[:other])
410 .and_return({ "baseurl" => "http://example.com" })
425 .and_return("baseurl" => "http://example.com")
411426 allow($stdout)
412427 .to receive(:puts)
413428 .with("Configuration file: #{@paths[:default]}")
415430 .to receive(:puts)
416431 .with("Configuration file: #{@paths[:other]}")
417432 assert_equal \
418 site_configuration({
433 site_configuration(
419434 "baseurl" => "http://example.com",
420 "config" => [@paths[:default], @paths[:other]],
421 }),
435 "config" => [@paths[:default], @paths[:other]]
436 ),
422437 Jekyll.configuration(
423 test_config.merge({ "config" => [@paths[:default], @paths[:other]] })
438 test_config.merge("config" => [@paths[:default], @paths[:other]])
424439 )
425440 end
426441 end
436451 conf = Configuration[default_configuration].tap do |c|
437452 c["collections"] = ["docs"]
438453 end
439 assert_equal conf.add_default_collections, conf.merge({
454 assert_equal conf.add_default_collections, conf.merge(
440455 "collections" => {
441456 "docs" => {},
442457 "posts" => {
443458 "output" => true,
444459 "permalink" => "/:categories/:year/:month/:day/:title:output_ext",
445460 },
446 },
447 })
461 }
462 )
448463 end
449464
450465 should "force collections.posts.output = true" do
451466 conf = Configuration[default_configuration].tap do |c|
452467 c["collections"] = { "posts" => { "output" => false } }
453468 end
454 assert_equal conf.add_default_collections, conf.merge({
469 assert_equal conf.add_default_collections, conf.merge(
455470 "collections" => {
456471 "posts" => {
457472 "output" => true,
458473 "permalink" => "/:categories/:year/:month/:day/:title:output_ext",
459474 },
460 },
461 })
475 }
476 )
462477 end
463478
464479 should "set collections.posts.permalink if it's not set" do
465480 conf = Configuration[default_configuration]
466 assert_equal conf.add_default_collections, conf.merge({
481 assert_equal conf.add_default_collections, conf.merge(
467482 "collections" => {
468483 "posts" => {
469484 "output" => true,
470485 "permalink" => "/:categories/:year/:month/:day/:title:output_ext",
471486 },
472 },
473 })
487 }
488 )
474489 end
475490
476491 should "leave collections.posts.permalink alone if it is set" do
480495 "posts" => { "permalink" => posts_permalink },
481496 }
482497 end
483 assert_equal conf.add_default_collections, conf.merge({
498 assert_equal conf.add_default_collections, conf.merge(
484499 "collections" => {
485500 "posts" => {
486501 "output" => true,
487502 "permalink" => posts_permalink,
488503 },
489 },
490 })
504 }
505 )
491506 end
492507 end
493508
503518 )
504519 )
505520 assert_equal(
506 config["folded_string"],
507 "This string of text will ignore newlines till the next key.\n"
508 )
521 "This string of text will ignore newlines till the next key.\n",
522 config["folded_string"]
523 )
524
509525 assert_equal(
510 config["clean_folded_string"],
511 "This string of text will ignore newlines till the next key."
526 "This string of text will ignore newlines till the next key.",
527 config["clean_folded_string"]
512528 )
513529 end
514530
77 setup do
88 @convertible = OpenStruct.new(
99 "site" => Site.new(Jekyll.configuration(
10 "source" => File.expand_path("fixtures", __dir__)
11 ))
10 "source" => File.expand_path("fixtures", __dir__)
11 ))
1212 )
1313 @convertible.extend Jekyll::Convertible
1414 @base = File.expand_path("fixtures", __dir__)
3131 assert_equal({}, ret)
3232 end
3333 assert_match(%r!YAML Exception!, out)
34 assert_match(%r!#{File.join(@base, name)}!, out)
34 assert_match(%r!#{Regexp.escape(File.join(@base, name))}!, out)
3535 end
3636
3737 should "raise for broken front matter with `strict_front_matter` set" do
4646 out = capture_stderr do
4747 @convertible.read_yaml(@base, "exploit_front_matter.erb")
4848 end
49 refute_match(%r!undefined class\/module DoesNotExist!, out)
49 refute_match(%r!undefined class/module DoesNotExist!, out)
5050 end
5151
5252 should "not parse if there is encoding error in file" do
5656 assert_equal({}, ret)
5757 end
5858 assert_match(%r!invalid byte sequence in UTF-8!, out)
59 assert_match(%r!#{File.join(@base, name)}!, out)
59 assert_match(%r!#{Regexp.escape(File.join(@base, name))}!, out)
6060 end
6161
6262 should "parse the front matter but show an error if permalink is empty" do
7272 end
7373 refute_match(%r!Invalid permalink!, out)
7474 end
75
76 should "not parse Liquid if disabled in front matter" do
77 name = "no_liquid.erb"
78 @convertible.read_yaml(@base, name)
79 ret = @convertible.content.strip
80 assert_equal("{% raw %}{% endraw %}", ret)
81 end
7582 end
7683 end
1313 )
1414 end
1515 end
16
17 context "with no csv options set" do
18 setup do
19 @reader = DataReader.new(fixture_site)
20 @parsed = [{ "id" => "1", "field_a" => "foo" }, { "id" => "2", "field_a" => "bar" }]
21 end
22
23 should "parse CSV normally" do
24 assert_equal @parsed, @reader.read_data_file(File.expand_path("fixtures/sample.csv", __dir__))
25 end
26
27 should "parse TSV normally" do
28 assert_equal @parsed, @reader.read_data_file(File.expand_path("fixtures/sample.tsv", __dir__))
29 end
30 end
31
32 context "with csv options set" do
33 setup do
34 reader_config = {
35 "csv_converters" => [:numeric],
36 "headers" => false,
37 }
38
39 @reader = DataReader.new(
40 fixture_site(
41 {
42 "csv_reader" => reader_config,
43 "tsv_reader" => reader_config,
44 }
45 )
46 )
47
48 @parsed = [%w(id field_a), [1, "foo"], [2, "bar"]]
49 end
50
51 should "parse CSV with options" do
52 assert_equal @parsed, @reader.read_data_file(File.expand_path("fixtures/sample.csv", __dir__))
53 end
54
55 should "parse TSV with options" do
56 assert_equal @parsed, @reader.read_data_file(File.expand_path("fixtures/sample.tsv", __dir__))
57 end
58 end
1659 end
99 end
1010
1111 should "return success on a valid site/page" do
12 @site = Site.new(Jekyll.configuration({
13 "source" => File.join(source_dir, "/_urls_differ_by_case_valid"),
14 "destination" => dest_dir,
15 }))
12 @site = Site.new(Jekyll.configuration(
13 "source" => File.join(source_dir, "/_urls_differ_by_case_valid"),
14 "destination" => dest_dir
15 ))
1616 @site.process
1717 output = capture_stderr do
1818 ret = Jekyll::Commands::Doctor.urls_only_differ_by_case(@site)
19 assert_equal false, ret
19 refute ret
2020 end
2121 assert_equal "", output
2222 end
2323
24 # rubocop:disable Layout/LineLength
2425 should "return warning for pages only differing by case" do
25 @site = Site.new(Jekyll.configuration({
26 "source" => File.join(source_dir, "/_urls_differ_by_case_invalid"),
27 "destination" => dest_dir,
28 }))
26 @site = Site.new(Jekyll.configuration(
27 "source" => File.join(source_dir, "/_urls_differ_by_case_invalid"),
28 "destination" => dest_dir
29 ))
2930 @site.process
3031 output = capture_stderr do
3132 ret = Jekyll::Commands::Doctor.urls_only_differ_by_case(@site)
32 assert_equal true, ret
33 assert ret
3334 end
34 assert_includes output, "Warning: The following URLs only differ by case. "\
35 "On a case-insensitive file system one of the URLs will be overwritten by the "\
36 "other: #{dest_dir}/about/index.html, #{dest_dir}/About/index.html"
35 assert_includes output, "Warning: The following URLs only differ by case. On a case-" \
36 "insensitive file system one of the URLs will be overwritten by the " \
37 "other: #{dest_dir}/about/index.html, #{dest_dir}/About/index.html"
3738 end
39 # rubocop:enable Layout/LineLength
3840 end
3941 end
99 def setup_encoded_document(filename)
1010 site = fixture_site("collections" => ["encodings"])
1111 site.process
12 Document.new(site.in_source_dir(File.join("_encodings", filename)), {
13 :site => site,
14 :collection => site.collections["encodings"],
15 }).tap(&:read)
12 Document.new(site.in_source_dir(File.join("_encodings", filename)),
13 :site => site,
14 :collection => site.collections["encodings"]).tap(&:read)
1615 end
1716
1817 def setup_document_with_dates(filename)
2019 site.process
2120 docs = nil
2221 with_env("TZ", "UTC") do
23 docs = Document.new(site.in_source_dir(File.join("_dates", filename)), {
24 :site => site,
25 :collection => site.collections["dates"],
26 }).tap(&:read)
22 docs = Document.new(site.in_source_dir(File.join("_dates", filename)),
23 :site => site,
24 :collection => site.collections["dates"]).tap(&:read)
2725 end
2826 docs
2927 end
3028
3129 context "a document in a collection" do
3230 setup do
33 @site = fixture_site({
34 "collections" => ["methods"],
35 })
31 @site = fixture_site(
32 "collections" => ["methods"]
33 )
3634 @site.process
3735 @document = @site.collections["methods"].docs.detect do |d|
3836 d.relative_path == "_methods/configuration.md"
4038 end
4139
4240 should "exist" do
43 assert !@document.nil?
41 refute_nil @document
4442 end
4543
4644 should "know its relative path" do
5957 assert_equal "configuration", @document.basename_without_ext
6058 end
6159
60 should "know its type" do
61 assert_equal :methods, @document.type
62 end
63
6264 should "know whether it's a YAML file" do
63 assert_equal false, @document.yaml_file?
65 refute @document.yaml_file?
6466 end
6567
6668 should "know its data" do
6769 assert_equal "Jekyll.configuration", @document.data["title"]
6870 assert_equal "foo.bar", @document.data["whatever"]
71 end
72
73 should "know its date" do
74 assert_nil @document.data["date"]
75 assert_equal Time.now.strftime("%Y/%m/%d"), @document.date.strftime("%Y/%m/%d")
76 assert_equal Time.now.strftime("%Y/%m/%d"), @document.to_liquid["date"].strftime("%Y/%m/%d")
6977 end
7078
7179 should "be jsonify-able" do
109117 assert_nil next_next_doc["previous"]["output"]
110118 end
111119
120 context "with the basename (without extension) ending with dot(s)" do
121 setup do
122 @site = fixture_site("collections" => ["methods"])
123 @site.process
124 @document = @site.collections["methods"].docs.detect do |d|
125 d.relative_path == "_methods/trailing-dots...md"
126 end
127 end
128
129 should "render into the proper url" do
130 assert_equal "/methods/trailing-dots.html", @document.url
131
132 trailing_dots_doc = @site.posts.docs.detect do |d|
133 d.relative_path == "_posts/2018-10-12-trailing-dots...markdown"
134 end
135 assert_equal "/2018/10/12/trailing-dots.html", trailing_dots_doc.url
136 end
137 end
138
112139 context "with YAML ending in three dots" do
113140 setup do
114 @site = fixture_site({ "collections" => ["methods"] })
141 @site = fixture_site("collections" => ["methods"])
115142 @site.process
116143 @document = @site.collections["methods"].docs.detect do |d|
117144 d.relative_path == "_methods/yaml_with_dots.md"
125152 end
126153
127154 should "output the collection name in the #to_liquid method" do
128 assert_equal @document.to_liquid["collection"], "methods"
155 assert_equal "methods", @document.to_liquid["collection"]
129156 end
130157
131158 should "output its relative path as path in Liquid" do
132 assert_equal @document.to_liquid["path"], "_methods/configuration.md"
159 assert_equal "_methods/configuration.md", @document.to_liquid["path"]
160 end
161
162 context "when rendered with Liquid" do
163 should "respect the front matter definition" do
164 site = fixture_site("collections" => ["roles"]).tap(&:process)
165 docs = site.collections["roles"].docs
166
167 # Ruby context: doc.basename is aliased as doc.to_liquid["name"] by default.
168
169 document = docs.detect { |d| d.relative_path == "_roles/unnamed.md" }
170 assert_equal "unnamed.md", document.basename
171 assert_equal "unnamed.md", document.to_liquid["name"]
172
173 document = docs.detect { |d| d.relative_path == "_roles/named.md" }
174 assert_equal "named.md", document.basename
175 assert_equal "launcher", document.to_liquid["name"]
176 end
133177 end
134178 end
135179
136180 context "a document as part of a collection with front matter defaults" do
137181 setup do
138 @site = fixture_site({
182 @site = fixture_site(
139183 "collections" => ["slides"],
140184 "defaults" => [{
141185 "scope" => { "path" => "", "type" => "slides" },
144188 "key" => "myval",
145189 },
146190 },
147 },],
148 })
149 @site.process
150 @document = @site.collections["slides"].docs.select { |d| d.is_a?(Document) }.first
191 }]
192 )
193 @site.process
194 @document = @site.collections["slides"].docs.find { |d| d.is_a?(Document) }
151195 end
152196
153197 should "know the front matter defaults" do
165209
166210 context "a document as part of a collection with overridden default values" do
167211 setup do
168 @site = fixture_site({
212 @site = fixture_site(
169213 "collections" => ["slides"],
170214 "defaults" => [{
171215 "scope" => { "path" => "", "type" => "slides" },
175219 "test2" => "default1",
176220 },
177221 },
178 },],
179 })
222 }]
223 )
180224 @site.process
181225 @document = @site.collections["slides"].docs[1]
182226 end
193237
194238 context "a document as part of a collection with valid path" do
195239 setup do
196 @site = fixture_site({
240 @site = fixture_site(
197241 "collections" => ["slides"],
198242 "defaults" => [{
199243 "scope" => { "path" => "_slides", "type" => "slides" },
202246 "key" => "value123",
203247 },
204248 },
205 },],
206 })
249 }]
250 )
207251 @site.process
208252 @document = @site.collections["slides"].docs.first
209253 end
217261
218262 context "a document as part of a collection with invalid path" do
219263 setup do
220 @site = fixture_site({
264 @site = fixture_site(
221265 "collections" => ["slides"],
222266 "defaults" => [{
223267 "scope" => { "path" => "somepath", "type" => "slides" },
226270 "key" => "myval",
227271 },
228272 },
229 },],
230 })
273 }]
274 )
231275 @site.process
232276 @document = @site.collections["slides"].docs.first
233277 end
241285
242286 context "a document in a collection with a custom permalink" do
243287 setup do
244 @site = fixture_site({
245 "collections" => ["slides"],
246 })
288 @site = fixture_site(
289 "collections" => ["slides"]
290 )
247291 @site.process
248292 @document = @site.collections["slides"].docs[2]
249293 @dest_file = dest_dir("slide/3/index.html")
260304
261305 context "a document in a collection with custom filename permalinks" do
262306 setup do
263 @site = fixture_site({
307 @site = fixture_site(
264308 "collections" => {
265309 "slides" => {
266310 "output" => true,
267311 "permalink" => "/slides/test/:name",
268312 },
269313 },
270 "permalink" => "pretty",
271 })
314 "permalink" => "pretty"
315 )
272316 @site.process
273317 @document = @site.collections["slides"].docs[0]
274318 @dest_file = dest_dir("slides/test/example-slide-1.html")
289333
290334 context "a document in a collection with pretty permalink style" do
291335 setup do
292 @site = fixture_site({
336 @site = fixture_site(
293337 "collections" => {
294338 "slides" => {
295339 "output" => true,
296340 },
297 },
298 })
341 }
342 )
299343 @site.permalink_style = :pretty
300344 @site.process
301345 @document = @site.collections["slides"].docs[0]
313357
314358 context "a document in a collection with cased file name" do
315359 setup do
316 @site = fixture_site({
360 @site = fixture_site(
317361 "collections" => {
318362 "slides" => {
319363 "output" => true,
320364 },
321 },
322 })
365 }
366 )
323367 @site.permalink_style = :pretty
324368 @site.process
325369 @document = @site.collections["slides"].docs[7]
333377
334378 context "a document in a collection with cased file name" do
335379 setup do
336 @site = fixture_site({
380 @site = fixture_site(
337381 "collections" => {
338382 "slides" => {
339383 "output" => true,
340384 },
341 },
342 })
385 }
386 )
343387 @site.process
344388 @document = @site.collections["slides"].docs[6]
345389 @dest_file = dest_dir("slides/example-slide-7.php")
364408
365409 context "documents in a collection with custom title permalinks" do
366410 setup do
367 @site = fixture_site({
411 @site = fixture_site(
368412 "collections" => {
369413 "slides" => {
370414 "output" => true,
371415 "permalink" => "/slides/:title",
372416 },
373 },
374 })
417 }
418 )
375419 @site.process
376420 @document = @site.collections["slides"].docs[3]
377421 @document_without_slug = @site.collections["slides"].docs[4]
409453
410454 context "document with a permalink with dots & a trailing slash" do
411455 setup do
412 @site = fixture_site({ "collections" => {
456 @site = fixture_site("collections" => {
413457 "with.dots" => { "output" => true },
414 }, })
458 })
415459 @site.process
416460 @document = @site.collections["with.dots"].docs.last
417461 @dest_file = dest_dir("with.dots", "permalink.with.slash.tho", "index.html")
432476
433477 context "documents in a collection" do
434478 setup do
435 @site = fixture_site({
479 @site = fixture_site(
436480 "collections" => {
437481 "slides" => {
438482 "output" => true,
439483 },
440 },
441 })
484 }
485 )
442486 @site.process
443487 @files = @site.collections["slides"].docs
444488 end
464508
465509 context "a static file in a collection" do
466510 setup do
467 @site = fixture_site({
511 @site = fixture_site(
468512 "collections" => {
469513 "slides" => {
470514 "output" => true,
471515 },
472 },
473 })
516 }
517 )
474518 @site.process
475519 @document = @site.collections["slides"].files.find do |doc|
476520 doc.relative_path == "_slides/octojekyll.png"
479523 end
480524
481525 should "be a static file" do
482 assert_equal true, @document.is_a?(StaticFile)
526 assert @document.is_a?(StaticFile)
483527 end
484528
485529 should "be set to write" do
487531 end
488532
489533 should "be in the list of docs_to_write" do
490 assert @site.docs_to_write.include?(@document)
534 assert_includes @site.docs_to_write, @document
491535 end
492536
493537 should "be output in the correct place" do
497541
498542 context "a document in a collection with non-alphabetic file name" do
499543 setup do
500 @site = fixture_site({
544 @site = fixture_site(
501545 "collections" => {
502546 "methods" => {
503547 "output" => true,
504548 },
505 },
506 })
549 }
550 )
507551 @site.process
508552 @document = @site.collections["methods"].docs.find do |doc|
509553 doc.relative_path == "_methods/escape-+ #%20[].md"
520564 end
521565
522566 should "be output in the correct place" do
523 assert_equal true, File.file?(@dest_file)
567 assert File.file?(@dest_file)
524568 end
525569 end
526570
527571 context "a document in a collection with dash-separated numeric file name" do
528572 setup do
529 @site = fixture_site({
573 @site = fixture_site(
530574 "collections" => {
531575 "methods" => {
532576 "output" => true,
533577 },
534 },
535 })
578 }
579 )
536580 @site.process
537581 @document = @site.collections["methods"].docs.find do |doc|
538582 doc.relative_path == "_methods/3940394-21-9393050-fifif1323-test.md"
549593 end
550594
551595 should "be output in the correct place" do
552 assert_equal true, File.file?(@dest_file)
596 assert File.file?(@dest_file)
553597 end
554598 end
555599
581625 should "have the expected date" do
582626 assert_equal "2015/09/30", @document.data["date"].strftime("%Y/%m/%d")
583627 end
628
629 should "return the expected date via Liquid" do
630 assert_equal "2015/09/30", @document.to_liquid["date"].strftime("%Y/%m/%d")
631 end
584632 end
585633
586634 context "a document with a date with time but without timezone" do
591639 should "have the expected date" do
592640 assert_equal "2015/10/01", @document.data["date"].strftime("%Y/%m/%d")
593641 end
642
643 should "return the expected date via Liquid" do
644 assert_equal "2015/10/01", @document.to_liquid["date"].strftime("%Y/%m/%d")
645 end
594646 end
595647
596648 context "a document with a date without time" do
601653 should "have the expected date" do
602654 assert_equal "2015/10/01", @document.data["date"].strftime("%Y/%m/%d")
603655 end
656
657 should "return the expected date via Liquid" do
658 assert_equal "2015/10/01", @document.to_liquid["date"].strftime("%Y/%m/%d")
659 end
604660 end
605661 end
33
44 class DropFixture < Jekyll::Drops::Drop
55 mutable true
6
7 attr_accessor :lipsum
68
79 def foo
810 "bar"
1618 class TestDrop < JekyllUnitTest
1719 context "Drops" do
1820 setup do
19 @site = fixture_site({
20 "collections" => ["methods"],
21 })
21 @site = fixture_site(
22 "collections" => ["methods"]
23 )
2224 @site.process
2325 @document = @site.collections["methods"].docs.detect do |d|
2426 d.relative_path == "_methods/configuration.md"
3739
3840 should "return values for #invoke_drop" do
3941 assert_equal "bar", @drop.invoke_drop("foo")
42 end
43
44 should "return array of strings for .getter_methods" do
45 assert(@drop.class.getter_method_names.all? { |entry| entry.is_a?(String) })
46 end
47
48 should "return array of only getter method name strings for .getter_methods" do
49 [:lipsum, :lipsum=].each { |id| assert_includes(@drop.class.instance_methods, id) }
50
51 assert_includes @drop.class.getter_method_names, "lipsum"
52 refute_includes @drop.class.getter_method_names, "lipsum="
53 end
54
55 should "not munge results for another Jekyll::Drops::Drop subclass" do
56 fixture_ids = [:lipsum, :lipsum=, :foo]
57 fixture_getter_names = %w(lipsum foo)
58 fixture_ids.each { |id| assert_includes(@drop.class.instance_methods, id) }
59
60 fixture_getter_names.each do |name|
61 assert_includes @drop.class.getter_method_names, name
62 refute_includes @document_drop.class.getter_method_names, name
63 end
64 end
65
66 should "return only getter method names for #content_methods" do
67 drop_base_class_method_names = Jekyll::Drops::Drop.instance_methods.map(&:to_s)
68 sample_method_names = ["lipsum=", "fallback_data", "collapse_document"]
69
70 (sample_method_names + drop_base_class_method_names).each do |entry|
71 refute_includes @drop.content_methods, entry
72 end
73
74 assert_equal %w(foo lipsum), @drop.content_methods.sort
4075 end
4176
4277 context "mutations" do
68103 end
69104
70105 should "fetch default boolean value correctly" do
71 assert_equal false, @document_drop.fetch("bar", false)
106 refute @document_drop.fetch("bar", false)
72107 end
73108
74109 should "fetch default value from block if key is not found" do
99
1010 should "filter entries" do
1111 ent1 = %w(foo.markdown bar.markdown baz.markdown #baz.markdown#
12 .baz.markdow foo.markdown~ .htaccess _posts _pages ~$benbalter.docx)
12 .baz.markdown foo.markdown~ .htaccess _posts _pages ~$benbalter.docx)
1313
1414 entries = EntryFilter.new(@site).filter(ent1)
1515 assert_equal %w(foo.markdown bar.markdown baz.markdown .htaccess), entries
6363 assert_equal files, @site.reader.filter_entries(files)
6464 end
6565
66 should "not exclude explicitly included entry" do
67 entries = %w(README TODO css .htaccess _movies/.)
68 excludes = %w(README TODO css)
69 includes = %w(README .htaccess)
70 @site.exclude = excludes
71 @site.include = includes
72 filtered_entries = EntryFilter.new(@site).filter(entries)
73 assert_equal %w(README .htaccess), filtered_entries
74 end
75
6676 should "keep safe symlink entries when safe mode enabled" do
6777 allow(File).to receive(:symlink?).with("symlink.js").and_return(true)
6878 files = %w(symlink.js)
8191 assert_equal %w(), entries
8292 end
8393
84 # rubocop:disable Performance/FixedSize
8594 should "include only safe symlinks in safe mode" do
8695 # no support for symlinks on Windows
8796 skip_if_windows "Jekyll does not currently support symlinks on Windows."
92101 assert_equal %w(main.scss symlinked-file).length, site.pages.length
93102 refute_equal [], site.static_files
94103 end
95 # rubocop:enable Performance/FixedSize
96104
97105 should "include symlinks in unsafe mode" do
98106 # no support for symlinks on Windows
110118 site = fixture_site("safe" => true, "include" => ["symlinked-file-outside-source"])
111119 site.reader.read_directories("symlink-test")
112120
113 # rubocop:disable Performance/FixedSize
114121 assert_equal %w(main.scss symlinked-file).length, site.pages.length
115122 refute_includes site.static_files.map(&:name), "symlinked-file-outside-source"
116 # rubocop:enable Performance/FixedSize
117123 end
118124 end
119125
124130 end
125131
126132 should "return false with no glob patterns" do
127 assert !@filter.glob_include?([], "a.txt")
133 refute @filter.glob_include?([], "a.txt")
128134 end
129135
130136 should "return false with all not match path" do
131137 data = ["a*", "b?"]
132 assert !@filter.glob_include?(data, "ca.txt")
133 assert !@filter.glob_include?(data, "ba.txt")
138 refute @filter.glob_include?(data, "ca.txt")
139 refute @filter.glob_include?(data, "ba.txt")
134140 end
135141
136142 should "return true with match path" do
146152 assert @filter.glob_include?(data, "/vendor/bundle")
147153 assert @filter.glob_include?(data, "vendor/bundle")
148154 end
155
156 should "match even if there is no trailing slash" do
157 data = ["/vendor/bundle/", "vendor/ruby"]
158 assert @filter.glob_include?(data, "vendor/bundle/jekyll/lib/page.rb")
159 assert @filter.glob_include?(data, "/vendor/ruby/lib/set.rb")
160 end
161
162 should "match directory only if there is trailing slash" do
163 data = ["_glob_include_test/_is_dir/", "_glob_include_test/_not_dir/"]
164 assert @filter.glob_include?(data, "_glob_include_test/_is_dir")
165 assert @filter.glob_include?(data, "_glob_include_test/_is_dir/include_me.txt")
166 refute @filter.glob_include?(data, "_glob_include_test/_not_dir")
167 end
149168 end
150169 end
33
44 class TestExcerpt < JekyllUnitTest
55 def setup_post(file)
6 Document.new(@site.in_source_dir(File.join("_posts", file)), {
7 :site => @site,
8 :collection => @site.posts,
9 }).tap(&:read)
6 Document.new(@site.in_source_dir(File.join("_posts", file)),
7 :site => @site,
8 :collection => @site.posts).tap(&:read)
109 end
1110
1211 def do_render(document)
4241 end
4342
4443 should "return true only if an excerpt output contains a specified string" do
45 assert @excerpt.include?("fake output")
46 refute @excerpt.include?("real output")
44 assert_includes @excerpt, "fake output"
45 refute_includes @excerpt, "real output"
4746 end
4847 end
4948
5655 end
5756 end
5857
58 context "#type" do
59 should "return the post's type" do
60 assert_equal @excerpt.type, @post.type
61 end
62 should "return a symbol" do
63 assert_same @excerpt.type.class, Symbol
64 end
65 end
66
5967 context "#to_s" do
6068 should "return rendered output" do
6169 assert_equal @excerpt.output, @excerpt.to_s
6977
7078 context "#inspect" do
7179 should "contain the excerpt id as a shorthand string identifier" do
72 assert_equal @excerpt.inspect, "<Excerpt: #{@excerpt.id}>"
80 assert_equal @excerpt.inspect, "<#{@excerpt.class} id=#{@excerpt.id}>"
7381 end
7482
7583 should "return a string" do
8088 context "#relative_path" do
8189 should "return its document's relative path with '/#excerpt' appended" do
8290 assert_equal "#{@excerpt.doc.relative_path}/#excerpt",
83 @excerpt.relative_path
91 @excerpt.relative_path
8492 assert_equal "_posts/2013-07-22-post-excerpt-with-layout.markdown/#excerpt",
85 @excerpt.relative_path
93 @excerpt.relative_path
8694 end
8795 end
8896
102110 context "#content" do
103111 context "before render" do
104112 should "be the first paragraph of the page" do
105 expected = "First paragraph with [link ref][link].\n\n[link]: "\
106 "https://jekyllrb.com/"
113 expected = "First paragraph with [link ref][link].\n\n[link]: https://jekyllrb.com/"
107114 assert_equal expected, @excerpt.content
108115 end
109116
110117 should "contain any refs at the bottom of the page" do
111 assert @excerpt.content.include?("[link]: https://jekyllrb.com/")
118 assert_includes @excerpt.content, "[link]: https://jekyllrb.com/"
112119 end
113120 end
114121
120127 end
121128
122129 should "be the first paragraph of the page" do
123 expected = "<p>First paragraph with <a href=\"https://jekyllrb.com/\">link "\
130 expected = "<p>First paragraph with <a href=\"https://jekyllrb.com/\">link " \
124131 "ref</a>.</p>\n\n"
125132 assert_equal expected, @extracted_excerpt.output
126133 end
127134
128135 should "link properly" do
129 assert @extracted_excerpt.content.include?("https://jekyllrb.com/")
136 assert_includes @extracted_excerpt.content, "https://jekyllrb.com/"
130137 end
131138 end
132139
137144 end
138145
139146 should "contain all refs at the bottom of the page" do
140 (0..3).each do |i|
147 4.times do |i|
141148 assert_match "[link_#{i}]: www.example.com/#{i}", @excerpt.content
142149 end
143150 end
150157 @rendered_post = @post.dup
151158 do_render(@rendered_post)
152159 output = @rendered_post.data["excerpt"].output
153 (0..3).each do |i|
160 4.times do |i|
154161 assert_includes output, "<a href=\"www.example.com/#{i}\">"
155162 end
156163 end
167174 end
168175
169176 should "be generated" do
170 assert_equal true, @excerpt.is_a?(Jekyll::Excerpt)
177 assert @excerpt.is_a?(Jekyll::Excerpt)
171178 end
172179
173180 context "#content" do
195202 should "be appended to as necessary and generated" do
196203 assert_includes @excerpt.content, "{% endraw %}"
197204 assert_includes @excerpt.content, "{% endhighlight %}"
198 assert_equal true, @excerpt.is_a?(Jekyll::Excerpt)
205 assert @excerpt.is_a?(Jekyll::Excerpt)
199206 end
200207 end
201208
219226 assert_includes @excerpt.content, "{%\n endhighlight\n%}"
220227 refute_includes @excerpt.content, "{%\n endraw\n%}\n\n{% endraw %}"
221228 refute_includes @excerpt.content, "{%\n endhighlight\n%}\n\n{% endhighlight %}"
222 assert_equal true, @excerpt.is_a?(Jekyll::Excerpt)
229 assert @excerpt.is_a?(Jekyll::Excerpt)
223230 end
224231 end
225232
237244 should "be appended to as necessary and generated" do
238245 assert_includes @excerpt.content, "{% endfor %}"
239246 refute_includes @excerpt.content, "{% endfor %}\n\n{% endfor %}"
240 assert_equal true, @excerpt.is_a?(Jekyll::Excerpt)
247 assert @excerpt.is_a?(Jekyll::Excerpt)
241248 end
242249 end
243250
255262 should "not be appended to but generated as is" do
256263 assert_includes @excerpt.content, "{%- endfor -%}"
257264 refute_includes @excerpt.content, "{% endfor %}\n\n{% endfor %}"
258 assert_equal true, @excerpt.is_a?(Jekyll::Excerpt)
265 assert @excerpt.is_a?(Jekyll::Excerpt)
259266 end
260267 end
261268
271278
272279 should "not be appended to but generated as is" do
273280 assert_includes @excerpt.content, "{{- xyzzy -}}"
274 assert_equal true, @excerpt.is_a?(Jekyll::Excerpt)
281 assert @excerpt.is_a?(Jekyll::Excerpt)
275282 end
276283 end
277284
283290 @excerpt = @post.data["excerpt"]
284291
285292 assert_includes @post.content.split("\n\n")[0].strip, "{% continue %}"
286 assert_equal true, Jekyll::DoNothingBlock.ancestors.include?(Liquid::Block)
287 assert_equal false, Jekyll::DoNothingOther.ancestors.include?(Liquid::Block)
293 assert_includes Jekyll::DoNothingBlock.ancestors, Liquid::Block
294 refute_includes Jekyll::DoNothingOther.ancestors, Liquid::Block
288295 assert_match "Jekyll::DoNothingBlock", Liquid::Template.tags["do_nothing"].name
289296 assert_match "Jekyll::DoNothingOther", Liquid::Template.tags["do_nothing_other"].name
290297 end
296303 assert_includes @excerpt.content, "{% endunless %}"
297304 assert_includes @excerpt.content, "{% enddo_nothing %}"
298305 refute_includes @excerpt.content, "{% enddo_nothing_other %}"
299 assert_equal true, @excerpt.is_a?(Jekyll::Excerpt)
306 assert @excerpt.is_a?(Jekyll::Excerpt)
300307 end
301308 end
302309 end
2727
2828 should "inherit the layout for the drop but not the excerpt" do
2929 assert_nil @excerpt.data["layout"]
30 assert_equal @excerpt_drop["layout"], @doc_drop["layout"]
30 assert_equal @doc_drop["layout"], @excerpt_drop["layout"]
3131 end
3232
3333 should "be inspectable" do
3535 end
3636
3737 should "inherit values from the document" do
38 assert_equal @excerpt_drop.keys.sort, @doc_drop.keys.sort
38 assert_equal @doc_drop.keys.sort, @excerpt_drop.keys.sort
3939 end
4040 end
4141 end
88
99 def initialize(opts = {})
1010 @site = Jekyll::Site.new(opts.merge("skip_config_files" => true))
11 @context = Liquid::Context.new(@site.site_payload, {}, { :site => @site })
11 @context = Liquid::Context.new(@site.site_payload, {}, :site => @site)
1212 end
1313 end
1414
3636 context "filters" do
3737 setup do
3838 @sample_time = Time.utc(2013, 3, 27, 11, 22, 33)
39 @filter = make_filter_mock({
39 @timezone_before_test = ENV["TZ"]
40 @filter = make_filter_mock(
4041 "timezone" => "UTC",
4142 "url" => "http://example.com",
4243 "baseurl" => "/base",
43 "dont_show_posts_before" => @sample_time,
44 })
44 "dont_show_posts_before" => @sample_time
45 )
4546 @sample_date = Date.parse("2013-03-02")
4647 @time_as_string = "September 11, 2001 12:46:30 -0000"
4748 @time_as_numeric = 1_399_680_607
4849 @integer_as_string = "142857"
4950 @array_of_objects = [
51 { "color" => "teal", "size" => "large" },
5052 { "color" => "red", "size" => "large" },
5153 { "color" => "red", "size" => "medium" },
5254 { "color" => "blue", "size" => "medium" },
5355 ]
56 end
57
58 teardown do
59 ENV["TZ"] = @timezone_before_test
5460 end
5561
5662 should "markdownify with simple string" do
8793 end
8894
8995 should "escapes special characters when configured to do so" do
90 kramdown = make_filter_mock({ :kramdown => { :entity_output => :symbolic } })
96 kramdown = make_filter_mock(:kramdown => { :entity_output => :symbolic })
9197 assert_equal(
9298 "&ldquo;This filter&rsquo;s test&hellip;&rdquo;",
9399 kramdown.smartify(%q{"This filter's test..."})
136142
137143 should "sassify with simple string" do
138144 assert_equal(
139 "p {\n color: #123456; }\n",
140 @filter.sassify("$blue:#123456\np\n color: $blue")
145 "p { color: #123456; }\n",
146 @filter.sassify(<<~SASS)
147 $blue: #123456
148 p
149 color: $blue
150 SASS
141151 )
142152 end
143153
144154 should "scssify with simple string" do
145155 assert_equal(
146 "p {\n color: #123456; }\n",
156 "p { color: #123456; }\n",
147157 @filter.scssify("$blue:#123456; p{color: $blue}")
148158 )
149159 end
405415
406416 should "ensure the leading slash for the baseurl" do
407417 page_url = "about/my_favorite_page/"
408 filter = make_filter_mock({
418 filter = make_filter_mock(
409419 "url" => "http://example.com",
410 "baseurl" => "base",
411 })
420 "baseurl" => "base"
421 )
412422 assert_equal "http://example.com/base/#{page_url}", filter.absolute_url(page_url)
413423 end
414424
415425 should "be ok with a blank but present 'url'" do
416426 page_url = "about/my_favorite_page/"
417 filter = make_filter_mock({
427 filter = make_filter_mock(
418428 "url" => "",
419 "baseurl" => "base",
420 })
429 "baseurl" => "base"
430 )
421431 assert_equal "/base/#{page_url}", filter.absolute_url(page_url)
422432 end
423433
424434 should "be ok with a nil 'url'" do
425435 page_url = "about/my_favorite_page/"
426 filter = make_filter_mock({
436 filter = make_filter_mock(
427437 "url" => nil,
428 "baseurl" => "base",
429 })
438 "baseurl" => "base"
439 )
430440 assert_equal "/base/#{page_url}", filter.absolute_url(page_url)
431441 end
432442
433443 should "be ok with a nil 'baseurl'" do
434444 page_url = "about/my_favorite_page/"
435 filter = make_filter_mock({
445 filter = make_filter_mock(
436446 "url" => "http://example.com",
437 "baseurl" => nil,
438 })
447 "baseurl" => nil
448 )
439449 assert_equal "http://example.com/#{page_url}", filter.absolute_url(page_url)
440450 end
441451
442452 should "not prepend a forward slash if input is empty" do
443453 page_url = ""
444 filter = make_filter_mock({
454 filter = make_filter_mock(
445455 "url" => "http://example.com",
446 "baseurl" => "/base",
447 })
456 "baseurl" => "/base"
457 )
448458 assert_equal "http://example.com/base", filter.absolute_url(page_url)
449459 end
450460
451461 should "not append a forward slash if input is '/'" do
452462 page_url = "/"
453 filter = make_filter_mock({
463 filter = make_filter_mock(
454464 "url" => "http://example.com",
455 "baseurl" => "/base",
456 })
465 "baseurl" => "/base"
466 )
457467 assert_equal "http://example.com/base/", filter.absolute_url(page_url)
458468 end
459469
460470 should "not append a forward slash if input is '/' and nil 'baseurl'" do
461471 page_url = "/"
462 filter = make_filter_mock({
472 filter = make_filter_mock(
463473 "url" => "http://example.com",
464 "baseurl" => nil,
465 })
474 "baseurl" => nil
475 )
466476 assert_equal "http://example.com/", filter.absolute_url(page_url)
467477 end
468478
469479 should "not append a forward slash if both input and baseurl are simply '/'" do
470480 page_url = "/"
471 filter = make_filter_mock({
481 filter = make_filter_mock(
472482 "url" => "http://example.com",
473 "baseurl" => "/",
474 })
483 "baseurl" => "/"
484 )
475485 assert_equal "http://example.com/", filter.absolute_url(page_url)
476486 end
477487
478488 should "normalize international URLs" do
479489 page_url = ""
480 filter = make_filter_mock({
490 filter = make_filter_mock(
481491 "url" => "http://ümlaut.example.org/",
482 "baseurl" => nil,
483 })
492 "baseurl" => nil
493 )
484494 assert_equal "http://xn--mlaut-jva.example.org/", filter.absolute_url(page_url)
485495 end
486496
491501
492502 should "transform the input URL to a string" do
493503 page_url = "/my-page.html"
494 filter = make_filter_mock({ "url" => Value.new(proc { "http://example.org" }) })
504 filter = make_filter_mock("url" => Value.new(proc { "http://example.org" }))
495505 assert_equal "http://example.org#{page_url}", filter.absolute_url(page_url)
496506 end
497507
498508 should "not raise a TypeError when passed a hash" do
499 assert @filter.absolute_url({ "foo" => "bar" })
509 assert @filter.absolute_url("foo" => "bar")
500510 end
501511
502512 context "with a document" do
503513 setup do
504 @site = fixture_site({
505 "collections" => ["methods"],
506 })
514 @site = fixture_site(
515 "collections" => ["methods"]
516 )
507517 @site.process
508518 @document = @site.collections["methods"].docs.detect do |d|
509519 d.relative_path == "_methods/configuration.md"
530540
531541 should "ensure the leading slash for the baseurl" do
532542 page_url = "about/my_favorite_page/"
533 filter = make_filter_mock({ "baseurl" => "base" })
543 filter = make_filter_mock("baseurl" => "base")
534544 assert_equal "/base/#{page_url}", filter.relative_url(page_url)
535545 end
536546
541551
542552 should "be ok with a nil 'baseurl'" do
543553 page_url = "about/my_favorite_page/"
544 filter = make_filter_mock({
554 filter = make_filter_mock(
545555 "url" => "http://example.com",
546 "baseurl" => nil,
547 })
556 "baseurl" => nil
557 )
548558 assert_equal "/#{page_url}", filter.relative_url(page_url)
549559 end
550560
551561 should "not prepend a forward slash if input is empty" do
552562 page_url = ""
553 filter = make_filter_mock({
563 filter = make_filter_mock(
554564 "url" => "http://example.com",
555 "baseurl" => "/base",
556 })
565 "baseurl" => "/base"
566 )
557567 assert_equal "/base", filter.relative_url(page_url)
558568 end
559569
560570 should "not prepend a forward slash if baseurl ends with a single '/'" do
561571 page_url = "/css/main.css"
562 filter = make_filter_mock({
572 filter = make_filter_mock(
563573 "url" => "http://example.com",
564 "baseurl" => "/base/",
565 })
574 "baseurl" => "/base/"
575 )
566576 assert_equal "/base/css/main.css", filter.relative_url(page_url)
567577 end
568578
569579 should "not return valid URI if baseurl ends with multiple '/'" do
570580 page_url = "/css/main.css"
571 filter = make_filter_mock({
581 filter = make_filter_mock(
572582 "url" => "http://example.com",
573 "baseurl" => "/base//",
574 })
583 "baseurl" => "/base//"
584 )
575585 refute_equal "/base/css/main.css", filter.relative_url(page_url)
576586 end
577587
578588 should "not prepend a forward slash if both input and baseurl are simply '/'" do
579589 page_url = "/"
580 filter = make_filter_mock({
590 filter = make_filter_mock(
581591 "url" => "http://example.com",
582 "baseurl" => "/",
583 })
592 "baseurl" => "/"
593 )
584594 assert_equal "/", filter.relative_url(page_url)
585595 end
586596
587597 should "not return the url by reference" do
588 filter = make_filter_mock({ :baseurl => nil })
598 filter = make_filter_mock(:baseurl => nil)
589599 page = Page.new(filter.site, test_dir("fixtures"), "", "front_matter.erb")
590600 assert_equal "/front_matter.erb", page.url
591601 url = filter.relative_url(page.url)
592602 url << "foo"
603 assert_equal "/front_matter.erb", filter.relative_url(page.url)
593604 assert_equal "/front_matter.erb", page.url
594605 end
595606
596607 should "transform the input baseurl to a string" do
597608 page_url = "/my-page.html"
598 filter = make_filter_mock({ "baseurl" => Value.new(proc { "/baseurl/" }) })
609 filter = make_filter_mock("baseurl" => Value.new(proc { "/baseurl/" }))
599610 assert_equal "/baseurl#{page_url}", filter.relative_url(page_url)
600611 end
601612
639650
640651 context "jsonify filter" do
641652 should "convert hash to json" do
642 assert_equal "{\"age\":18}", @filter.jsonify({ :age => 18 })
653 assert_equal "{\"age\":18}", @filter.jsonify(:age => 18)
643654 end
644655
645656 should "convert array to json" do
653664 should "convert drop to json" do
654665 @filter.site.read
655666 expected = {
667 "name" => "2008-02-02-published.markdown",
656668 "path" => "_posts/2008-02-02-published.markdown",
657669 "previous" => nil,
658670 "output" => nil,
686698 should "convert drop with drops to json" do
687699 @filter.site.read
688700 actual = @filter.jsonify(@filter.site.to_liquid)
689 assert_equal JSON.parse(actual)["jekyll"], {
701 expected = {
690702 "environment" => "development",
691703 "version" => Jekyll::VERSION,
692704 }
705 assert_equal expected, JSON.parse(actual)["jekyll"]
693706 end
694707
695708 # rubocop:disable Style/StructInheritance
698711 [message]
699712 end
700713 end
714
701715 class T < Struct.new(:name)
702716 def to_liquid
703717 {
704718 "name" => name,
705719 :v => 1,
706 :thing => M.new({ :kay => "jewelers" }),
720 :thing => M.new(:kay => "jewelers"),
707721 :stuff => true,
708722 }
709723 end
782796 should "successfully group array of Jekyll::Page's" do
783797 @filter.site.process
784798 grouping = @filter.group_by(@filter.site.pages, "layout")
799 names = ["default", "nil", ""]
785800 grouping.each do |g|
786 assert(
787 ["default", "nil", ""].include?(g["name"]),
788 "#{g["name"]} isn't a valid grouping."
789 )
801 assert_includes names, g["name"], "#{g["name"]} isn't a valid grouping."
790802 case g["name"]
791803 when "default"
792804 assert(
808820 "The list of grouped items for '' is not an Array."
809821 )
810822 # adjust array.size to ignore symlinked page in Windows
811 qty = Utils::Platforms.really_windows? ? 14 : 15
823 qty = Utils::Platforms.really_windows? ? 19 : 21
812824 assert_equal qty, g["items"].size
813825 end
814826 end
824836 )
825837 end
826838 end
839
840 should "should pass integers as is" do
841 grouping = @filter.group_by([
842 { "name" => "Allison", "year" => 2016 },
843 { "name" => "Amy", "year" => 2016 },
844 { "name" => "George", "year" => 2019 },
845 ], "year")
846 assert_equal "2016", grouping[0]["name"]
847 assert_equal "2019", grouping[1]["name"]
848 end
827849 end
828850
829851 context "where filter" do
839861
840862 should "filter objects appropriately" do
841863 assert_equal 2, @filter.where(@array_of_objects, "color", "red").length
864 end
865
866 should "filter objects with null properties appropriately" do
867 array = [{}, { "color" => nil }, { "color" => "" }, { "color" => "text" }]
868 assert_equal 2, @filter.where(array, "color", nil).length
869 end
870
871 should "filter objects with numerical properties appropriately" do
872 array = [
873 { "value" => "555" },
874 { "value" => 555 },
875 { "value" => 24.625 },
876 { "value" => "24.625" },
877 ]
878 assert_equal 2, @filter.where(array, "value", 24.625).length
879 assert_equal 2, @filter.where(array, "value", 555).length
842880 end
843881
844882 should "filter array properties appropriately" do
859897 assert_equal 2, @filter.where(hash, "tags", "x").length
860898 end
861899
900 should "filter hash properties with null and empty values" do
901 hash = {
902 "a" => { "tags" => {} },
903 "b" => { "tags" => "" },
904 "c" => { "tags" => nil },
905 "d" => { "tags" => ["x", nil] },
906 "e" => { "tags" => [] },
907 "f" => { "tags" => "xtra" },
908 }
909
910 assert_equal [{ "tags" => nil }], @filter.where(hash, "tags", nil)
911
912 assert_equal(
913 [{ "tags" => "" }, { "tags" => ["x", nil] }],
914 @filter.where(hash, "tags", "")
915 )
916
917 # `{{ hash | where: 'tags', empty }}`
918 assert_equal(
919 [{ "tags" => {} }, { "tags" => "" }, { "tags" => nil }, { "tags" => [] }],
920 @filter.where(hash, "tags", Liquid::Expression::LITERALS["empty"])
921 )
922
923 # `{{ `hash | where: 'tags', blank }}`
924 assert_equal(
925 [{ "tags" => {} }, { "tags" => "" }, { "tags" => nil }, { "tags" => [] }],
926 @filter.where(hash, "tags", Liquid::Expression::LITERALS["blank"])
927 )
928 end
929
862930 should "not match substrings" do
863931 hash = {
864932 "a" => { "category"=>"bear" },
877945
878946 results = @filter.where(hash, "featured", "true")
879947 assert_equal 2, results.length
880 assert_equal 9.2, results[0]["rating"]
881 assert_equal 4.7, results[1]["rating"]
948 assert_in_delta(9.2, results[0]["rating"])
949 assert_in_delta(4.7, results[1]["rating"])
882950
883951 results = @filter.where(hash, "rating", 4.7)
884952 assert_equal 1, results.length
885 assert_equal 4.7, results[0]["rating"]
953 assert_in_delta(4.7, results[0]["rating"])
886954 end
887955
888956 should "always return an array if the object responds to 'select'" do
909977 assert_equal(
910978 2,
911979 @filter.where_exp(@array_of_objects, "item", "item.color == 'red'").length
980 )
981 end
982
983 should "filter objects appropriately with 'or', 'and' operators" do
984 assert_equal(
985 [
986 { "color" => "teal", "size" => "large" },
987 { "color" => "red", "size" => "large" },
988 { "color" => "red", "size" => "medium" },
989 ],
990 @filter.where_exp(
991 @array_of_objects, "item", "item.color == 'red' or item.size == 'large'"
992 )
993 )
994
995 assert_equal(
996 [
997 { "color" => "red", "size" => "large" },
998 ],
999 @filter.where_exp(
1000 @array_of_objects, "item", "item.color == 'red' and item.size == 'large'"
1001 )
1002 )
1003 end
1004
1005 should "filter objects across multiple conditions" do
1006 sample = [
1007 { "color" => "teal", "size" => "large", "type" => "variable" },
1008 { "color" => "red", "size" => "large", "type" => "fixed" },
1009 { "color" => "red", "size" => "medium", "type" => "variable" },
1010 { "color" => "blue", "size" => "medium", "type" => "fixed" },
1011 ]
1012 assert_equal(
1013 [
1014 { "color" => "red", "size" => "large", "type" => "fixed" },
1015 ],
1016 @filter.where_exp(
1017 sample, "item", "item.type == 'fixed' and item.color == 'red' or item.color == 'teal'"
1018 )
9121019 )
9131020 end
9141021
9211028
9221029 results = @filter.where_exp(hash, "item", "item.featured == true")
9231030 assert_equal 2, results.length
924 assert_equal 9.2, results[0]["rating"]
925 assert_equal 4.7, results[1]["rating"]
1031 assert_in_delta(9.2, results[0]["rating"])
1032 assert_in_delta(4.7, results[1]["rating"])
9261033
9271034 results = @filter.where_exp(hash, "item", "item.rating == 4.7")
9281035 assert_equal 1, results.length
929 assert_equal 4.7, results[0]["rating"]
1036 assert_in_delta(4.7, results[0]["rating"])
9301037 end
9311038
9321039 should "filter with other operators" do
9711078 @filter.site.tap(&:read)
9721079 posts = @filter.site.site_payload["site"]["posts"]
9731080 results = @filter.where_exp(posts, "post",
974 "post.date > site.dont_show_posts_before")
975 assert_equal posts.select { |p| p.date > @sample_time }.count, results.length
1081 "post.date > site.dont_show_posts_before")
1082 assert_equal posts.count { |p| p.date > @sample_time }, results.length
1083 end
1084 end
1085
1086 context "find filter" do
1087 should "return any input that is not an array" do
1088 assert_equal "some string", @filter.find("some string", "la", "le")
1089 end
1090
1091 should "filter objects in a hash appropriately" do
1092 hash = { "a" => { "color" => "red" }, "b" => { "color" => "blue" } }
1093 assert_equal({ "color" => "red" }, @filter.find(hash, "color", "red"))
1094 end
1095
1096 should "filter objects appropriately" do
1097 assert_equal(
1098 { "color" => "red", "size" => "large" },
1099 @filter.find(@array_of_objects, "color", "red")
1100 )
1101 end
1102
1103 should "filter objects with null properties appropriately" do
1104 array = [{}, { "color" => nil }, { "color" => "" }, { "color" => "text" }]
1105 assert_equal({}, @filter.find(array, "color", nil))
1106 end
1107
1108 should "filter objects with numerical properties appropriately" do
1109 array = [
1110 { "value" => "555" },
1111 { "value" => 555 },
1112 { "value" => 24.625 },
1113 { "value" => "24.625" },
1114 ]
1115 assert_equal({ "value" => 24.625 }, @filter.find(array, "value", 24.625))
1116 assert_equal({ "value" => "555" }, @filter.find(array, "value", 555))
1117 end
1118
1119 should "filter array properties appropriately" do
1120 hash = {
1121 "a" => { "tags" => %w(x y) },
1122 "b" => { "tags" => ["x"] },
1123 "c" => { "tags" => %w(y z) },
1124 }
1125 assert_equal({ "tags" => %w(x y) }, @filter.find(hash, "tags", "x"))
1126 end
1127
1128 should "filter array properties alongside string properties" do
1129 hash = {
1130 "a" => { "tags" => %w(x y) },
1131 "b" => { "tags" => "x" },
1132 "c" => { "tags" => %w(y z) },
1133 }
1134 assert_equal({ "tags" => %w(x y) }, @filter.find(hash, "tags", "x"))
1135 end
1136
1137 should "filter hash properties with null and empty values" do
1138 hash = {
1139 "a" => { "tags" => {} },
1140 "b" => { "tags" => "" },
1141 "c" => { "tags" => nil },
1142 "d" => { "tags" => ["x", nil] },
1143 "e" => { "tags" => [] },
1144 "f" => { "tags" => "xtra" },
1145 }
1146
1147 assert_equal({ "tags" => nil }, @filter.find(hash, "tags", nil))
1148 assert_equal({ "tags" => "" }, @filter.find(hash, "tags", ""))
1149
1150 # `{{ hash | find: 'tags', empty }}`
1151 assert_equal(
1152 { "tags" => {} },
1153 @filter.find(hash, "tags", Liquid::Expression::LITERALS["empty"])
1154 )
1155
1156 # `{{ `hash | find: 'tags', blank }}`
1157 assert_equal(
1158 { "tags" => {} },
1159 @filter.find(hash, "tags", Liquid::Expression::LITERALS["blank"])
1160 )
1161 end
1162
1163 should "not match substrings" do
1164 hash = {
1165 "a" => { "category" => "bear" },
1166 "b" => { "category" => "wolf" },
1167 "c" => { "category" => %w(bear lion) },
1168 }
1169 assert_nil @filter.find(hash, "category", "ear")
1170 end
1171
1172 should "stringify during comparison for compatibility with liquid parsing" do
1173 hash = {
1174 "The Words" => { "rating" => 1.2, "featured" => false },
1175 "Limitless" => { "rating" => 9.2, "featured" => true },
1176 "Hustle" => { "rating" => 4.7, "featured" => true },
1177 }
1178
1179 result = @filter.find(hash, "featured", "true")
1180 assert_in_delta(9.2, result["rating"])
1181
1182 result = @filter.find(hash, "rating", 4.7)
1183 assert_in_delta(4.7, result["rating"])
1184 end
1185 end
1186
1187 context "find_exp filter" do
1188 should "return any input that is not an array" do
1189 assert_equal "some string", @filter.find_exp("some string", "la", "le")
1190 end
1191
1192 should "filter objects in a hash appropriately" do
1193 hash = { "a" => { "color"=>"red" }, "b" => { "color"=>"blue" } }
1194 assert_equal(
1195 { "color" => "red" },
1196 @filter.find_exp(hash, "item", "item.color == 'red'")
1197 )
1198 end
1199
1200 should "filter objects appropriately" do
1201 assert_equal(
1202 { "color" => "red", "size" => "large" },
1203 @filter.find_exp(@array_of_objects, "item", "item.color == 'red'")
1204 )
1205 end
1206
1207 should "filter objects appropriately with 'or', 'and' operators" do
1208 assert_equal(
1209 { "color" => "teal", "size" => "large" },
1210 @filter.find_exp(
1211 @array_of_objects, "item", "item.color == 'red' or item.size == 'large'"
1212 )
1213 )
1214
1215 assert_equal(
1216 { "color" => "red", "size" => "large" },
1217 @filter.find_exp(
1218 @array_of_objects, "item", "item.color == 'red' and item.size == 'large'"
1219 )
1220 )
1221 end
1222
1223 should "filter objects across multiple conditions" do
1224 sample = [
1225 { "color" => "teal", "size" => "large", "type" => "variable" },
1226 { "color" => "red", "size" => "large", "type" => "fixed" },
1227 { "color" => "red", "size" => "medium", "type" => "variable" },
1228 { "color" => "blue", "size" => "medium", "type" => "fixed" },
1229 ]
1230 assert_equal(
1231 { "color" => "red", "size" => "large", "type" => "fixed" },
1232 @filter.find_exp(
1233 sample, "item", "item.type == 'fixed' and item.color == 'red' or item.color == 'teal'"
1234 )
1235 )
1236 end
1237
1238 should "stringify during comparison for compatibility with liquid parsing" do
1239 hash = {
1240 "The Words" => { "rating" => 1.2, "featured" => false },
1241 "Limitless" => { "rating" => 9.2, "featured" => true },
1242 "Hustle" => { "rating" => 4.7, "featured" => true },
1243 }
1244
1245 result = @filter.find_exp(hash, "item", "item.featured == true")
1246 assert_in_delta(9.2, result["rating"])
1247
1248 result = @filter.find_exp(hash, "item", "item.rating == 4.7")
1249 assert_in_delta(4.7, result["rating"])
1250 end
1251
1252 should "filter with other operators" do
1253 assert_equal 3, @filter.find_exp([1, 2, 3, 4, 5], "n", "n >= 3")
1254 end
1255
1256 objects = [
1257 { "id" => "a", "groups" => [1, 2] },
1258 { "id" => "b", "groups" => [2, 3] },
1259 { "id" => "c" },
1260 { "id" => "d", "groups" => [1, 3] },
1261 ]
1262 should "filter with the contains operator over arrays" do
1263 result = @filter.find_exp(objects, "obj", "obj.groups contains 1")
1264 assert_equal "a", result["id"]
1265 end
1266
1267 should "filter with the contains operator over hash keys" do
1268 result = @filter.find_exp(objects, "obj", "obj contains 'groups'")
1269 assert_equal "a", result["id"]
1270 end
1271
1272 should "filter posts" do
1273 site = fixture_site.tap(&:read)
1274 posts = site.site_payload["site"]["posts"]
1275 result = @filter.find_exp(posts, "obj", "obj.title == 'Foo Bar'")
1276 assert_equal(result, site.posts.find { |p| p.title == "Foo Bar" })
1277 end
1278
1279 should "filter by variable values" do
1280 @filter.site.tap(&:read)
1281 posts = @filter.site.site_payload["site"]["posts"]
1282 result = @filter.find_exp(posts, "post", "post.date > site.dont_show_posts_before")
1283 assert result.date > @sample_time
9761284 end
9771285 end
9781286
9801288 should "successfully group array of Jekyll::Page's" do
9811289 @filter.site.process
9821290 groups = @filter.group_by_exp(@filter.site.pages, "page", "page.layout | upcase")
1291 names = ["DEFAULT", "NIL", ""]
9831292 groups.each do |g|
984 assert(
985 ["DEFAULT", "NIL", ""].include?(g["name"]),
986 "#{g["name"]} isn't a valid grouping."
987 )
1293 assert_includes names, g["name"], "#{g["name"]} isn't a valid grouping."
9881294 case g["name"]
9891295 when "DEFAULT"
9901296 assert(
10061312 "The list of grouped items for '' is not an Array."
10071313 )
10081314 # adjust array.size to ignore symlinked page in Windows
1009 qty = Utils::Platforms.really_windows? ? 14 : 15
1315 qty = Utils::Platforms.really_windows? ? 19 : 21
10101316 assert_equal qty, g["items"].size
10111317 end
10121318 end
10731379 end
10741380 should "return sorted strings" do
10751381 assert_equal %w(10 2), @filter.sort(%w(10 2))
1076 assert_equal(
1077 [{ "a" => "10" }, { "a" => "2" }],
1078 @filter.sort([{ "a" => "10" }, { "a" => "2" }], "a")
1079 )
10801382 assert_equal %w(FOO Foo foo), @filter.sort(%w(foo Foo FOO))
10811383 assert_equal %w(_foo foo foo_), @filter.sort(%w(foo_ _foo foo))
10821384 # Cyrillic
10871389 end
10881390 should "return sorted by property array" do
10891391 assert_equal [{ "a" => 1 }, { "a" => 2 }, { "a" => 3 }, { "a" => 4 }],
1090 @filter.sort([{ "a" => 4 }, { "a" => 3 }, { "a" => 1 }, { "a" => 2 }], "a")
1392 @filter.sort([{ "a" => 4 }, { "a" => 3 }, { "a" => 1 }, { "a" => 2 }], "a")
1393 end
1394 should "return sorted by property array with numeric strings sorted as numbers" do
1395 assert_equal([{ "a" => ".5" }, { "a" => "0.65" }, { "a" => "10" }],
1396 @filter.sort([{ "a" => "10" }, { "a" => ".5" }, { "a" => "0.65" }], "a"))
1397 end
1398 should "return sorted by property array with numeric strings first" do
1399 assert_equal([{ "a" => ".5" }, { "a" => "0.6" }, { "a" => "twelve" }],
1400 @filter.sort([{ "a" => "twelve" }, { "a" => ".5" }, { "a" => "0.6" }], "a"))
1401 end
1402 should "return sorted by property array with numbers and strings " do
1403 assert_equal([{ "a" => "1" }, { "a" => "1abc" }, { "a" => "20" }],
1404 @filter.sort([{ "a" => "20" }, { "a" => "1" }, { "a" => "1abc" }], "a"))
10911405 end
10921406 should "return sorted by property array with nils first" do
10931407 ary = [{ "a" => 2 }, { "b" => 1 }, { "a" => 1 }]
10961410 end
10971411 should "return sorted by property array with nils last" do
10981412 assert_equal [{ "a" => 1 }, { "a" => 2 }, { "b" => 1 }],
1099 @filter.sort([{ "a" => 2 }, { "b" => 1 }, { "a" => 1 }], "a", "last")
1413 @filter.sort([{ "a" => 2 }, { "b" => 1 }, { "a" => 1 }], "a", "last")
11001414 end
11011415 should "return sorted by subproperty array" do
11021416 assert_equal [{ "a" => { "b" => 1 } }, { "a" => { "b" => 2 } },
11031417 { "a" => { "b" => 3 } },],
1104 @filter.sort([{ "a" => { "b" => 2 } }, { "a" => { "b" => 1 } },
1105 { "a" => { "b" => 3 } },], "a.b")
1418 @filter.sort([{ "a" => { "b" => 2 } }, { "a" => { "b" => 1 } },
1419 { "a" => { "b" => 3 } },], "a.b")
11061420 end
11071421 end
11081422
11301444
11311445 context "inspect filter" do
11321446 should "return a HTML-escaped string representation of an object" do
1133 assert_equal "{&quot;&lt;a&gt;&quot;=&gt;1}", @filter.inspect({ "<a>" => 1 })
1447 assert_equal "{&quot;&lt;a&gt;&quot;=&gt;1}", @filter.inspect("<a>" => 1)
11341448 end
11351449
11361450 should "quote strings" do
12011515 end
12021516 end
12031517 end
1518
1519 context "number_of_words filter" do
1520 should "return the number of words for Latin-only text" do
1521 assert_equal 5, @filter.number_of_words("hello world and taoky strong!", "auto")
1522 assert_equal 5, @filter.number_of_words("hello world and taoky strong!", "cjk")
1523 end
1524
1525 should "return the number of characters for CJK-only text" do
1526 assert_equal 17, @filter.number_of_words("こんにちは、世界!안녕하세요 세상!", "auto")
1527 assert_equal 17, @filter.number_of_words("こんにちは、世界!안녕하세요 세상!", "cjk")
1528 end
1529
1530 should "process Latin and CJK independently" do
1531 # Intentional: No space between Latin and CJK
1532 assert_equal 6, @filter.number_of_words("你好hello世界world", "auto")
1533 assert_equal 6, @filter.number_of_words("你好hello世界world", "cjk")
1534 end
1535
1536 should "maintain original behavior unless specified" do
1537 assert_equal 1, @filter.number_of_words("你好hello世界world")
1538 end
1539 end
12041540 end
12051541 end
44 class TestFrontMatterDefaults < JekyllUnitTest
55 context "A site with full front matter defaults" do
66 setup do
7 @site = fixture_site({
7 @site = fixture_site(
88 "defaults" => [{
99 "scope" => {
1010 "path" => "contacts",
1313 "values" => {
1414 "key" => "val",
1515 },
16 },],
17 })
16 }]
17 )
1818 @output = capture_output { @site.process }
1919 @affected = @site.pages.find { |page| page.relative_path == "contacts/bar.html" }
2020 @not_affected = @site.pages.find { |page| page.relative_path == "about.html" }
2121 end
2222
2323 should "affect only the specified path and type" do
24 assert_equal @affected.data["key"], "val"
24 assert_equal "val", @affected.data["key"]
2525 assert_nil @not_affected.data["key"]
2626 end
2727
3232
3333 context "A site with full front matter defaults (glob)" do
3434 setup do
35 @site = fixture_site({
35 @site = fixture_site(
3636 "defaults" => [{
3737 "scope" => {
3838 "path" => "contacts/*.html",
4141 "values" => {
4242 "key" => "val",
4343 },
44 },],
45 })
44 }]
45 )
4646 @output = capture_output { @site.process }
4747 @affected = @site.pages.find { |page| page.relative_path == "contacts/bar.html" }
4848 @not_affected = @site.pages.find { |page| page.relative_path == "about.html" }
4949 end
5050
5151 should "affect only the specified path and type" do
52 assert_equal @affected.data["key"], "val"
52 assert_equal "val", @affected.data["key"]
5353 assert_nil @not_affected.data["key"]
5454 end
5555
6060
6161 context "A site with front matter type pages and an extension" do
6262 setup do
63 @site = fixture_site({
63 @site = fixture_site(
6464 "defaults" => [{
6565 "scope" => {
6666 "path" => "index.html",
6868 "values" => {
6969 "key" => "val",
7070 },
71 },],
72 })
71 }]
72 )
7373
7474 @site.process
7575 @affected = @site.pages.find { |page| page.relative_path == "index.html" }
7777 end
7878
7979 should "affect only the specified path" do
80 assert_equal @affected.data["key"], "val"
80 assert_equal "val", @affected.data["key"]
8181 assert_nil @not_affected.data["key"]
8282 end
8383 end
8484
8585 context "A site with front matter defaults with no type" do
8686 setup do
87 @site = fixture_site({
87 @site = fixture_site(
8888 "defaults" => [{
8989 "scope" => {
9090 "path" => "win",
9292 "values" => {
9393 "key" => "val",
9494 },
95 },],
96 })
97
98 @site.process
99 @affected = @site.posts.docs.find { |page| page.relative_path =~ %r!win\/! }
95 }]
96 )
97
98 @site.process
99 @affected = @site.posts.docs.find { |page| page.relative_path.include?("win") }
100100 @not_affected = @site.pages.find { |page| page.relative_path == "about.html" }
101101 end
102102
103103 should "affect only the specified path and all types" do
104 assert_equal @affected.data["key"], "val"
104 assert_equal "val", @affected.data["key"]
105105 assert_nil @not_affected.data["key"]
106106 end
107107 end
108108
109109 context "A site with front matter defaults with no path and a deprecated type" do
110110 setup do
111 @site = fixture_site({
111 @site = fixture_site(
112112 "defaults" => [{
113113 "scope" => {
114114 "type" => "page",
116116 "values" => {
117117 "key" => "val",
118118 },
119 },],
120 })
119 }]
120 )
121121
122122 @site.process
123123 @affected = @site.pages
125125 end
126126
127127 should "affect only the specified type and all paths" do
128 assert_equal @affected.reject { |page| page.data["key"] == "val" }, []
128 assert_equal([], @affected.reject { |page| page.data["key"] == "val" })
129129 assert_equal @not_affected.reject { |page| page.data["key"] == "val" },
130130 @not_affected
131131 end
133133
134134 context "A site with front matter defaults with no path" do
135135 setup do
136 @site = fixture_site({
136 @site = fixture_site(
137137 "defaults" => [{
138138 "scope" => {
139139 "type" => "pages",
141141 "values" => {
142142 "key" => "val",
143143 },
144 },],
145 })
144 }]
145 )
146146 @site.process
147147 @affected = @site.pages
148148 @not_affected = @site.posts.docs
149149 end
150150
151151 should "affect only the specified type and all paths" do
152 assert_equal @affected.reject { |page| page.data["key"] == "val" }, []
152 assert_equal([], @affected.reject { |page| page.data["key"] == "val" })
153153 assert_equal @not_affected.reject { |page| page.data["key"] == "val" },
154154 @not_affected
155155 end
157157
158158 context "A site with front matter defaults with no path or type" do
159159 setup do
160 @site = fixture_site({
161 "defaults" => [{
162 "scope" => {
163 },
164 "values" => {
165 "key" => "val",
166 },
167 },],
168 })
160 @site = fixture_site(
161 "defaults" => [{
162 "scope" => {
163 },
164 "values" => {
165 "key" => "val",
166 },
167 }]
168 )
169169 @site.process
170170 @affected = @site.pages
171171 @not_affected = @site.posts
172172 end
173173
174174 should "affect all types and paths" do
175 assert_equal @affected.reject { |page| page.data["key"] == "val" }, []
176 assert_equal @not_affected.reject { |page| page.data["key"] == "val" }, []
175 assert_equal([], @affected.reject { |page| page.data["key"] == "val" })
176 assert_equal([], @not_affected.reject { |page| page.data["key"] == "val" })
177177 end
178178 end
179179
180180 context "A site with front matter defaults with no scope" do
181181 setup do
182 @site = fixture_site({
183 "defaults" => [{
184 "values" => {
185 "key" => "val",
186 },
187 },],
188 })
182 @site = fixture_site(
183 "defaults" => [{
184 "values" => {
185 "key" => "val",
186 },
187 }]
188 )
189189 @site.process
190190 @affected = @site.pages
191191 @not_affected = @site.posts
192192 end
193193
194194 should "affect all types and paths" do
195 assert_equal @affected.reject { |page| page.data["key"] == "val" }, []
196 assert_equal @not_affected.reject { |page| page.data["key"] == "val" }, []
195 assert_equal([], @affected.reject { |page| page.data["key"] == "val" })
196 assert_equal([], @not_affected.reject { |page| page.data["key"] == "val" })
197197 end
198198 end
199199
200200 context "A site with front matter defaults with quoted date" do
201201 setup do
202 @site = Site.new(Jekyll.configuration({
203 "source" => source_dir,
204 "destination" => dest_dir,
205 "defaults" => [{
206 "values" => {
207 "date" => "2015-01-01 00:00:01",
208 },
209 },],
210 }))
202 @site = Site.new(Jekyll.configuration(
203 "source" => source_dir,
204 "destination" => dest_dir,
205 "defaults" => [{
206 "values" => {
207 "date" => "2015-01-01 00:00:01",
208 },
209 }]
210 ))
211211 end
212212
213213 should "not raise error" do
1010 @site.process
1111 @index = File.read(
1212 dest_dir("index.html"),
13 Utils.merged_file_read_opts(@site, {})
13 **Utils.merged_file_read_opts(@site, {})
1414 )
1515 end
1616
1717 should "ensure post count is as expected" do
18 assert_equal 58, @site.posts.size
18 assert_equal 59, @site.posts.size
1919 end
2020
2121 should "insert site.posts into the index" do
22 assert @index.include?("#{@site.posts.size} Posts")
22 assert_includes @index, "#{@site.posts.size} Posts"
2323 end
2424
2525 should "insert variable from layout into the index" do
26 assert @index.include?("variable from layout")
26 assert_includes @index, "variable from layout"
2727 end
2828
2929 should "render latest post's content" do
30 assert @index.include?(@site.posts.last.content)
30 assert_includes @index, @site.posts.last.content
3131 end
3232
3333 should "hide unpublished posts" do
6969 time_regexp = "\\d+:\\d+"
7070 #
7171 # adding a pipe character at the beginning preserves formatting with newlines
72 expected_output = Regexp.new <<-OUTPUT
73 | - /css/screen.css last edited at #{time_regexp} with extname .css
74 - /pgp.key last edited at #{time_regexp} with extname .key
75 - /products.yml last edited at #{time_regexp} with extname .yml
76 - /symlink-test/symlinked-dir/screen.css last edited at #{time_regexp} with extname .css
77 OUTPUT
72 expected_output = Regexp.new <<~OUTPUT
73 | - /css/screen.css last edited at #{time_regexp} with extname .css
74 - /pgp.key last edited at #{time_regexp} with extname .key
75 - /products.yml last edited at #{time_regexp} with extname .yml
76 - /symlink-test/symlinked-dir/screen.css last edited at #{time_regexp} with extname .css
77 OUTPUT
7878 assert_match expected_output, File.read(dest_dir("static_files.html"))
7979 end
8080 end
00 # frozen_string_literal: true
11
22 require "helper"
3 require "rouge"
34
45 class TestKramdown < JekyllUnitTest
56 def fixture_converter(config)
6 site_config = Utils.deep_merge_hashes({ "markdown" => "kramdown" }, config)
7 site = fixture_site(site_config)
8 converter = site.find_converter_instance(Jekyll::Converters::Markdown)
9 converter.setup
10 converter
7 site = fixture_site(
8 Utils.deep_merge_hashes(
9 {
10 "markdown" => "kramdown",
11 },
12 config
13 )
14 )
15 Jekyll::Cache.clear
16 site.find_converter_instance(
17 Jekyll::Converters::Markdown
18 )
1119 end
1220
1321 context "kramdown" do
2634 "bold_every" => 8,
2735 "css" => :class,
2836 "css_class" => "highlight",
29 "formatter" => Jekyll::Utils::Rouge.html_formatter.class,
37 "formatter" => ::Rouge::Formatters::HTMLLegacy,
38 "foobar" => "lipsum",
3039 },
3140 },
3241 }
3847 @converter = fixture_converter(@config)
3948 end
4049
41 should "fill symbolized keys into config for compatibility with kramdown" do
42 kramdown_config = @converter.instance_variable_get(:@parser)
43 .instance_variable_get(:@config)
44
45 @kramdown_config_keys.each do |key|
46 assert kramdown_config.key?(key.to_sym),
47 "Expected #{kramdown_config} to include key #{key.to_sym.inspect}"
48 end
49
50 @syntax_highlighter_opts_config_keys.each do |key|
51 assert kramdown_config["syntax_highlighter_opts"].key?(key.to_sym),
52 "Expected #{kramdown_config["syntax_highlighter_opts"]} to include " \
53 "key #{key.to_sym.inspect}"
54 end
55
56 assert_equal kramdown_config["smart_quotes"], kramdown_config[:smart_quotes]
57 assert_equal kramdown_config["syntax_highlighter_opts"]["css"],
58 kramdown_config[:syntax_highlighter_opts][:css]
50 should "not break kramdown" do
51 kramdown_doc = Kramdown::Document.new("# Some Header #", @config["kramdown"])
52 assert_equal :class, kramdown_doc.options[:syntax_highlighter_opts][:css]
53 assert_equal "lipsum", kramdown_doc.options[:syntax_highlighter_opts][:foobar]
5954 end
6055
6156 should "run Kramdown" do
7469 puts "Hello World"
7570 ~~~
7671 MARKDOWN
77 div_highlight = Rouge.version.to_i == 1 ? "" : ">div.highlight"
72 div_highlight = ">div.highlight"
7873 selector = "div.highlighter-rouge#{div_highlight}>pre.highlight>code"
79 refute(result.css(selector).empty?, result.to_html)
74 refute_empty(result.css(selector), result.to_html)
8075 end
8176
8277 context "when configured" do
9287
9388 should "have 'plaintext' as the default syntax_highlighter language" do
9489 converter = fixture_converter(@config)
95 parser = converter.instance_variable_get(:@parser)
90 parser = converter.setup && converter.instance_variable_get(:@parser)
9691 parser_config = parser.instance_variable_get(:@config)
9792
9893 assert_equal "plaintext", parser_config.dig("syntax_highlighter_opts", "default_lang")
107102 },
108103 }
109104 converter = fixture_converter(Utils.deep_merge_hashes(@config, override))
110 parser = converter.instance_variable_get(:@parser)
105 parser = converter.setup && converter.instance_variable_get(:@parser)
111106 parser_config = parser.instance_variable_get(:@config)
112107
113108 assert_equal "yaml", parser_config.dig("syntax_highlighter_opts", "default_lang")
121116 should "convert" do
122117 converter = fixture_converter(@config)
123118 assert_match(
124 %r!<p>(&#8220;|“)Pit(&#8217;|’)hy(&#8221;|”)<\/p>!,
119 %r!<p>(&#8220;|“)Pit(&#8217;|’)hy(&#8221;|”)</p>!,
125120 converter.convert(%("Pit'hy")).strip
126121 )
127122 end
134129 },
135130 }
136131 converter = fixture_converter(Utils.deep_merge_hashes(@config, override))
137 assert_match %r!<p>(&#171;|«)Pit(&#8250;|›)hy(&#187;|»)<\/p>!, \
138 converter.convert(%("Pit'hy")).strip
132 assert_match %r!<p>(&#171;|«)Pit(&#8250;|›)hy(&#187;|»)</p>!, \
133 converter.convert(%("Pit'hy")).strip
139134 end
140135 end
141136
147142 "syntax_highlighter" => "coderay",
148143 },
149144 }
150
151145 converter = fixture_converter(Utils.deep_merge_hashes(@config, override))
152146 result = nokogiri_fragment(converter.convert(<<~MARKDOWN))
153147 ~~~ruby
156150 MARKDOWN
157151
158152 selector = "div.highlighter-coderay>div.CodeRay>div.code>pre"
159 refute result.css(selector).empty?
153 refute_empty result.css(selector)
160154 end
161155
162156 should "support legacy enable_coderay... for now" do
177171 MARKDOWN
178172
179173 selector = "div.highlighter-coderay>div.CodeRay>div.code>pre"
180 refute result.css(selector).empty?, "pre tag should exist"
174 refute_empty result.css(selector), "pre tag should exist"
181175 end
182176 end
183177
184178 should "move coderay to syntax_highlighter_opts" do
185179 override = {
186 "higlighter" => nil,
187 "kramdown" => {
180 "highlighter" => nil,
181 "kramdown" => {
188182 "syntax_highlighter" => "coderay",
189183 "coderay" => {
190184 "hello" => "world",
197191 )
198192
199193 expect(Kramdown::Document).to receive(:new) do |arg1, hash|
200 assert_equal hash["syntax_highlighter_opts"]["hello"], "world"
194 assert_equal "world", hash["syntax_highlighter_opts"]["hello"]
201195 original.call(arg1, hash)
202196 end
203197
44 class TestLayoutReader < JekyllUnitTest
55 context "reading layouts" do
66 setup do
7 config = Jekyll::Configuration::DEFAULTS.merge({ "source" => source_dir,
8 "destination" => dest_dir, })
7 config = Jekyll::Configuration::DEFAULTS.merge("source" => source_dir,
8 "destination" => dest_dir)
99 @site = fixture_site(config)
1010 end
1111
2626 allow(Dir).to receive(:pwd).and_return(source_dir("blah"))
2727 end
2828
29 should "know to use the layout directory relative to CWD" do
30 assert_equal LayoutReader.new(@site).layout_directory, source_dir("blah/_layouts")
29 should "ignore the layout directory in CWD and use the directory relative to site source" do
30 refute_equal source_dir("blah/_layouts"), LayoutReader.new(@site).layout_directory
31 assert_equal source_dir("_layouts"), LayoutReader.new(@site).layout_directory
3132 end
3233 end
3334
2121 end
2222
2323 should "extract the var properly" do
24 assert_equal @template.render({ "page" => { "name" => "tobi" } }), "hi tobi"
24 assert_equal "hi tobi", @template.render("page" => { "name" => "tobi" })
2525 end
2626
2727 should "return the variable name if the value isn't there" do
28 assert_equal @template.render({ "page" => { "title" => "tobi" } }), "hi page.name"
28 assert_equal "hi page.name", @template.render("page" => { "title" => "tobi" })
2929 end
3030 end
3131 end
1313
1414 output = @renderer.stats_table
1515
16 # rubocop:disable Metrics/LineLength
16 # rubocop:disable Layout/LineLength
1717 expected = [
18 %r!^Filename\s+|\s+Count\s+|\s+Bytes\s+|\s+Time$!,
19 %r!^-+\++-+\++-+\++-+$!,
20 %r!^_posts/2010-01-09-date-override\.markdown\s+|\s+\d+\s+|\s+\d+\.\d{2}K\s+|\s+\d+\.\d{3}$!,
18 %r!^\| Filename\s+|\s+Count\s+|\s+Bytes\s+|\s+Time$!,
19 %r!^\+(?:-+\+){4}$!,
20 %r!^\|_posts/2010-01-09-date-override\.markdown\s+|\s+\d+\s+|\s+\d+\.\d{2}K\s+|\s+\d+\.\d{3}$!,
2121 ]
22 # rubocop:enable Metrics/LineLength
22 # rubocop:enable Layout/LineLength
2323
2424 expected.each do |regexp|
2525 assert_match regexp, output
2626 end
2727 end
28
29 should "normalize paths of rendered items" do
30 site = fixture_site("theme" => "test-theme")
31 MockRenderer = Class.new(Jekyll::LiquidRenderer) { public :normalize_path }
32 renderer = MockRenderer.new(site)
33
34 assert_equal "feed.xml", renderer.normalize_path("/feed.xml")
35 assert_equal(
36 "_layouts/post.html",
37 renderer.normalize_path(site.in_source_dir("_layouts", "post.html"))
38 )
39 assert_equal(
40 "test-theme/_layouts/page.html",
41 renderer.normalize_path(site.in_theme_dir("_layouts", "page.html"))
42 )
43 assert_equal(
44 "my_plugin-0.1.0/lib/my_plugin/layout.html",
45 renderer.normalize_path(
46 "/users/jo/blog/vendor/bundle/ruby/2.4.0/gems/my_plugin-0.1.0/lib/my_plugin/layout.html"
47 )
48 )
49 assert_equal(
50 "test_plugin-0.1.0/lib/test_plugin/layout.html",
51 renderer.normalize_path(
52 "C:/Ruby2.4/lib/ruby/gems/2.4.0/gems/test_plugin-0.1.0/lib/test_plugin/layout.html"
53 )
54 )
55 end
2856 end
2957 end
1111
1212 def site_template
1313 File.expand_path("../lib/site_template", __dir__)
14 end
15
16 def blank_template
17 File.expand_path("../lib/blank_template", __dir__)
1418 end
1519
1620 context "when args contains a path" do
3539 refute_exist @full_path
3640 capture_output { Jekyll::Commands::New.process(@args) }
3741 assert_exist gemfile
38 assert_match(%r!gem "jekyll", "~> #{Jekyll::VERSION}"!, File.read(gemfile))
42 assert_match(%r!gem "jekyll", "~> #{Jekyll::VERSION}"!o, File.read(gemfile))
3943 assert_match(%r!gem "github-pages"!, File.read(gemfile))
4044 end
4145
5660 capture_output { Jekyll::Commands::New.process(@args) }
5761
5862 new_site_files = dir_contents(@full_path).reject do |f|
59 File.extname(f) == ".markdown"
63 f.end_with?("welcome-to-jekyll.markdown")
6064 end
6165
6266 assert_same_elements static_template_files, new_site_files
8589 end
8690
8791 should "create blank project" do
88 blank_contents = %w(/_drafts /_layouts /_posts /index.html)
92 blank_contents = dir_contents(blank_template)
93 blank_contents += %w(/_data /_drafts /_includes /_posts)
8994 output = capture_output { Jekyll::Commands::New.process(@args, "--blank") }
9095 bundle_message = "Running bundle install in #{@full_path.cyan}..."
9196 assert_same_elements blank_contents, dir_contents(@full_path)
2121 context "A Page" do
2222 setup do
2323 clear_dest
24 @site = Site.new(Jekyll.configuration({
25 "source" => source_dir,
26 "destination" => dest_dir,
27 "skip_config_files" => true,
28 }))
24 @site = Site.new(Jekyll.configuration(
25 "source" => source_dir,
26 "destination" => dest_dir,
27 "skip_config_files" => true
28 ))
2929 end
3030
3131 context "processing pages" do
3434 assert_equal "/contacts.html", @page.url
3535 end
3636
37 should "create proper URL from filename" do
38 @page = setup_page("trailing-dots...md")
39 assert_equal "/trailing-dots.html", @page.url
40 end
41
3742 should "not published when published yaml is false" do
3843 @page = setup_page("unpublished.html")
39 assert_equal false, @page.published?
44 refute @page.published?
4045 end
4146
4247 should "create URL with non-alphabetic characters" do
361366 assert_exist dest_dir("contacts", "bar", "index.html")
362367 end
363368 end
369
370 context "read-in by default" do
371 should "not initialize excerpts by default" do
372 page = setup_page("contacts", "foo.md")
373 assert_nil page.excerpt
374 end
375
376 should "not expose an excerpt to Liquid templates by default" do
377 page = setup_page("/contacts", "bar.html")
378 assert_nil page.to_liquid["excerpt"]
379 end
380
381 context "in a site configured to generate page excerpts" do
382 setup { @configured_site = fixture_site("page_excerpts" => true) }
383
384 should "initialize excerpt eagerly but render only when needed" do
385 test_page = Jekyll::Page.new(@configured_site, source_dir, "contacts", "foo.md")
386 assert_equal Jekyll::PageExcerpt, test_page.data["excerpt"].class
387 assert_equal String, test_page.excerpt.class
388 assert_equal(
389 "<h2 id=\"contact-information\">Contact Information</h2>\n",
390 test_page.excerpt
391 )
392 end
393
394 should "expose an excerpt to Liquid templates" do
395 test_page = Jekyll::Page.new(@configured_site, source_dir, "/contacts", "bar.html")
396 assert_equal "Contact Information\n", test_page.to_liquid["excerpt"]
397 end
398
399 should "not expose an excerpt for non-html pages" do
400 test_page = Jekyll::Page.new(@configured_site, source_dir, "assets", "test-styles.scss")
401 refute_equal ".half { width: 50%; }\n", test_page.to_liquid["excerpt"]
402 assert_nil test_page.to_liquid["excerpt"]
403 end
404 end
405 end
406
407 context "generated via plugin" do
408 setup do
409 PageSubclass = Class.new(Jekyll::Page)
410 @test_page = PageSubclass.new(@site, source_dir, "/contacts", "bar.html")
411 @test_page.data.clear
412 end
413
414 should "not expose an excerpt to Liquid templates by default" do
415 assert_equal "Contact Information\n", @test_page.content
416 assert_nil @test_page.to_liquid["excerpt"]
417 end
418
419 should "expose an excerpt to Liquid templates if hardcoded" do
420 @test_page.data["excerpt"] = "Test excerpt."
421 assert_equal "Contact Information\n", @test_page.content
422 assert_equal "Test excerpt.", @test_page.to_liquid["excerpt"]
423 end
424 end
364425 end
365426 end
366427 end
2020 context "A PageWithoutAFile" do
2121 setup do
2222 clear_dest
23 @site = Site.new(Jekyll.configuration({
24 "source" => source_dir,
25 "destination" => dest_dir,
26 "skip_config_files" => true,
27 }))
23 @site = Site.new(Jekyll.configuration(
24 "source" => source_dir,
25 "destination" => dest_dir,
26 "skip_config_files" => true
27 ))
28 end
29
30 should "have non-frozen path and relative_path attributes" do
31 {
32 ["foo", "bar.md"] => "foo/bar.md",
33 [nil, nil] => "",
34 ["", ""] => "",
35 ["/lorem/", "/ipsum"] => "lorem/ipsum",
36 %w(lorem ipsum) => "lorem/ipsum",
37 }.each do |(dir, name), result|
38 page = PageWithoutAFile.new(@site, @site.source, dir, name)
39 assert_equal result, page.path
40 assert_equal result, page.relative_path
41 refute page.relative_path.frozen?
42 end
2843 end
2944
3045 context "with default site configuration" do
3146 setup do
3247 @page = setup_page("properties.html")
48 end
49
50 should "identify itself properly" do
51 assert_equal '#<Jekyll::PageWithoutAFile @relative_path="properties.html">', @page.inspect
3352 end
3453
3554 should "not have page-content and page-data defined within it" do
4059
4160 should "have basic attributes defined in it" do
4261 regular_page = setup_page("properties.html", :klass => Page)
43 basic_attrs = %w(dir name path url)
62 # assert a couple of attributes accessible in a regular Jekyll::Page instance
63 assert_equal "All the properties.\n", regular_page["content"]
64 assert_equal "properties.html", regular_page["name"]
65
66 basic_attrs = %w(dir name path url excerpt)
4467 attrs = {
45 "content" => "All the properties.\n",
68 "content" => nil,
4669 "dir" => "/",
4770 "excerpt" => nil,
4871 "foo" => "bar",
5578 "url" => "/properties.html",
5679 }
5780 attrs.each do |prop, value|
58 # assert the props being accessible in a Jekyll::Page instance
59 assert_equal "All the properties.\n", regular_page["content"]
60 assert_equal "properties.html", regular_page["name"]
61
62 # assert differences with Jekyll::PageWithoutAFile instance
81 # assert that all attributes (of a Jekyll::PageWithoutAFile instance) other than
82 # "dir", "name", "path", "url" are `nil`.
83 # For example, @page[dir] should be "/" but @page[content] or @page[layout], should
84 # simply be nil.
85 #
6386 if basic_attrs.include?(prop)
64 assert_equal @page[prop], value, "For <page[\"#{prop}\"]>:"
87 assert_equal value, @page[prop], "For Jekyll::PageWithoutAFile attribute '#{prop}':"
6588 else
6689 assert_nil @page[prop]
6790 end
137160
138161 refute_exist dest_dir("physical")
139162 refute_exist dest_dir("virtual-about")
140 refute File.exist?(dest_dir("virtual-about", "index.html"))
163 refute_path_exists(dest_dir("virtual-about", "index.html"))
141164 end
142165
143 should "be processed and written to destination when passed as "\
144 "an entry in 'site.pages' array" do
166 should "be processed and written to destination when passed as an entry in " \
167 "'site.pages' array" do
145168 @page.content = "{{ site.title }}"
146169 @page.data["permalink"] = "/virtual-about/"
147170
150173
151174 refute_exist dest_dir("physical")
152175 assert_exist dest_dir("virtual-about")
153 assert File.exist?(dest_dir("virtual-about", "index.html"))
176 assert_path_exists(dest_dir("virtual-about", "index.html"))
154177 assert_equal "Test Site", File.read(dest_dir("virtual-about", "index.html"))
155178 end
156179 end
0 # frozen_string_literal: true
1
2 require "helper"
3
4 class TestPathManager < JekyllUnitTest
5 context "PathManager" do
6 setup do
7 @source = Dir.pwd
8 end
9
10 should "return frozen copy of base if questionable path is nil" do
11 assert_equal @source, Jekyll::PathManager.sanitized_path(@source, nil)
12 assert Jekyll::PathManager.sanitized_path(@source, nil).frozen?
13 end
14
15 should "return a frozen copy of base if questionable path expands into the base" do
16 assert_equal @source, Jekyll::PathManager.sanitized_path(@source, File.join(@source, "/"))
17 assert Jekyll::PathManager.sanitized_path(@source, File.join(@source, "/")).frozen?
18 end
19
20 should "return a frozen string result" do
21 if Jekyll::Utils::Platforms.really_windows?
22 assert_equal(
23 "#{@source}/_config.yml",
24 Jekyll::PathManager.sanitized_path(@source, "E:\\_config.yml")
25 )
26 end
27 assert_equal(
28 "#{@source}/_config.yml",
29 Jekyll::PathManager.sanitized_path(@source, "//_config.yml")
30 )
31 assert Jekyll::PathManager.sanitized_path(@source, "//_config.yml").frozen?
32 end
33 end
34 end
2828 should "remove path traversals" do
2929 assert_equal source_dir("files", "hi.txt"),
3030 Jekyll.sanitized_path(source_dir, "f./../../../../../../files/hi.txt")
31 end
32
33 should "strip extra slashes in questionable path" do
34 subdir = "/files/"
35 file_path = "/hi.txt"
36 assert_equal source_dir("files", "hi.txt"),
37 Jekyll.sanitized_path(source_dir, "/#{subdir}/#{file_path}")
38 end
39
40 should "handle nil questionable_path" do
41 assert_equal source_dir, Jekyll.sanitized_path(source_dir, nil)
3142 end
3243
3344 if Jekyll::Utils::Platforms.really_windows?
99 FileUtils.mv "Gemfile.old", "Gemfile"
1010 end
1111
12 def with_bundle_gemfile
13 FileUtils.mv "Gemfile", "AlternateGemfile"
14 yield
15 ensure
16 FileUtils.mv "AlternateGemfile", "Gemfile"
17 end
18
1219 context "JEKYLL_NO_BUNDLER_REQUIRE set to `nil`" do
1320 should "require from bundler" do
1421 with_env("JEKYLL_NO_BUNDLER_REQUIRE", nil) do
1926 end
2027 end
2128
29 context "BUNDLE_GEMFILE set to `AlternateGemfile`" do
30 should "require from bundler" do
31 with_env("BUNDLE_GEMFILE", "AlternateGemfile") do
32 with_bundle_gemfile do
33 assert Jekyll::PluginManager.require_from_bundler,
34 "require_from_bundler should return true"
35 assert ENV["JEKYLL_NO_BUNDLER_REQUIRE"], "Gemfile plugins were not required."
36 end
37 end
38 end
39 end
40
2241 context "JEKYLL_NO_BUNDLER_REQUIRE set to `true`" do
2342 should "not require from bundler" do
2443 with_env("JEKYLL_NO_BUNDLER_REQUIRE", "true") do
2544 refute Jekyll::PluginManager.require_from_bundler,
26 "Gemfile plugins were required but shouldn't have been"
45 "Gemfile plugins were required but shouldn't have been"
2746 assert ENV["JEKYLL_NO_BUNDLER_REQUIRE"]
2847 end
2948 end
3453 with_env("JEKYLL_NO_BUNDLER_REQUIRE", nil) do
3554 with_no_gemfile do
3655 refute Jekyll::PluginManager.require_from_bundler,
37 "Gemfile plugins were required but shouldn't have been"
56 "Gemfile plugins were required but shouldn't have been"
3857 assert_nil ENV["JEKYLL_NO_BUNDLER_REQUIRE"]
3958 end
4059 end
6786 end
6887
6988 should "require plugin files" do
70 site = double({ :safe => false,
71 :config => { "plugins_dir" => "_plugins" },
72 :in_source_dir => "/tmp/", })
89 site = double(:safe => false,
90 :config => { "plugins_dir" => "_plugins" },
91 :in_source_dir => "/tmp/")
7392 plugin_manager = PluginManager.new(site)
7493
7594 expect(Jekyll::External).to receive(:require_with_graceful_fail)
7998
8099 context "site is marked as safe" do
81100 should "allow plugins if they are whitelisted" do
82 site = double({ :safe => true, :config => { "whitelist" => ["jemoji"] } })
101 site = double(:safe => true, :config => { "whitelist" => ["jemoji"] })
83102 plugin_manager = PluginManager.new(site)
84103
85104 assert plugin_manager.plugin_allowed?("jemoji")
86 assert !plugin_manager.plugin_allowed?("not_allowed_plugin")
105 refute plugin_manager.plugin_allowed?("not_allowed_plugin")
87106 end
88107
89108 should "not require plugin files" do
90 site = double({ :safe => true })
109 site = double(:safe => true)
91110 plugin_manager = PluginManager.new(site)
92111
93112 expect(Jekyll::External).to_not receive(:require_with_graceful_fail)
97116
98117 context "plugins_dir is set to the default" do
99118 should "call site's in_source_dir" do
100 site = double({
119 site = double(
101120 :config => {
102121 "plugins_dir" => Jekyll::Configuration::DEFAULTS["plugins_dir"],
103122 },
104 :in_source_dir => "/tmp/",
105 })
123 :in_source_dir => "/tmp/"
124 )
106125 plugin_manager = PluginManager.new(site)
107126
108127 expect(site).to receive(:in_source_dir).with("_plugins")
112131
113132 context "plugins_dir is set to a different dir" do
114133 should "expand plugin path" do
115 site = double({ :config => { "plugins_dir" => "some_other_plugins_path" } })
134 site = double(:config => { "plugins_dir" => "some_other_plugins_path" })
116135 plugin_manager = PluginManager.new(site)
117136
118137 expect(File).to receive(:expand_path).with("some_other_plugins_path")
122141
123142 context "`paginate` config is activated" do
124143 should "print deprecation warning if jekyll-paginate is not present" do
125 site = double({ :config => { "paginate" => true } })
144 site = double(:config => { "paginate" => true })
126145 plugin_manager = PluginManager.new(site)
127146
128147 expect(Jekyll::Deprecator).to(
132151 end
133152
134153 should "print no deprecation warning if jekyll-paginate is present" do
135 site = double({
136 :config => { "paginate" => true, "plugins" => ["jekyll-paginate"] },
137 })
154 site = double(
155 :config => { "paginate" => true, "plugins" => ["jekyll-paginate"] }
156 )
138157 plugin_manager = PluginManager.new(site)
139158
140159 expect(Jekyll::Deprecator).to_not receive(:deprecation_message)
143162 end
144163
145164 should "conscientious require" do
146 site = double({
165 site = double(
147166 :config => { "theme" => "test-dependency-theme" },
148 :in_dest_dir => "/tmp/_site/",
149 })
167 :in_dest_dir => "/tmp/_site/"
168 )
150169 plugin_manager = PluginManager.new(site)
151170
152171 expect(site).to receive(:theme).and_return(true)
0 # frozen_string_literal: true
1
2 require "helper"
3
4 class TestPostReader < JekyllUnitTest
5 context "#read_publishable" do
6 setup do
7 @site = Site.new(site_configuration)
8 @post_reader = PostReader.new(@site)
9 @dir = ""
10 @magic_dir = "_posts"
11 @matcher = Document::DATE_FILENAME_MATCHER
12 end
13
14 should "skip unprocessable documents" do
15 all_file_names = all_documents.collect(&:basename)
16 processed_file_names = processed_documents.collect(&:basename)
17
18 actual_skipped_file_names = all_file_names - processed_file_names
19
20 expected_skipped_file_names = [
21 "2008-02-02-not-published.markdown",
22 "2008-02-03-wrong-extension.yml",
23 ]
24
25 skipped_file_names_difference = expected_skipped_file_names - actual_skipped_file_names
26
27 assert expected_skipped_file_names.count.positive?,
28 "There should be at least one document expected to be skipped"
29 assert_empty skipped_file_names_difference,
30 "The skipped documents (expected/actual) should be congruent (= empty array)"
31 end
32 end
33
34 def all_documents
35 @post_reader.read_content(@dir, @magic_dir, @matcher)
36 end
37
38 def processed_documents
39 @post_reader.read_publishable(@dir, @magic_dir, @matcher)
40 end
41 end
+0
-51
test/test_rdiscount.rb less more
0 # frozen_string_literal: true
1
2 require "helper"
3
4 class TestRdiscount < JekyllUnitTest
5 context "rdiscount" do
6 setup do
7 if jruby?
8 then skip(
9 "JRuby does not perform well with CExt, test disabled."
10 )
11 end
12
13 config = {
14 "markdown" => "rdiscount",
15 "rdiscount" => {
16 "toc_token" => "{:toc}",
17 "extensions" => %w(smart generate_toc),
18 },
19 }
20
21 @markdown = Converters::Markdown.new config
22 end
23
24 should "pass rdiscount extensions" do
25 assert_equal "<p>&ldquo;smart&rdquo;</p>", @markdown.convert('"smart"').strip
26 end
27
28 should "render toc" do
29 toc = <<-TOC
30 <a name="Header.1"></a>
31 <h1>Header 1</h1>
32
33 <a name="Header.2"></a>
34 <h2>Header 2</h2>
35
36 <p><ul>
37 <li><a href="#Header.1">Header 1</a>
38 <ul>
39 <li><a href="#Header.2">Header 2</a></li>
40 </ul>
41 </li>
42 </ul>
43
44 </p>
45 TOC
46 assert_equal toc.strip,
47 @markdown.convert("# Header 1\n\n## Header 2\n\n{:toc}").strip
48 end
49 end
50 end
+0
-94
test/test_redcarpet.rb less more
0 # frozen_string_literal: true
1
2 require "helper"
3
4 class TestRedcarpet < JekyllUnitTest
5 context "redcarpet" do
6 setup do
7 if jruby?
8 then skip(
9 "JRuby does not perform well with CExt, test disabled."
10 )
11 end
12
13 @config = {
14 "markdown" => "redcarpet",
15 "redcarpet" => {
16 "extensions" => %w(smart strikethrough filter_html),
17 },
18 }
19
20 @markdown = Converters::Markdown.new @config
21
22 @sample = Jekyll::Utils.strip_heredoc(<<-EOS
23 ```ruby
24 puts "Hello world"
25 ```
26 EOS
27 )
28 end
29
30 should "pass redcarpet options" do
31 assert_equal "<h1>Some Header</h1>", @markdown.convert("# Some Header #").strip
32 end
33
34 should "pass redcarpet SmartyPants options" do
35 assert_equal "<p>&ldquo;smart&rdquo;</p>", @markdown.convert('"smart"').strip
36 end
37
38 should "pass redcarpet extensions" do
39 assert_equal "<p><del>deleted</del></p>", @markdown.convert("~~deleted~~").strip
40 end
41
42 should "pass redcarpet render options" do
43 assert_equal "<p><strong>bad code not here</strong>: i am bad</p>",
44 @markdown.convert("**bad code not here**: <script>i am bad</script>").strip
45 end
46
47 context "with pygments enabled" do
48 setup do
49 @markdown = Converters::Markdown.new @config.merge(
50 { "highlighter" => "pygments" }
51 )
52 end
53
54 should "render fenced code blocks with syntax highlighting" do
55 assert_equal(
56 %(<div class="highlight"><pre><code class="language-ruby" ) +
57 %(data-lang="ruby"><span></span><span class="nb">puts</span> <span ) +
58 %(class="s2">&quot;Hello world&quot;</span>\n</code></pre></div>),
59 @markdown.convert(@sample).strip
60 )
61 end
62 end
63
64 context "with rouge enabled" do
65 setup do
66 @markdown = Converters::Markdown.new @config.merge({ "highlighter" => "rouge" })
67 end
68
69 should "render fenced code blocks with syntax highlighting" do
70 assert_equal(
71 %(<div class="highlight"><pre><code class="language-ruby" ) +
72 %(data-lang="ruby"><span class="nb">puts</span> <span ) +
73 %(class="s2">"Hello world"</span>\n</code></pre></div>),
74 @markdown.convert(@sample).strip
75 )
76 end
77 end
78
79 context "without any highlighter" do
80 setup do
81 @markdown = Converters::Markdown.new @config.merge({ "highlighter" => nil })
82 end
83
84 should "render fenced code blocks without syntax highlighting" do
85 assert_equal(
86 %(<figure class="highlight"><pre><code class="language-ruby" ) +
87 %(data-lang="ruby">puts &quot;Hello world&quot;\n</code></pre></figure>),
88 @markdown.convert(@sample).strip
89 )
90 end
91 end
92 end
93 end
66 setup do
77 FileUtils.rm_rf(source_dir(".jekyll-metadata"))
88
9 @site = fixture_site({
9 @site = fixture_site(
1010 "collections" => {
1111 "methods" => {
1212 "output" => true,
1313 },
1414 },
15 "incremental" => true,
16 })
15 "incremental" => true
16 )
1717
1818 @site.read
1919 @page = @site.pages.first
4141 # because regenerate? checks if the destination exists
4242 [@page, @post, @document, @asset_file].each do |item|
4343 next unless item.respond_to?(:destination)
44
4445 dest = item.destination(@site.dest)
4546 FileUtils.mkdir_p(File.dirname(dest))
4647 FileUtils.touch(dest)
5051
5152 # these should pass, since nothing has changed, and the
5253 # loop above made sure the designations exist
53 assert !@regenerator.regenerate?(@page)
54 assert !@regenerator.regenerate?(@post)
55 assert !@regenerator.regenerate?(@document)
54 refute @regenerator.regenerate?(@page)
55 refute @regenerator.regenerate?(@post)
56 refute @regenerator.regenerate?(@document)
5657 end
5758
5859 should "regenerate if destination missing" do
9293 context "The site regenerator" do
9394 setup do
9495 FileUtils.rm_rf(source_dir(".jekyll-metadata"))
95 @site = fixture_site({
96 "incremental" => true,
97 })
96 @site = fixture_site(
97 "incremental" => true
98 )
9899
99100 @site.read
100101 @post = @site.posts.first
116117 @regenerator.add_dependency(path, @layout_path)
117118
118119 File.rename(@layout_path, @layout_path + ".tmp")
119 refute File.exist?(@layout_path)
120 refute_path_exists(@layout_path)
120121
121122 @regenerator.clear_cache
122123 assert @regenerator.regenerate?(@post)
127128 setup do
128129 FileUtils.rm_rf(source_dir(".jekyll-metadata"))
129130
130 @site = Site.new(Jekyll.configuration({
131 "source" => source_dir,
132 "destination" => dest_dir,
133 "incremental" => true,
134 }))
131 @site = Site.new(Jekyll.configuration(
132 "source" => source_dir,
133 "destination" => dest_dir,
134 "incremental" => true
135 ))
135136
136137 @site.process
137138 @path = @site.in_source_dir(@site.pages.first.path)
152153 assert @regenerator.cache[@path]
153154
154155 @regenerator.clear_cache
155 assert_equal @regenerator.cache, {}
156 expected = {}
157 assert_equal expected, @regenerator.cache
156158 end
157159
158160 should "write to the metadata file" do
171173 metadata_file = source_dir(".jekyll-metadata")
172174 @regenerator = Regenerator.new(@site)
173175
174 File.open(metadata_file, "w") do |f|
175 f.write(@regenerator.metadata.to_yaml)
176 end
176 File.write(metadata_file, @regenerator.metadata.to_yaml)
177177
178178 @regenerator = Regenerator.new(@site)
179179 assert_equal File.mtime(@path), @regenerator.metadata[@path]["mtime"]
182182 should "not crash when reading corrupted marshal file" do
183183 metadata_file = source_dir(".jekyll-metadata")
184184 File.open(metadata_file, "w") do |file|
185 file.puts Marshal.dump({ :foo => "bar" })[0, 5]
185 file.puts Marshal.dump(:foo => "bar")[0, 5]
186186 end
187187
188188 @regenerator = Regenerator.new(@site)
228228 @regenerator.write_metadata
229229 @regenerator = Regenerator.new(@site)
230230
231 assert !@regenerator.modified?(@path)
231 refute @regenerator.modified?(@path)
232232 end
233233
234234 should "not regenerate if path in cache is false" do
237237 @regenerator.write_metadata
238238 @regenerator = Regenerator.new(@site)
239239
240 assert !@regenerator.modified?(@path)
241 assert !@regenerator.cache[@path]
242 assert !@regenerator.modified?(@path)
240 refute @regenerator.modified?(@path)
241 refute @regenerator.cache[@path]
242 refute @regenerator.modified?(@path)
243243 end
244244
245245 should "regenerate if path in not in metadata" do
309309 context "when incremental regeneration is disabled" do
310310 setup do
311311 FileUtils.rm_rf(source_dir(".jekyll-metadata"))
312 @site = Site.new(Jekyll.configuration({
313 "source" => source_dir,
314 "destination" => dest_dir,
315 "incremental" => false,
316 }))
312 @site = Site.new(Jekyll.configuration(
313 "source" => source_dir,
314 "destination" => dest_dir,
315 "incremental" => false
316 ))
317317
318318 @site.process
319319 @path = @site.in_source_dir(@site.pages.first.path)
2828 end
2929
3030 allow_any_instance_of(Jekyll::RelatedPosts).to receive(:display)
31 @site = fixture_site({
32 "lsi" => true,
33 })
31 @site = fixture_site(
32 "lsi" => true
33 )
3434
3535 @site.reset
3636 @site.read
44 class TestSass < JekyllUnitTest
55 context "importing partials" do
66 setup do
7 @site = Jekyll::Site.new(Jekyll.configuration({
8 "source" => source_dir,
9 "destination" => dest_dir,
10 }))
7 @site = Jekyll::Site.new(Jekyll.configuration(
8 "source" => source_dir,
9 "destination" => dest_dir
10 ))
1111 @site.process
1212 @test_css_file = dest_dir("css/main.css")
1313 end
1414
1515 should "import SCSS partial" do
16 assert_equal ".half {\n width: 50%; }\n", File.read(@test_css_file)
16 result = <<~CSS
17 .half { width: 50%; }
18
19 /*# sourceMappingURL=main.css.map */
20 CSS
21 assert_equal result.rstrip, File.read(@test_css_file)
1722 end
1823
1924 should "register the SCSS converter" do
2025 message = "SCSS converter implementation should exist."
21 assert !!@site.find_converter_instance(Jekyll::Converters::Scss), message
26 refute !@site.find_converter_instance(Jekyll::Converters::Scss), message
2227 end
2328
2429 should "register the Sass converter" do
2530 message = "Sass converter implementation should exist."
26 assert !!@site.find_converter_instance(Jekyll::Converters::Sass), message
31 refute !@site.find_converter_instance(Jekyll::Converters::Sass), message
2732 end
2833 end
2934 end
1414 @site.posts.docs.concat(PostReader.new(@site).read_posts(""))
1515 posts = Dir[source_dir("_posts", "**", "*")]
1616 posts.delete_if do |post|
17 File.directory?(post) && !(post =~ Document::DATE_FILENAME_MATCHER)
17 File.directory?(post) && post !~ Document::DATE_FILENAME_MATCHER
1818 end
1919 end
2020
3030 end
3131
3232 should "have an array for plugins if passed as a string" do
33 site = Site.new(site_configuration({ "plugins_dir" => "/tmp/plugins" }))
34 array = Utils::Platforms.windows? ? ["C:/tmp/plugins"] : ["/tmp/plugins"]
33 site = Site.new(site_configuration("plugins_dir" => "/tmp/plugins"))
34 array = [temp_dir("plugins")]
3535 assert_equal array, site.plugins
3636 end
3737
3838 should "have an array for plugins if passed as an array" do
39 site = Site.new(site_configuration({
40 "plugins_dir" => ["/tmp/plugins", "/tmp/otherplugins"],
41 }))
42 array = if Utils::Platforms.windows?
43 ["C:/tmp/plugins", "C:/tmp/otherplugins"]
44 else
45 ["/tmp/plugins", "/tmp/otherplugins"]
46 end
39 site = Site.new(site_configuration(
40 "plugins_dir" => ["/tmp/plugins", "/tmp/otherplugins"]
41 ))
42 array = [temp_dir("plugins"), temp_dir("otherplugins")]
4743 assert_equal array, site.plugins
4844 end
4945
5046 should "have an empty array for plugins if nothing is passed" do
51 site = Site.new(site_configuration({ "plugins_dir" => [] }))
47 site = Site.new(site_configuration("plugins_dir" => []))
5248 assert_equal [], site.plugins
5349 end
5450
5551 should "have the default for plugins if nil is passed" do
56 site = Site.new(site_configuration({ "plugins_dir" => nil }))
52 site = Site.new(site_configuration("plugins_dir" => nil))
5753 assert_equal [source_dir("_plugins")], site.plugins
5854 end
5955
6359 end
6460
6561 should "expose baseurl passed in from config" do
66 site = Site.new(site_configuration({ "baseurl" => "/blog" }))
62 site = Site.new(site_configuration("baseurl" => "/blog"))
6763 assert_equal "/blog", site.baseurl
6864 end
6965
7066 should "only include theme includes_path if the path exists" do
71 site = fixture_site({ "theme" => "test-theme" })
67 site = fixture_site("theme" => "test-theme")
7268 assert_equal [source_dir("_includes"), theme_dir("_includes")],
73 site.includes_load_paths
69 site.includes_load_paths
7470
7571 allow(File).to receive(:directory?).with(theme_dir("_sass")).and_return(true)
7672 allow(File).to receive(:directory?).with(theme_dir("_layouts")).and_return(true)
7773 allow(File).to receive(:directory?).with(theme_dir("_includes")).and_return(false)
78 site = fixture_site({ "theme" => "test-theme" })
74 site = fixture_site("theme" => "test-theme")
7975 assert_equal [source_dir("_includes")], site.includes_load_paths
8076 end
77
78 should "configure cache_dir" do
79 fixture_site.process
80 assert File.directory?(source_dir(".jekyll-cache", "Jekyll", "Cache"))
81 assert File.directory?(source_dir(".jekyll-cache", "Jekyll", "Cache", "Jekyll--Cache"))
82 end
83
84 should "use .jekyll-cache directory at source as cache_dir by default" do
85 site = Site.new(default_configuration)
86 assert_equal File.join(site.source, ".jekyll-cache"), site.cache_dir
87 end
88
89 should "have the cache_dir hidden from Git" do
90 site = fixture_site
91 assert_equal site.source, source_dir
92 assert_exist source_dir(".jekyll-cache", ".gitignore")
93 assert_equal(
94 "# ignore everything in this directory\n*\n",
95 File.binread(source_dir(".jekyll-cache", ".gitignore"))
96 )
97 end
98
99 should "load config file from theme-gem as Jekyll::Configuration instance" do
100 site = fixture_site("theme" => "test-theme")
101 assert_instance_of Jekyll::Configuration, site.config
102 assert_equal "Hello World", site.config["title"]
103 end
104
105 context "with a custom cache_dir configuration" do
106 should "have the custom cache_dir hidden from Git" do
107 site = fixture_site("cache_dir" => "../../custom-cache-dir")
108 refute_exist File.expand_path("../../custom-cache-dir/.gitignore", site.source)
109 assert_exist source_dir("custom-cache-dir", ".gitignore")
110 assert_equal(
111 "# ignore everything in this directory\n*\n",
112 File.binread(source_dir("custom-cache-dir", ".gitignore"))
113 )
114 end
115 end
81116 end
117
82118 context "creating sites" do
83119 setup do
84120 @site = Site.new(site_configuration)
85 @num_invalid_posts = 4
121 @num_invalid_posts = 6
86122 end
87123
88124 teardown do
89 if defined?(MyGenerator)
90 self.class.send(:remove_const, :MyGenerator)
91 end
125 self.class.send(:remove_const, :MyGenerator) if defined?(MyGenerator)
92126 end
93127
94128 should "have an empty tag hash by default" do
180214
181215 # simulate destination file deletion
182216 File.unlink dest
183 refute File.exist?(dest)
217 refute_path_exists(dest)
184218
185219 sleep 1
186220 @site.process
212246 @site.process
213247 # exclude files in symlinked directories here and insert them in the
214248 # following step when not on Windows.
249 # rubocop:disable Style/WordArray
215250 sorted_pages = %w(
216251 %#\ +.md
217252 .htaccess
225260 environment.html
226261 exploit.md
227262 foo.md
263 foo.md
228264 humans.txt
229265 index.html
230266 index.html
231267 info.md
268 main.css.map
232269 main.scss
233270 properties.html
234271 sitemap.xml
235272 static_files.html
273 test-styles.css.map
274 test-styles.scss
275 trailing-dots...md
236276 )
277 # rubocop:enable Style/WordArray
237278 unless Utils::Platforms.really_windows?
238279 # files in symlinked directories may appear twice
239 sorted_pages.push("main.scss", "symlinked-file").sort!
240 end
241 assert_equal sorted_pages, @site.pages.map(&:name)
280 sorted_pages.push("main.css.map", "main.scss", "symlinked-file").sort!
281 end
282 assert_equal sorted_pages, @site.pages.map(&:name).sort!
242283 end
243284
244285 should "read posts" do
256297
257298 should "read pages with YAML front matter" do
258299 abs_path = File.expand_path("about.html", @site.source)
259 assert_equal true, Utils.has_yaml_header?(abs_path)
300 assert Utils.has_yaml_header?(abs_path)
260301 end
261302
262303 should "enforce a strict 3-dash limit on the start of the YAML front matter" do
263304 abs_path = File.expand_path("pgp.key", @site.source)
264 assert_equal false, Utils.has_yaml_header?(abs_path)
305 refute Utils.has_yaml_header?(abs_path)
265306 end
266307
267308 should "expose jekyll version to site payload" do
278319
279320 posts = Dir[source_dir("**", "_posts", "**", "*")]
280321 posts.delete_if do |post|
281 File.directory?(post) && !(post =~ Document::DATE_FILENAME_MATCHER)
322 File.directory?(post) && post !~ Document::DATE_FILENAME_MATCHER
282323 end
283324 categories = %w(
284325 2013 bar baz category foo z_category MixedCase Mixedcase publish_test win
304345
305346 should "raise for bad frontmatter if strict_front_matter is set" do
306347 site = Site.new(site_configuration(
307 "collections" => ["broken"],
308 "strict_front_matter" => true
309 ))
348 "collections" => ["broken"],
349 "strict_front_matter" => true
350 ))
310351 assert_raises(Psych::SyntaxError) do
311352 site.process
312353 end
314355
315356 should "not raise for bad frontmatter if strict_front_matter is not set" do
316357 site = Site.new(site_configuration(
317 "collections" => ["broken"],
318 "strict_front_matter" => false
319 ))
358 "collections" => ["broken"],
359 "strict_front_matter" => false
360 ))
320361 site.process
321362 end
322363 end
411452
412453 bad_processor = "Custom::Markdown"
413454 s = Site.new(site_configuration(
414 "markdown" => bad_processor,
415 "incremental" => false
416 ))
455 "markdown" => bad_processor,
456 "incremental" => false
457 ))
417458 assert_raises Jekyll::Errors::FatalException do
418459 s.process
419460 end
432473 should "throw FatalException at process time" do
433474 bad_processor = "not a processor name"
434475 s = Site.new(site_configuration(
435 "markdown" => bad_processor,
436 "incremental" => false
437 ))
476 "markdown" => bad_processor,
477 "incremental" => false
478 ))
438479 assert_raises Jekyll::Errors::FatalException do
439480 s.process
440481 end
488529 site.process
489530
490531 file_content = SafeYAML.load_file(File.join(
491 source_dir, "_data", "categories", "dairy.yaml"
492 ))
532 source_dir, "_data", "categories", "dairy.yaml"
533 ))
493534
494535 assert_equal site.data["categories"]["dairy"], file_content
495536 assert_equal(
503544 site.process
504545
505546 file_content = SafeYAML.load_file(File.join(
506 source_dir, "_data", "categories.01", "dairy.yaml"
507 ))
547 source_dir, "_data", "categories.01", "dairy.yaml"
548 ))
508549
509550 assert_equal site.data["categories01"]["dairy"], file_content
510551 assert_equal(
535576
536577 context "manipulating the Jekyll environment" do
537578 setup do
538 @site = Site.new(site_configuration({
539 "incremental" => false,
540 }))
579 @site = Site.new(site_configuration(
580 "incremental" => false
581 ))
541582 @site.process
542583 @page = @site.pages.find { |p| p.name == "environment.html" }
543584 end
549590 context "in production" do
550591 setup do
551592 ENV["JEKYLL_ENV"] = "production"
552 @site = Site.new(site_configuration({
553 "incremental" => false,
554 }))
593 @site = Site.new(site_configuration(
594 "incremental" => false
595 ))
555596 @site.process
556597 @page = @site.pages.find { |p| p.name == "environment.html" }
557598 end
570611 should "set no theme if config is not set" do
571612 expect($stderr).not_to receive(:puts)
572613 expect($stdout).not_to receive(:puts)
573 site = fixture_site({ "theme" => nil })
614 site = fixture_site("theme" => nil)
574615 assert_nil site.theme
575616 end
576617
577618 should "set no theme if config is a hash" do
578619 output = capture_output do
579 site = fixture_site({ "theme" => {} })
620 site = fixture_site("theme" => {})
580621 assert_nil site.theme
581622 end
582 expected_msg = "Theme: value of 'theme' in config should be String " \
583 "to use gem-based themes, but got Hash\n"
623 expected_msg = "Theme: value of 'theme' in config should be String to use " \
624 "gem-based themes, but got Hash\n"
584625 assert_includes output, expected_msg
585626 end
586627
587628 should "set a theme if the config is a string" do
588629 [:debug, :info, :warn, :error].each do |level|
589 expect(Jekyll.logger.writer).not_to receive(level)
590 end
591 site = fixture_site({ "theme" => "test-theme" })
630 if level == :info
631 expect(Jekyll.logger.writer).to receive(level)
632 else
633 expect(Jekyll.logger.writer).not_to receive(level)
634 end
635 end
636 site = fixture_site("theme" => "test-theme")
592637 assert_instance_of Jekyll::Theme, site.theme
593638 assert_equal "test-theme", site.theme.name
594639 end
615660
616661 context "incremental build" do
617662 setup do
618 @site = Site.new(site_configuration({
619 "incremental" => true,
620 }))
663 @site = Site.new(site_configuration(
664 "incremental" => true
665 ))
621666 @site.read
622667 end
623668
667712 refute_equal mtime1, mtime2 # must be regenerated
668713 end
669714 end
715
716 context "#in_cache_dir method" do
717 setup do
718 @site = Site.new(
719 site_configuration(
720 "cache_dir" => "../../custom-cache-dir"
721 )
722 )
723 end
724
725 should "create sanitized paths within the cache directory" do
726 assert_equal File.join(@site.source, "custom-cache-dir"), @site.cache_dir
727 assert_equal(
728 File.join(@site.source, "custom-cache-dir", "foo.md.metadata"),
729 @site.in_cache_dir("../../foo.md.metadata")
730 )
731 end
732 end
733 end
734
735 context "site process phases" do
736 should "return nil as documented" do
737 site = fixture_site
738 [:reset, :read, :generate, :render, :cleanup, :write].each do |phase|
739 assert_nil site.send(phase)
740 end
741 end
742 end
743
744 context "static files in a collection" do
745 should "be exposed via site instance" do
746 site = fixture_site("collections" => ["methods"])
747 site.read
748
749 assert_includes site.static_files.map(&:relative_path), "_methods/extensionless_static_file"
750 end
751
752 should "not be revisited in `Site#each_site_file`" do
753 site = fixture_site("collections" => { "methods" => { "output" => true } })
754 site.read
755
756 visited_files = []
757 site.each_site_file { |file| visited_files << file }
758 assert_equal visited_files.count, visited_files.uniq.count
759 end
670760 end
671761 end
44 class TestSiteDrop < JekyllUnitTest
55 context "a site drop" do
66 setup do
7 @site = fixture_site({
8 "collections" => ["thanksgiving"],
9 })
7 @site = fixture_site(
8 "collections" => ["thanksgiving"]
9 )
1010 @site.process
1111 @drop = @site.to_liquid.site
1212 end
1313
1414 should "respond to `key?`" do
15 assert @drop.respond_to?(:key?)
15 assert_respond_to @drop, :key?
1616 end
1717
1818 should "find a key if it's in the collection of the drop" do
4747 remove_dummy_file(@filename) if File.exist?(source_dir(@filename))
4848 end
4949
50 should "return a simple string on inspection" do
51 static_file = setup_static_file("root", "dir", @filename)
52 assert_equal "#<Jekyll::StaticFile @relative_path=\"dir/#{@filename}\">", static_file.inspect
53 end
54
5055 should "have a source file path" do
5156 static_file = setup_static_file("root", "dir", @filename)
5257 assert_equal "root/dir/#{@filename}", static_file.path
6974 "root",
7075 "_foo/dir/subdir",
7176 "file.html",
72 { "output" => true }
77 "output" => true
7378 )
7479 assert_equal :foo, static_file.type
7580 assert_equal "/foo/dir/subdir/file.html", static_file.url
8186 "root",
8287 "_foo/dir/subdir",
8388 "file.html",
84 { "output" => true, "permalink" => "/:path/" }
89 "output" => true, "permalink" => "/:path/"
8590 )
8691 assert_equal :foo, static_file.type
8792 assert_equal "/dir/subdir/file.html", static_file.url
9196 should "be writable by default" do
9297 static_file = setup_static_file("root", "dir/subdir", "file.html")
9398 assert(static_file.write?,
94 "static_file.write? should return true by default")
99 "static_file.write? should return true by default")
95100 end
96101
97102 should "use the _config.yml defaults to determine writability" do
98103 defaults = [{
99104 "scope" => { "path" => "private" },
100105 "values" => { "published" => false },
101 },]
106 }]
102107 static_file = setup_static_file_with_defaults(
103108 "root",
104109 "private/dir/subdir",
105110 "file.html",
106111 defaults
107112 )
108 assert(!static_file.write?,
109 "static_file.write? should return false when _config.yml sets " \
110 "`published: false`")
113 refute(static_file.write?,
114 "static_file.write? should return false when _config.yml sets " \
115 "`published: false`")
111116 end
112117
113118 should "respect front matter defaults" do
114119 defaults = [{
115120 "scope" => { "path" => "" },
116121 "values" => { "front-matter" => "default" },
117 },]
122 }]
118123
119124 static_file = setup_static_file_with_defaults "", "", "file.pdf", defaults
120125 assert_equal "default", static_file.data["front-matter"]
124129 defaults = [{
125130 "scope" => { "path" => "" },
126131 "values" => { "front-matter" => "default" },
127 },]
132 }]
128133
129134 static_file = setup_static_file_with_defaults "", "", "file.pdf", defaults
130135 hash = static_file.to_liquid
153158 should "known if the source path is modified, when it's not" do
154159 @static_file.write(dest_dir)
155160 sleep 1 # wait, else the times are still the same
156 assert !@static_file.modified?
161 refute @static_file.modified?
157162 end
158163
159164 should "known whether to write the file to the filesystem" do
66 FileUtils.mkdir_p("tmp")
77 end
88
9 # rubocop:disable Metrics/AbcSize
109 def create_post(content, override = {}, converter_class = Jekyll::Converters::Markdown)
1110 site = fixture_site({ "highlighter" => "rouge" }.merge(override))
1211
1514 site.read if override["read_all"]
1615
1716 info = { :filters => [Jekyll::Filters], :registers => { :site => site } }
18 @converter = site.converters.find { |c| c.class == converter_class }
17 @converter = site.converters.find { |c| c.instance_of?(converter_class) }
1918 payload = { "highlighter_prefix" => @converter.highlighter_prefix,
2019 "highlighter_suffix" => @converter.highlighter_suffix, }
2120
2221 @result = Liquid::Template.parse(content).render!(payload, info)
2322 @result = @converter.convert(@result)
2423 end
25 # rubocop:enable Metrics/AbcSize
2624
2725 def fill_post(code, override = {})
28 content = <<CONTENT
29 ---
30 title: This is a test
31 ---
32
33 This document has some highlighted code in it.
34
35 {% highlight text %}
36 #{code}
37 {% endhighlight %}
38 {% highlight text linenos %}
39 #{code}
40 {% endhighlight %}
41 CONTENT
26 content = <<~CONTENT
27 ---
28 title: This is a test
29 ---
30
31 This document has some highlighted code in it.
32
33 {% highlight text %}
34 #{code}
35 {% endhighlight %}
36 {% highlight text linenos %}
37 #{code}
38 {% endhighlight %}
39 CONTENT
4240 create_post(content, override)
4341 end
4442
133131 end
134132 end
135133
136 context "in safe mode" do
137 setup do
138 @tag = highlight_block_with_opts("text ")
139 end
140
141 should "allow linenos" do
142 sanitized = @tag.sanitized_opts({ :linenos => true }, true)
143 assert_equal true, sanitized[:linenos]
144 end
145
146 should "allow hl_lines" do
147 sanitized = @tag.sanitized_opts({ :hl_lines => %w(1 2 3 4) }, true)
148 assert_equal %w(1 2 3 4), sanitized[:hl_lines]
149 end
150
151 should "allow cssclass" do
152 sanitized = @tag.sanitized_opts({ :cssclass => "ahoy" }, true)
153 assert_equal "ahoy", sanitized[:cssclass]
154 end
155
156 should "allow startinline" do
157 sanitized = @tag.sanitized_opts({ :startinline => true }, true)
158 assert_equal true, sanitized[:startinline]
159 end
160
161 should "strip unknown options" do
162 sanitized = @tag.sanitized_opts({ :light => true }, true)
163 assert_nil sanitized[:light]
164 end
165 end
166
167 context "with the pygments highlighter" do
168 setup do
169 if jruby?
170 then skip(
171 "JRuby does not support Pygments."
172 )
173 end
174 end
175
176 context "post content has highlight tag" do
177 setup do
178 fill_post("test", { "highlighter" => "pygments" })
179 end
180
181 should "not cause a markdown error" do
182 refute_match(%r!markdown\-html\-error!, @result)
183 end
184
185 should "render markdown with pygments" do
186 assert_match(
187 %(<pre><code class="language-text" data-lang="text">) +
188 %(<span></span>test</code></pre>),
189 @result
190 )
191 end
192
193 should "render markdown with pygments with line numbers" do
194 assert_match(
195 %(<pre><code class="language-text" data-lang="text">) +
196 %(<span></span><span class="lineno">1 </span>test</code></pre>),
197 @result
198 )
199 end
200 end
201
202 context "post content has highlight with file reference" do
203 setup do
204 fill_post("./jekyll.gemspec", { "highlighter" => "pygments" })
205 end
206
207 should "not embed the file" do
208 assert_match(
209 %(<pre><code class="language-text" data-lang="text"><span></span>) +
210 %(./jekyll.gemspec</code></pre>),
211 @result
212 )
213 end
214 end
215
216 context "post content has highlight tag with UTF character" do
217 setup do
218 fill_post("Æ", { "highlighter" => "pygments" })
219 end
220
221 should "render markdown with pygments line handling" do
222 assert_match(
223 %(<pre><code class="language-text" data-lang="text">) +
224 %(<span></span>Æ</code></pre>),
225 @result
226 )
227 end
228 end
229
230 context "post content has highlight tag with preceding spaces & lines" do
231 setup do
232 code = <<-EOS
233
234
235 [,1] [,2]
236 [1,] FALSE TRUE
237 [2,] FALSE TRUE
238 EOS
239 fill_post(code, { "highlighter" => "pygments" })
240 end
241
242 should "only strip the preceding newlines" do
243 assert_match(
244 %(<pre><code class=\"language-text\" data-lang=\"text\">) +
245 %(<span></span> [,1] [,2]),
246 @result
247 )
248 end
249 end
250
251 context "post content has highlight tag " \
252 "with preceding spaces & lines in several places" do
253 setup do
254 code = <<-EOS
255
256
257 [,1] [,2]
258
259
260 [1,] FALSE TRUE
261 [2,] FALSE TRUE
262
263
264 EOS
265 fill_post(code, { "highlighter" => "pygments" })
266 end
267
268 should "only strip the newlines which precede and succeed the entire block" do
269 assert_match(
270 %(<pre><code class=\"language-text\" data-lang=\"text\"><span></span>) +
271 %( [,1] [,2]\n\n\n[1,] FALSE TRUE\n[2,] FALSE TRUE</code></pre>),
272 @result
273 )
274 end
275 end
276
277 context "post content has highlight tag with " \
278 "preceding spaces & Windows-style newlines" do
279 setup do
280 fill_post "\r\n\r\n\r\n [,1] [,2]", { "highlighter" => "pygments" }
281 end
282
283 should "only strip the preceding newlines" do
284 assert_match(
285 %(<pre><code class="language-text" data-lang="text"><span></span>) +
286 %( [,1] [,2]),
287 @result
288 )
289 end
290 end
291
292 context "post content has highlight tag with only preceding spaces" do
293 setup do
294 code = <<-EOS
295 [,1] [,2]
296 [1,] FALSE TRUE
297 [2,] FALSE TRUE
298 EOS
299 fill_post(code, { "highlighter" => "pygments" })
300 end
301
302 should "only strip the preceding newlines" do
303 assert_match(
304 %(<pre><code class=\"language-text\" data-lang=\"text\"><span></span>) +
305 %( [,1] [,2]),
306 @result
307 )
308 end
309 end
310 end
311
312134 context "with the rouge highlighter" do
313135 context "post content has highlight tag" do
314136 setup do
322144 )
323145 end
324146
325 should "render markdown with rouge 2 with line numbers" do
326 skip "Skipped because using an older version of Rouge" if Utils::Rouge.old_api?
147 should "render markdown with rouge with line numbers" do
327148 assert_match(
328149 %(<table class="rouge-table"><tbody>) +
329150 %(<tr><td class="gutter gl">) +
333154 @result
334155 )
335156 end
336
337 should "render markdown with rouge 1 with line numbers" do
338 skip "Skipped because using a newer version of Rouge" unless Utils::Rouge.old_api?
339 assert_match(
340 %(<table style="border-spacing: 0"><tbody>) +
341 %(<tr><td class="gutter gl" style="text-align: right">) +
342 %(<pre class="lineno">1</pre></td>) +
343 %(<td class="code"><pre>test<span class="w">\n</span></pre></td></tr>) +
344 %(</tbody></table>),
345 @result
346 )
347 end
348157 end
349158
350159 context "post content has raw tag" do
351160 setup do
352 content = <<-CONTENT
353 ---
354 title: This is a test
355 ---
356
357 ```liquid
358 {% raw %}
359 {{ site.baseurl }}{% link _collection/name-of-document.md %}
360 {% endraw %}
361 ```
362 CONTENT
161 content = <<~CONTENT
162 ---
163 title: This is a test
164 ---
165
166 ```liquid
167 {% raw %}
168 {{ site.baseurl }}{% link _collection/name-of-document.md %}
169 {% endraw %}
170 ```
171 CONTENT
363172 create_post(content)
364173 end
365174
366 should "render markdown with rouge 1" do
367 skip "Skipped because using a newer version of Rouge" unless Utils::Rouge.old_api?
368
369 assert_match(
370 %(<div class="language-liquid highlighter-rouge"><pre class="highlight"><code>),
371 @result
372 )
373 end
374
375 should "render markdown with rouge 2" do
376 skip "Skipped because using an older version of Rouge" if Utils::Rouge.old_api?
377
175 should "render markdown with rouge" do
378176 assert_match(
379177 %(<div class="language-liquid highlighter-rouge">) +
380178 %(<div class="highlight"><pre class="highlight"><code>),
412210
413211 context "post content has highlight tag with preceding spaces & lines" do
414212 setup do
415 fill_post <<-EOS
416
417
418 [,1] [,2]
419 [1,] FALSE TRUE
420 [2,] FALSE TRUE
421 EOS
213 fill_post <<~EOS
214
215
216 [,1] [,2]
217 [1,] FALSE TRUE
218 [2,] FALSE TRUE
219 EOS
422220 end
423221
424222 should "only strip the preceding newlines" do
432230 context "post content has highlight tag with " \
433231 "preceding spaces & lines in several places" do
434232 setup do
435 fill_post <<-EOS
436
437
438 [,1] [,2]
439
440
441 [1,] FALSE TRUE
442 [2,] FALSE TRUE
443
444
445 EOS
233 fill_post <<~EOS
234
235
236 [,1] [,2]
237
238
239 [1,] FALSE TRUE
240 [2,] FALSE TRUE
241
242
243 EOS
446244 end
447245
448246 should "only strip the newlines which precede and succeed the entire block" do
456254
457255 context "post content has highlight tag with linenumbers" do
458256 setup do
459 create_post <<-EOS
460 ---
461 title: This is a test
462 ---
463
464 This is not yet highlighted
465 {% highlight php linenos %}
466 test
467 {% endhighlight %}
468
469 This should not be highlighted, right?
470 EOS
471 end
472
473 should "should stop highlighting at boundary with rouge 2" do
474 skip "Skipped because using an older version of Rouge" if Utils::Rouge.old_api?
475 expected = <<-EOS
476 <p>This is not yet highlighted</p>\n
477 <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
478 </pre></td><td class="code"><pre><span class="nx">test</span>\n</pre></td></tr></tbody></table></code></pre></figure>\n
479 <p>This should not be highlighted, right?</p>
480 EOS
481 assert_match(expected, @result)
482 end
483
484 should "should stop highlighting at boundary with rouge 1" do
485 skip "Skipped because using a newer version of Rouge" unless Utils::Rouge.old_api?
486 expected = <<-EOS
487 <p>This is not yet highlighted</p>\n
488 <figure class="highlight"><pre><code class="language-php" data-lang="php"><table style="border-spacing: 0"><tbody><tr><td class="gutter gl" style="text-align: right"><pre class="lineno">1</pre></td><td class="code"><pre>test<span class="w">
489 </span></pre></td></tr></tbody></table></code></pre></figure>\n
490 <p>This should not be highlighted, right?</p>
491 EOS
257 create_post <<~EOS
258 ---
259 title: This is a test
260 ---
261
262 This is not yet highlighted
263 {% highlight php linenos %}
264 test
265 {% endhighlight %}
266
267 This should not be highlighted, right?
268 EOS
269 end
270
271 should "should stop highlighting at boundary with rouge" do
272 expected = <<~EOS
273 <p>This is not yet highlighted</p>\n
274 <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
275 </pre></td><td class="code"><pre><span class="n">test</span>\n</pre></td></tr></tbody></table></code></pre></figure>\n
276 <p>This should not be highlighted, right?</p>
277 EOS
492278 assert_match(expected, @result)
493279 end
494280 end
509295
510296 context "post content has highlight tag with only preceding spaces" do
511297 setup do
512 fill_post <<-EOS
513 [,1] [,2]
514 [1,] FALSE TRUE
515 [2,] FALSE TRUE
516 EOS
298 fill_post <<~EOS
299 [,1] [,2]
300 [1,] FALSE TRUE
301 [2,] FALSE TRUE
302 EOS
517303 end
518304
519305 should "only strip the preceding newlines" do
527313
528314 context "simple post with markdown and pre tags" do
529315 setup do
530 @content = <<CONTENT
531 ---
532 title: Kramdown vs. RDiscount vs. Redcarpet
533 ---
534
535 _FIGHT!_
536
537 {% highlight ruby %}
538 puts "3..2..1.."
539 {% endhighlight %}
540
541 *FINISH HIM*
542 CONTENT
543 end
544
545 context "using RDiscount" do
546 setup do
547 if jruby?
548 then skip(
549 "JRuby does not perform well with CExt, test disabled."
550 )
551 end
552
553 create_post(@content, {
554 "markdown" => "rdiscount",
555 })
316 @content = <<~CONTENT
317 ---
318 title: Kramdown post with pre
319 ---
320
321 _FIGHT!_
322
323 {% highlight ruby %}
324 puts "3..2..1.."
325 {% endhighlight %}
326
327 *FINISH HIM*
328 CONTENT
329 end
330
331 context "using Kramdown" do
332 setup do
333 create_post(@content, "markdown" => "kramdown")
556334 end
557335
558336 should "parse correctly" do
560338 assert_match %r!<em>FINISH HIM</em>!, @result
561339 end
562340 end
563
564 context "using Kramdown" do
565 setup do
566 create_post(@content, "markdown" => "kramdown")
567 end
568
569 should "parse correctly" do
570 assert_match %r{<em>FIGHT!</em>}, @result
571 assert_match %r!<em>FINISH HIM</em>!, @result
572 end
573 end
574
575 context "using Redcarpet" do
576 setup do
577 if jruby?
578 skip(
579 "JRuby does not perform well with CExt, test disabled."
580 )
581 end
582
583 create_post(@content, {
584 "markdown" => "redcarpet",
585 })
586 end
587
588 should "parse correctly" do
589 assert_match %r{<em>FIGHT!</em>}, @result
590 assert_match %r!<em>FINISH HIM</em>!, @result
591 end
592 end
593341 end
594342
595343 context "simple page with post linking" do
596344 setup do
597 content = <<CONTENT
598 ---
599 title: Post linking
600 ---
601
602 {% post_url 2008-11-21-complex %}
603 CONTENT
604 create_post(content, {
605 "permalink" => "pretty",
606 "source" => source_dir,
607 "destination" => dest_dir,
608 "read_posts" => true,
609 })
345 content = <<~CONTENT
346 ---
347 title: Post linking
348 ---
349
350 {% post_url 2008-11-21-complex %}
351 CONTENT
352 create_post(content,
353 "permalink" => "pretty",
354 "source" => source_dir,
355 "destination" => dest_dir,
356 "read_posts" => true)
610357 end
611358
612359 should "not cause an error" do
613 refute_match(%r!markdown\-html\-error!, @result)
360 refute_match(%r!markdown-html-error!, @result)
614361 end
615362
616363 should "have the URL to the 'complex' post from 2008-11-21" do
620367
621368 context "simple page with post linking containing special characters" do
622369 setup do
623 content = <<CONTENT
624 ---
625 title: Post linking
626 ---
627
628 {% post_url 2016-11-26-special-chars-(+) %}
629 CONTENT
630 create_post(content, {
631 "permalink" => "pretty",
632 "source" => source_dir,
633 "destination" => dest_dir,
634 "read_posts" => true,
635 })
370 content = <<~CONTENT
371 ---
372 title: Post linking
373 ---
374
375 {% post_url 2016-11-26-special-chars-(+) %}
376 CONTENT
377 create_post(content,
378 "permalink" => "pretty",
379 "source" => source_dir,
380 "destination" => dest_dir,
381 "read_posts" => true)
636382 end
637383
638384 should "not cause an error" do
639 refute_match(%r!markdown\-html\-error!, @result)
385 refute_match(%r!markdown-html-error!, @result)
640386 end
641387
642388 should "have the URL to the 'special-chars' post from 2016-11-26" do
646392
647393 context "simple page with nested post linking" do
648394 setup do
649 content = <<CONTENT
650 ---
651 title: Post linking
652 ---
653
654 - 1 {% post_url 2008-11-21-complex %}
655 - 2 {% post_url /2008-11-21-complex %}
656 - 3 {% post_url es/2008-11-21-nested %}
657 - 4 {% post_url /es/2008-11-21-nested %}
658 CONTENT
659 create_post(content, {
660 "permalink" => "pretty",
661 "source" => source_dir,
662 "destination" => dest_dir,
663 "read_posts" => true,
664 })
395 content = <<~CONTENT
396 ---
397 title: Post linking
398 ---
399
400 - 1 {% post_url 2008-11-21-complex %}
401 - 2 {% post_url /2008-11-21-complex %}
402 - 3 {% post_url es/2008-11-21-nested %}
403 - 4 {% post_url /es/2008-11-21-nested %}
404 CONTENT
405 create_post(content,
406 "permalink" => "pretty",
407 "source" => source_dir,
408 "destination" => dest_dir,
409 "read_posts" => true)
665410 end
666411
667412 should "not cause an error" do
668 refute_match(%r!markdown\-html\-error!, @result)
413 refute_match(%r!markdown-html-error!, @result)
669414 end
670415
671416 should "have the URL to the 'complex' post from 2008-11-21" do
681426
682427 context "simple page with nested post linking and path not used in `post_url`" do
683428 setup do
684 content = <<CONTENT
685 ---
686 title: Deprecated Post linking
687 ---
688
689 - 1 {% post_url 2008-11-21-nested %}
690 CONTENT
691 create_post(content, {
692 "permalink" => "pretty",
693 "source" => source_dir,
694 "destination" => dest_dir,
695 "read_posts" => true,
696 })
429 content = <<~CONTENT
430 ---
431 title: Deprecated Post linking
432 ---
433
434 - 1 {% post_url 2008-11-21-nested %}
435 CONTENT
436 create_post(content,
437 "permalink" => "pretty",
438 "source" => source_dir,
439 "destination" => dest_dir,
440 "read_posts" => true)
697441 end
698442
699443 should "not cause an error" do
700 refute_match(%r!markdown\-html\-error!, @result)
444 refute_match(%r!markdown-html-error!, @result)
701445 end
702446
703447 should "have the url to the 'nested' post from 2008-11-21" do
705449 end
706450
707451 should "throw a deprecation warning" do
708 deprecation_warning = " Deprecation: A call to "\
709 "'{% post_url 2008-11-21-nested %}' did not match a post using the new matching "\
710 "method of checking name (path-date-slug) equality. Please make sure that you "\
711 "change this tag to match the post's name exactly."
452 deprecation_warning = " Deprecation: A call to '{% post_url 2008-11-21-nested %}' " \
453 "did not match a post using the new matching method of checking " \
454 "name (path-date-slug) equality. Please make sure that you change " \
455 "this tag to match the post's name exactly."
712456 assert_includes Jekyll.logger.messages, deprecation_warning
713457 end
714458 end
715459
716460 context "simple page with invalid post name linking" do
717461 should "cause an error" do
718 content = <<CONTENT
719 ---
720 title: Invalid post name linking
721 ---
722
723 {% post_url abc2008-11-21-complex %}
724 CONTENT
462 content = <<~CONTENT
463 ---
464 title: Invalid post name linking
465 ---
466
467 {% post_url abc2008-11-21-complex %}
468 CONTENT
725469
726470 assert_raises Jekyll::Errors::PostURLError do
727 create_post(content, {
728 "permalink" => "pretty",
729 "source" => source_dir,
730 "destination" => dest_dir,
731 "read_posts" => true,
732 })
471 create_post(content,
472 "permalink" => "pretty",
473 "source" => source_dir,
474 "destination" => dest_dir,
475 "read_posts" => true)
733476 end
734477 end
735478
736479 should "cause an error with a bad date" do
737 content = <<CONTENT
738 ---
739 title: Invalid post name linking
740 ---
741
742 {% post_url 2008-42-21-complex %}
743 CONTENT
480 content = <<~CONTENT
481 ---
482 title: Invalid post name linking
483 ---
484
485 {% post_url 2008-42-21-complex %}
486 CONTENT
744487
745488 assert_raises Jekyll::Errors::InvalidDateError do
746 create_post(content, {
747 "permalink" => "pretty",
748 "source" => source_dir,
749 "destination" => dest_dir,
750 "read_posts" => true,
751 })
489 create_post(content,
490 "permalink" => "pretty",
491 "source" => source_dir,
492 "destination" => dest_dir,
493 "read_posts" => true)
752494 end
753495 end
754496 end
755497
756498 context "simple page with linking to a page" do
757499 setup do
758 content = <<CONTENT
759 ---
760 title: linking
761 ---
762
763 {% link contacts.html %}
764 {% link info.md %}
765 {% link /css/screen.css %}
766 CONTENT
767 create_post(content, {
768 "source" => source_dir,
769 "destination" => dest_dir,
770 "read_all" => true,
771 })
500 content = <<~CONTENT
501 ---
502 title: linking
503 ---
504
505 {% link contacts.html %}
506 {% link info.md %}
507 {% link /css/screen.css %}
508 CONTENT
509 create_post(content,
510 "source" => source_dir,
511 "destination" => dest_dir,
512 "read_all" => true)
772513 end
773514
774515 should "not cause an error" do
775 refute_match(%r!markdown\-html\-error!, @result)
516 refute_match(%r!markdown-html-error!, @result)
776517 end
777518
778519 should "have the URL to the 'contacts' item" do
788529 end
789530 end
790531
532 context "simple page with dynamic linking to a page" do
533 setup do
534 content = <<~CONTENT
535 ---
536 title: linking
537 ---
538
539 {% assign contacts_filename = 'contacts' %}
540 {% assign contacts_ext = 'html' %}
541 {% link {{contacts_filename}}.{{contacts_ext}} %}
542 {% assign info_path = 'info.md' %}
543 {% link {{\ info_path\ }} %}
544 {% assign screen_css_path = '/css' %}
545 {% link {{ screen_css_path }}/screen.css %}
546 CONTENT
547 create_post(content,
548 "source" => source_dir,
549 "destination" => dest_dir,
550 "read_all" => true)
551 end
552
553 should "not cause an error" do
554 refute_match(%r!markdown-html-error!, @result)
555 end
556
557 should "have the URL to the 'contacts' item" do
558 assert_match(%r!/contacts\.html!, @result)
559 end
560
561 should "have the URL to the 'info' item" do
562 assert_match(%r!/info\.html!, @result)
563 end
564
565 should "have the URL to the 'screen.css' item" do
566 assert_match(%r!/css/screen\.css!, @result)
567 end
568 end
569
791570 context "simple page with linking" do
792571 setup do
793 content = <<CONTENT
794 ---
795 title: linking
796 ---
797
798 {% link _methods/yaml_with_dots.md %}
799 CONTENT
800 create_post(content, {
801 "source" => source_dir,
802 "destination" => dest_dir,
803 "collections" => { "methods" => { "output" => true } },
804 "read_collections" => true,
805 })
572 content = <<~CONTENT
573 ---
574 title: linking
575 ---
576
577 {% link _methods/yaml_with_dots.md %}
578 CONTENT
579 create_post(content,
580 "source" => source_dir,
581 "destination" => dest_dir,
582 "collections" => { "methods" => { "output" => true } },
583 "read_collections" => true)
806584 end
807585
808586 should "not cause an error" do
809 refute_match(%r!markdown\-html\-error!, @result)
587 refute_match(%r!markdown-html-error!, @result)
810588 end
811589
812590 should "have the URL to the 'yaml_with_dots' item" do
814592 end
815593 end
816594
595 context "simple page with dynamic linking" do
596 setup do
597 content = <<~CONTENT
598 ---
599 title: linking
600 ---
601
602 {% assign yaml_with_dots_path = '_methods/yaml_with_dots.md' %}
603 {% link {{yaml_with_dots_path}} %}
604 CONTENT
605 create_post(content,
606 "source" => source_dir,
607 "destination" => dest_dir,
608 "collections" => { "methods" => { "output" => true } },
609 "read_collections" => true)
610 end
611
612 should "not cause an error" do
613 refute_match(%r!markdown-html-error!, @result)
614 end
615
616 should "have the URL to the 'yaml_with_dots' item" do
617 assert_match(%r!/methods/yaml_with_dots\.html!, @result)
618 end
619 end
620
817621 context "simple page with nested linking" do
818622 setup do
819 content = <<CONTENT
820 ---
821 title: linking
822 ---
823
824 - 1 {% link _methods/sanitized_path.md %}
825 - 2 {% link _methods/site/generate.md %}
826 CONTENT
827 create_post(content, {
828 "source" => source_dir,
829 "destination" => dest_dir,
830 "collections" => { "methods" => { "output" => true } },
831 "read_collections" => true,
832 })
623 content = <<~CONTENT
624 ---
625 title: linking
626 ---
627
628 - 1 {% link _methods/sanitized_path.md %}
629 - 2 {% link _methods/site/generate.md %}
630 CONTENT
631 create_post(content,
632 "source" => source_dir,
633 "destination" => dest_dir,
634 "collections" => { "methods" => { "output" => true } },
635 "read_collections" => true)
833636 end
834637
835638 should "not cause an error" do
836 refute_match(%r!markdown\-html\-error!, @result)
639 refute_match(%r!markdown-html-error!, @result)
837640 end
838641
839642 should "have the URL to the 'sanitized_path' item" do
847650
848651 context "simple page with invalid linking" do
849652 should "cause an error" do
850 content = <<CONTENT
851 ---
852 title: Invalid linking
853 ---
854
855 {% link non-existent-collection-item %}
856 CONTENT
653 content = <<~CONTENT
654 ---
655 title: Invalid linking
656 ---
657
658 {% link non-existent-collection-item %}
659 CONTENT
857660
858661 assert_raises ArgumentError do
859 create_post(content, {
860 "source" => source_dir,
861 "destination" => dest_dir,
862 "collections" => { "methods" => { "output" => true } },
863 "read_collections" => true,
864 })
662 create_post(content,
663 "source" => source_dir,
664 "destination" => dest_dir,
665 "collections" => { "methods" => { "output" => true } },
666 "read_collections" => true)
667 end
668 end
669 end
670
671 context "simple page with invalid dynamic linking" do
672 should "cause an error" do
673 content = <<~CONTENT
674 ---
675 title: Invalid linking
676 ---
677
678 {% assign non_existent_path = 'non-existent-collection-item' %}
679 {% link {{\ non_existent_path\ }} %}
680 CONTENT
681
682 assert_raises ArgumentError do
683 create_post(content,
684 "source" => source_dir,
685 "destination" => dest_dir,
686 "collections" => { "methods" => { "output" => true } },
687 "read_collections" => true)
865688 end
866689 end
867690 end
869692 context "include tag with parameters" do
870693 context "with symlink'd include" do
871694 should "not allow symlink includes" do
872 File.open("tmp/pages-test", "w") { |file| file.write("SYMLINK TEST") }
695 File.write("tmp/pages-test", "SYMLINK TEST")
873696 assert_raises IOError do
874 content = <<CONTENT
875 ---
876 title: Include symlink
877 ---
878
879 {% include tmp/pages-test %}
880
881 CONTENT
882 create_post(content, {
883 "permalink" => "pretty",
884 "source" => source_dir,
885 "destination" => dest_dir,
886 "read_posts" => true,
887 "safe" => true,
888 })
697 content = <<~CONTENT
698 ---
699 title: Include symlink
700 ---
701
702 {% include tmp/pages-test %}
703
704 CONTENT
705 create_post(content,
706 "permalink" => "pretty",
707 "source" => source_dir,
708 "destination" => dest_dir,
709 "read_posts" => true,
710 "safe" => true)
889711 end
890712 @result ||= ""
891713 refute_match(%r!SYMLINK TEST!, @result)
893715
894716 should "not expose the existence of symlinked files" do
895717 ex = assert_raises IOError do
896 content = <<CONTENT
897 ---
898 title: Include symlink
899 ---
900
901 {% include tmp/pages-test-does-not-exist %}
902
903 CONTENT
904 create_post(content, {
905 "permalink" => "pretty",
906 "source" => source_dir,
907 "destination" => dest_dir,
908 "read_posts" => true,
909 "safe" => true,
910 })
718 content = <<~CONTENT
719 ---
720 title: Include symlink
721 ---
722
723 {% include tmp/pages-test-does-not-exist %}
724
725 CONTENT
726 create_post(content,
727 "permalink" => "pretty",
728 "source" => source_dir,
729 "destination" => dest_dir,
730 "read_posts" => true,
731 "safe" => true)
911732 end
912733 assert_match(
913734 "Could not locate the included file 'tmp/pages-test-does-not-exist' " \
921742
922743 context "with one parameter" do
923744 setup do
924 content = <<CONTENT
925 ---
926 title: Include tag parameters
927 ---
928
929 {% include sig.markdown myparam="test" %}
930
931 {% include params.html param="value" %}
932 CONTENT
933 create_post(content, {
934 "permalink" => "pretty",
935 "source" => source_dir,
936 "destination" => dest_dir,
937 "read_posts" => true,
938 })
745 content = <<~CONTENT
746 ---
747 title: Include tag parameters
748 ---
749
750 {% include sig.markdown myparam="test" %}
751
752 {% include params.html param="value" %}
753 CONTENT
754 create_post(content,
755 "permalink" => "pretty",
756 "source" => source_dir,
757 "destination" => dest_dir,
758 "read_posts" => true)
939759 end
940760
941761 should "correctly output include variable" do
949769
950770 context "with simple syntax but multiline markup" do
951771 setup do
952 content = <<CONTENT
953 ---
954 title: Include tag parameters
955 ---
956
957 {% include sig.markdown myparam="test" %}
958
959 {% include params.html
960 param="value" %}
961 CONTENT
962 create_post(content, {
963 "permalink" => "pretty",
964 "source" => source_dir,
965 "destination" => dest_dir,
966 "read_posts" => true,
967 })
772 content = <<~CONTENT
773 ---
774 title: Include tag parameters
775 ---
776
777 {% include sig.markdown myparam="test" %}
778
779 {% include params.html
780 param="value" %}
781 CONTENT
782 create_post(content,
783 "permalink" => "pretty",
784 "source" => source_dir,
785 "destination" => dest_dir,
786 "read_posts" => true)
968787 end
969788
970789 should "correctly output include variable" do
978797
979798 context "with variable syntax but multiline markup" do
980799 setup do
981 content = <<CONTENT
982 ---
983 title: Include tag parameters
984 ---
985
986 {% include sig.markdown myparam="test" %}
987 {% assign path = "params" | append: ".html" %}
988 {% include {{ path }}
989 param="value" %}
990 CONTENT
991 create_post(content, {
992 "permalink" => "pretty",
993 "source" => source_dir,
994 "destination" => dest_dir,
995 "read_posts" => true,
996 })
800 content = <<~CONTENT
801 ---
802 title: Include tag parameters
803 ---
804
805 {% include sig.markdown myparam="test" %}
806 {% assign path = "params" | append: ".html" %}
807 {% include {{ path }}
808 param="value" %}
809 CONTENT
810 create_post(content,
811 "permalink" => "pretty",
812 "source" => source_dir,
813 "destination" => dest_dir,
814 "read_posts" => true)
997815 end
998816
999817 should "correctly output include variable" do
1007825
1008826 context "with invalid parameter syntax" do
1009827 should "throw a ArgumentError" do
1010 content = <<CONTENT
1011 ---
1012 title: Invalid parameter syntax
1013 ---
1014
1015 {% include params.html param s="value" %}
1016 CONTENT
828 content = <<~CONTENT
829 ---
830 title: Invalid parameter syntax
831 ---
832
833 {% include params.html param s="value" %}
834 CONTENT
1017835 assert_raises ArgumentError, "Did not raise exception on invalid " \
1018836 '"include" syntax' do
1019 create_post(content, {
1020 "permalink" => "pretty",
1021 "source" => source_dir,
1022 "destination" => dest_dir,
1023 "read_posts" => true,
1024 })
837 create_post(content,
838 "permalink" => "pretty",
839 "source" => source_dir,
840 "destination" => dest_dir,
841 "read_posts" => true)
1025842 end
1026843
1027 content = <<CONTENT
1028 ---
1029 title: Invalid parameter syntax
1030 ---
1031
1032 {% include params.html params="value %}
1033 CONTENT
844 content = <<~CONTENT
845 ---
846 title: Invalid parameter syntax
847 ---
848
849 {% include params.html params="value %}
850 CONTENT
1034851 assert_raises ArgumentError, "Did not raise exception on invalid " \
1035852 '"include" syntax' do
1036 create_post(content, {
1037 "permalink" => "pretty",
1038 "source" => source_dir,
1039 "destination" => dest_dir,
1040 "read_posts" => true,
1041 })
853 create_post(content,
854 "permalink" => "pretty",
855 "source" => source_dir,
856 "destination" => dest_dir,
857 "read_posts" => true)
1042858 end
1043859 end
1044860 end
1045861
1046862 context "with several parameters" do
1047863 setup do
1048 content = <<CONTENT
1049 ---
1050 title: multiple include parameters
1051 ---
1052
1053 {% include params.html param1="new_value" param2="another" %}
1054 CONTENT
1055 create_post(content, {
1056 "permalink" => "pretty",
1057 "source" => source_dir,
1058 "destination" => dest_dir,
1059 "read_posts" => true,
1060 })
864 content = <<~CONTENT
865 ---
866 title: multiple include parameters
867 ---
868
869 {% include params.html param1="new_value" param2="another" %}
870 CONTENT
871 create_post(content,
872 "permalink" => "pretty",
873 "source" => source_dir,
874 "destination" => dest_dir,
875 "read_posts" => true)
1061876 end
1062877
1063878 should "list all parameters" do
1072887
1073888 context "without parameters" do
1074889 setup do
1075 content = <<CONTENT
1076 ---
1077 title: without parameters
1078 ---
1079
1080 {% include params.html %}
1081 CONTENT
1082 create_post(content, {
1083 "permalink" => "pretty",
1084 "source" => source_dir,
1085 "destination" => dest_dir,
1086 "read_posts" => true,
1087 })
890 content = <<~CONTENT
891 ---
892 title: without parameters
893 ---
894
895 {% include params.html %}
896 CONTENT
897 create_post(content,
898 "permalink" => "pretty",
899 "source" => source_dir,
900 "destination" => dest_dir,
901 "read_posts" => true)
1088902 end
1089903
1090904 should "include file with empty parameters" do
1092906 end
1093907 end
1094908
909 context "with include file with special characters without params" do
910 setup do
911 content = <<~CONTENT
912 ---
913 title: special characters
914 ---
915
916 {% include params@2.0.html %}
917 CONTENT
918 create_post(content,
919 "permalink" => "pretty",
920 "source" => source_dir,
921 "destination" => dest_dir,
922 "read_posts" => true)
923 end
924
925 should "include file with empty parameters" do
926 assert_match "<span id=\"include-param\"></span>", @result
927 end
928 end
929
930 context "with include file with special characters with params" do
931 setup do
932 content = <<~CONTENT
933 ---
934 title: special characters
935 ---
936
937 {% include params@2.0.html param1="foobar" param2="bazbar" %}
938 CONTENT
939 create_post(content,
940 "permalink" => "pretty",
941 "source" => source_dir,
942 "destination" => dest_dir,
943 "read_posts" => true)
944 end
945
946 should "include file with empty parameters" do
947 assert_match "<li>param1 = foobar</li>", @result
948 assert_match "<li>param2 = bazbar</li>", @result
949 end
950 end
951
1095952 context "with custom includes directory" do
1096953 setup do
1097 content = <<CONTENT
1098 ---
1099 title: custom includes directory
1100 ---
1101
1102 {% include custom.html %}
1103 CONTENT
1104 create_post(content, {
1105 "includes_dir" => "_includes_custom",
1106 "permalink" => "pretty",
1107 "source" => source_dir,
1108 "destination" => dest_dir,
1109 "read_posts" => true,
1110 })
954 content = <<~CONTENT
955 ---
956 title: custom includes directory
957 ---
958
959 {% include custom.html %}
960 CONTENT
961 create_post(content,
962 "includes_dir" => "_includes_custom",
963 "permalink" => "pretty",
964 "source" => source_dir,
965 "destination" => dest_dir,
966 "read_posts" => true)
1111967 end
1112968
1113969 should "include file from custom directory" do
1117973
1118974 context "without parameters within if statement" do
1119975 setup do
1120 content = <<CONTENT
1121 ---
1122 title: without parameters within if statement
1123 ---
1124
1125 {% if true %}{% include params.html %}{% endif %}
1126 CONTENT
1127 create_post(content, {
1128 "permalink" => "pretty",
1129 "source" => source_dir,
1130 "destination" => dest_dir,
1131 "read_posts" => true,
1132 })
976 content = <<~CONTENT
977 ---
978 title: without parameters within if statement
979 ---
980
981 {% if true %}{% include params.html %}{% endif %}
982 CONTENT
983 create_post(content,
984 "permalink" => "pretty",
985 "source" => source_dir,
986 "destination" => dest_dir,
987 "read_posts" => true)
1133988 end
1134989
1135990 should "include file with empty parameters within if statement" do
1139994
1140995 context "include missing file" do
1141996 setup do
1142 @content = <<CONTENT
1143 ---
1144 title: missing file
1145 ---
1146
1147 {% include missing.html %}
1148 CONTENT
997 @content = <<~CONTENT
998 ---
999 title: missing file
1000 ---
1001
1002 {% include missing.html %}
1003 CONTENT
11491004 end
11501005
11511006 should "raise error relative to source directory" do
11521007 exception = assert_raises IOError do
1153 create_post(@content, {
1154 "permalink" => "pretty",
1155 "source" => source_dir,
1156 "destination" => dest_dir,
1157 "read_posts" => true,
1158 })
1008 create_post(@content,
1009 "permalink" => "pretty",
1010 "source" => source_dir,
1011 "destination" => dest_dir,
1012 "read_posts" => true)
11591013 end
11601014 assert_match(
11611015 "Could not locate the included file 'missing.html' in any of " \
11671021
11681022 context "include tag with variable and liquid filters" do
11691023 setup do
1170 site = fixture_site({ "pygments" => true }).tap(&:read).tap(&:render)
1024 site = fixture_site("pygments" => true).tap(&:read).tap(&:render)
11711025 post = site.posts.docs.find do |p|
11721026 p.basename.eql? "2013-12-17-include-variable-filters.markdown"
11731027 end
11991053
12001054 context "relative include tag with variable and liquid filters" do
12011055 setup do
1202 site = fixture_site({ "pygments" => true }).tap(&:read).tap(&:render)
1056 site = fixture_site("pygments" => true).tap(&:read).tap(&:render)
12031057 post = site.posts.docs.find do |p|
12041058 p.basename.eql? "2014-09-02-relative-includes.markdown"
12051059 end
12341088 context "trying to do bad stuff" do
12351089 context "include missing file" do
12361090 setup do
1237 @content = <<CONTENT
1238 ---
1239 title: missing file
1240 ---
1241
1242 {% include_relative missing.html %}
1243 CONTENT
1091 @content = <<~CONTENT
1092 ---
1093 title: missing file
1094 ---
1095
1096 {% include_relative missing.html %}
1097 CONTENT
12441098 end
12451099
12461100 should "raise error relative to source directory" do
12471101 exception = assert_raises IOError do
1248 create_post(@content, {
1249 "permalink" => "pretty",
1250 "source" => source_dir,
1251 "destination" => dest_dir,
1252 "read_posts" => true,
1253 })
1102 create_post(@content,
1103 "permalink" => "pretty",
1104 "source" => source_dir,
1105 "destination" => dest_dir,
1106 "read_posts" => true)
12541107 end
12551108 assert_match "Could not locate the included file 'missing.html' in any of " \
12561109 "[\"#{source_dir}\"].", exception.message
12591112
12601113 context "include existing file above you" do
12611114 setup do
1262 @content = <<CONTENT
1263 ---
1264 title: higher file
1265 ---
1266
1267 {% include_relative ../README.markdown %}
1268 CONTENT
1115 @content = <<~CONTENT
1116 ---
1117 title: higher file
1118 ---
1119
1120 {% include_relative ../README.markdown %}
1121 CONTENT
12691122 end
12701123
12711124 should "raise error relative to source directory" do
12721125 exception = assert_raises ArgumentError do
1273 create_post(@content, {
1274 "permalink" => "pretty",
1275 "source" => source_dir,
1276 "destination" => dest_dir,
1277 "read_posts" => true,
1278 })
1126 create_post(@content,
1127 "permalink" => "pretty",
1128 "source" => source_dir,
1129 "destination" => dest_dir,
1130 "read_posts" => true)
12791131 end
12801132 assert_equal(
12811133 "Invalid syntax for include tag. File contains invalid characters or " \
12891141
12901142 context "with symlink'd include" do
12911143 should "not allow symlink includes" do
1292 File.open("tmp/pages-test", "w") { |file| file.write("SYMLINK TEST") }
1144 File.write("tmp/pages-test", "SYMLINK TEST")
12931145 assert_raises IOError do
1294 content = <<CONTENT
1295 ---
1296 title: Include symlink
1297 ---
1298
1299 {% include_relative tmp/pages-test %}
1300
1301 CONTENT
1302 create_post(content, {
1303 "permalink" => "pretty",
1304 "source" => source_dir,
1305 "destination" => dest_dir,
1306 "read_posts" => true,
1307 "safe" => true,
1308 })
1146 content = <<~CONTENT
1147 ---
1148 title: Include symlink
1149 ---
1150
1151 {% include_relative tmp/pages-test %}
1152
1153 CONTENT
1154 create_post(content,
1155 "permalink" => "pretty",
1156 "source" => source_dir,
1157 "destination" => dest_dir,
1158 "read_posts" => true,
1159 "safe" => true)
13091160 end
13101161 @result ||= ""
13111162 refute_match(%r!SYMLINK TEST!, @result)
13131164
13141165 should "not expose the existence of symlinked files" do
13151166 ex = assert_raises IOError do
1316 content = <<CONTENT
1317 ---
1318 title: Include symlink
1319 ---
1320
1321 {% include_relative tmp/pages-test-does-not-exist %}
1322
1323 CONTENT
1324 create_post(content, {
1325 "permalink" => "pretty",
1326 "source" => source_dir,
1327 "destination" => dest_dir,
1328 "read_posts" => true,
1329 "safe" => true,
1330 })
1167 content = <<~CONTENT
1168 ---
1169 title: Include symlink
1170 ---
1171
1172 {% include_relative tmp/pages-test-does-not-exist %}
1173
1174 CONTENT
1175 create_post(content,
1176 "permalink" => "pretty",
1177 "source" => source_dir,
1178 "destination" => dest_dir,
1179 "read_posts" => true,
1180 "safe" => true)
13311181 end
13321182 assert_match(
1333 "Ensure it exists in one of those directories and is not a symlink "\
1183 "Ensure it exists in one of those directories and is not a symlink " \
13341184 "as those are not allowed in safe mode.",
13351185 ex.message
13361186 )
2525 Theme.new("foo").version
2626 end
2727 end
28
29 should "add itself to sass's load path" do
30 @theme.configure_sass
31 message = "Sass load paths should include the theme sass dir"
32 assert Sass.load_paths.include?(@theme.sass_path), message
33 end
3428 end
3529
3630 context "path generation" do
37 [:assets, :_layouts, :_includes, :_sass].each do |folder|
31 [:assets, :_data, :_layouts, :_includes, :_sass].each do |folder|
3832 should "know the #{folder} path" do
3933 expected = theme_dir(folder.to_s)
4034 assert_equal expected, @theme.public_send("#{folder.to_s.tr("_", "")}_path")
7973
8074 should "raise when getting theme root" do
8175 error = assert_raises(RuntimeError) { Theme.new("test-non-existent-theme") }
82 assert_match(%r!fixtures\/test-non-existent-theme does not exist!, error.message)
76 assert_match(%r!fixtures/test-non-existent-theme does not exist!, error.message)
8377 end
8478 end
8579 end
1313 def assert_file_with_relative_path(haystack, relative_path)
1414 assert haystack.any? { |f|
1515 f.relative_path == relative_path
16 }, "Site should read in the #{relative_path} file, " \
17 "but it was not found in #{haystack.inspect}"
16 }, "Site should read in the #{relative_path} file, but it was not found in #{haystack.inspect}"
1817 end
1918
2019 def refute_file_with_relative_path(haystack, relative_path)
2120 refute haystack.any? { |f|
2221 f.relative_path == relative_path
23 }, "Site should not have read in the #{relative_path} file, " \
24 "but it was found in #{haystack.inspect}"
22 }, "Site should not have read in the #{relative_path} file, but it was found in " \
23 "#{haystack.inspect}"
2524 end
2625
2726 context "with a valid theme" do
3837 file = @site.pages.find { |f| f.relative_path == "assets/style.scss" }
3938 refute_nil file
4039 assert_equal @site.in_dest_dir("assets/style.css"), file.destination(@site.dest)
41 assert_includes file.output, ".sample {\n color: black; }"
40 assert_includes file.output, ".sample { color: black; }"
4241 end
4342
4443 should "not overwrite site content with the same relative path" do
8180
8281 begin
8382 tmp_dir = Dir.mktmpdir("jekyll-theme-test")
84 File.open(File.join(tmp_dir, "test.txt"), "wb") { |f| f.write "content" }
83 File.binwrite(File.join(tmp_dir, "test.txt"), "content")
8584
8685 theme_dir = File.join(__dir__, "fixtures", "test-theme-symlink")
8786 File.symlink(tmp_dir, File.join(theme_dir, "assets"))
0 # frozen_string_literal: true
1
2 require "helper"
3
4 class TestThemeDataReader < JekyllUnitTest
5 context "site without a theme" do
6 setup do
7 @site = fixture_site("theme" => nil)
8 @site.reader.read_data
9 assert @site.data["greetings"]
10 assert @site.data["categories"]["dairy"]
11 end
12
13 should "should read data from source" do
14 assert_equal "Hello! I’m foo. And who are you?", @site.data["greetings"]["foo"]
15 assert_equal "Dairy", @site.data["categories"]["dairy"]["name"]
16 end
17 end
18
19 context "site with a theme without _data" do
20 setup do
21 @site = fixture_site("theme" => "test-theme-skinny")
22 @site.reader.read_data
23 assert @site.data["greetings"]
24 assert @site.data["categories"]["dairy"]
25 end
26
27 should "should read data from source" do
28 assert_equal "Hello! I’m foo. And who are you?", @site.data["greetings"]["foo"]
29 assert_equal "Dairy", @site.data["categories"]["dairy"]["name"]
30 end
31 end
32
33 context "site with a theme with empty _data directory" do
34 setup do
35 @site = fixture_site("theme" => "test-theme-w-empty-data")
36 @site.reader.read_data
37 assert @site.data["greetings"]
38 assert @site.data["categories"]["dairy"]
39 end
40
41 should "should read data from source" do
42 assert_equal "Hello! I’m foo. And who are you?", @site.data["greetings"]["foo"]
43 assert_equal "Dairy", @site.data["categories"]["dairy"]["name"]
44 end
45 end
46
47 context "site with a theme with data at root of _data" do
48 setup do
49 @site = fixture_site("theme" => "test-theme")
50 @site.reader.read_data
51 assert @site.data["greetings"]
52 assert @site.data["categories"]["dairy"]
53 assert @site.data["cars"]
54 end
55
56 should "should merge nested keys" do
57 refute_equal "Hello! I’m bar. What’s up so far?", @site.data["greetings"]["foo"]
58 assert_equal "Hello! I’m foo. And who are you?", @site.data["greetings"]["foo"]
59 assert_equal "Mercedes", @site.data["cars"]["manufacturer"]
60 end
61 end
62
63 context "site with a theme with data at root of _data and in a subdirectory" do
64 setup do
65 @site = fixture_site("theme" => "test-theme")
66 @site.reader.read_data
67 assert @site.data["greetings"]
68 assert @site.data["categories"]["dairy"]
69 assert @site.data["cars"]
70 end
71
72 should "should merge nested keys" do
73 refute_equal "Cheese Dairy", @site.data["categories"]["dairy"]["name"]
74 expected_names = %w(cheese milk)
75 product_names = @site.data["categories"]["dairy"]["products"].map do |product|
76 product["name"]
77 end
78 expected_names.each do |expected_name|
79 assert_includes product_names, expected_name
80 end
81 assert_equal "Dairy", @site.data["categories"]["dairy"]["name"]
82 end
83
84 should "should illustrate the documented sample" do
85 assert_equal "Kundenstimmen", @site.data["i18n"]["testimonials"]["header"]
86 assert_equal "Design by FTC", @site.data["i18n"]["testimonials"]["footer"]
87 end
88 end
89 end
0 # frozen_string_literal: true
1
2 require "helper"
3
4 class TestThemeDrop < JekyllUnitTest
5 should "be initialized only for gem-based themes" do
6 assert_nil fixture_site.to_liquid.theme
7 end
8
9 context "a theme drop" do
10 setup do
11 @drop = fixture_site("theme" => "test-theme").to_liquid.theme
12 end
13
14 should "respond to `key?`" do
15 assert_respond_to @drop, :key?
16 end
17
18 should "export relevant data to Liquid templates" do
19 expected = {
20 "authors" => "Jekyll",
21 "dependencies" => [],
22 "description" => "This is a theme used to test Jekyll",
23 "metadata" => {},
24 "root" => theme_dir,
25 "version" => "0.1.0",
26 }
27 expected.each_key do |key|
28 assert @drop.key?(key)
29 assert_equal expected[key], @drop[key]
30 end
31 end
32 end
33 end
129129
130130 context "The \`Utils.slugify\` method" do
131131 should "return nil if passed nil" do
132 begin
133 assert Utils.slugify(nil).nil?
134 rescue NoMethodError
135 assert false, "Threw NoMethodError"
136 end
132 assert_nil Utils.slugify(nil)
133 rescue NoMethodError
134 assert false, "Threw NoMethodError"
137135 end
138136
139137 should "replace whitespace with hyphens" do
173171
174172 should "replace punctuation in any scripts by hyphens" do
175173 assert_equal "5時-6時-三-一四", Utils.slugify("5時〜6時 三・一四")
174 end
175
176 should "not replace Unicode 'Mark', 'Letter', or 'Number: Decimal Digit' category characters" do
177 assert_equal "மல்லிப்பூ-வகைகள்", Utils.slugify("மல்லிப்பூ வகைகள்")
178 assert_equal "மல்லிப்பூ-வகைகள்", Utils.slugify("மல்லிப்பூ வகைகள்", :mode => "pretty")
176179 end
177180
178181 should "not modify the original string" do
201204
202205 should "replace everything else but ASCII characters" do
203206 assert_equal "the-config-yml-file",
204 Utils.slugify("The _config.yml file?", :mode => "ascii")
207 Utils.slugify("The _config.yml file?", :mode => "ascii")
205208 assert_equal "f-rtive-glance",
206 Utils.slugify("fürtive glance!!!!", :mode => "ascii")
209 Utils.slugify("fürtive glance!!!!", :mode => "ascii")
207210 end
208211
209212 should "map accented latin characters to ASCII characters" do
210213 assert_equal "the-config-yml-file",
211 Utils.slugify("The _config.yml file?", :mode => "latin")
214 Utils.slugify("The _config.yml file?", :mode => "latin")
212215 assert_equal "furtive-glance",
213 Utils.slugify("fürtive glance!!!!", :mode => "latin")
216 Utils.slugify("fürtive glance!!!!", :mode => "latin")
214217 assert_equal "aaceeiioouu",
215 Utils.slugify("àáçèéíïòóúü", :mode => "latin")
218 Utils.slugify("àáçèéíïòóúü", :mode => "latin")
216219 assert_equal "a-z",
217 Utils.slugify("Aあわれ鬱господинZ", :mode => "latin")
220 Utils.slugify("Aあわれ鬱господинZ", :mode => "latin")
218221 end
219222
220223 should "only replace whitespace if mode is raw" do
280283 "The _config.yml file?",
281284 Utils.slugify("The _config.yml file?", :mode => "none", :cased => true)
282285 )
286 end
287
288 should "records a warning in the log if the returned slug is empty" do
289 expect(Jekyll.logger).to receive(:warn)
290 assert_equal "", Utils.slugify("💎")
283291 end
284292 end
285293
399407 assert_nil opts[:encoding]
400408 end
401409
402 should "add bom to encoding" do
410 should "add bom to utf-encoding" do
403411 opts = { "encoding" => "utf-8", :encoding => "utf-8" }
404412 merged = Utils.merged_file_read_opts(nil, opts)
405413 assert_equal "bom|utf-8", merged["encoding"]
406414 assert_equal "bom|utf-8", merged[:encoding]
415 end
416
417 should "not add bom to non-utf encoding" do
418 opts = { "encoding" => "ISO-8859-1", :encoding => "ISO-8859-1" }
419 merged = Utils.merged_file_read_opts(nil, opts)
420 assert_equal "ISO-8859-1", merged["encoding"]
421 assert_equal "ISO-8859-1", merged[:encoding]
407422 end
408423
409424 should "preserve bom in encoding" do
0 # frozen_string_literal: true
1
2 require "helper"
3
4 class TestWinTz < JekyllUnitTest
5 [["America/New_York", "WTZ+05:00"], ["Europe/Paris", "WTZ-01:00"]].each do |tz, expected|
6 should "use base offset in winter for #{tz}" do
7 result = Jekyll::Utils::WinTZ.calculate(tz, Time.utc(2021, 1, 1))
8 assert_equal expected, result
9 end
10 end
11
12 [["America/New_York", "WTZ+04:00"], ["Europe/Paris", "WTZ-02:00"]].each do |tz, expected|
13 should "apply DST in summer for #{tz}" do
14 result = Jekyll::Utils::WinTZ.calculate(tz, Time.utc(2021, 7, 1))
15 assert_equal expected, result
16 end
17 end
18
19 [["Australia/Eucla", "WTZ-08:45"], ["Pacific/Marquesas", "WTZ+09:30"]].each do |tz, expected|
20 should "handle non zero minutes for #{tz}" do
21 result = Jekyll::Utils::WinTZ.calculate(tz, Time.utc(2021, 1, 1))
22 assert_equal expected, result
23 end
24 end
25
26 should "return zero for UTC" do
27 result = Jekyll::Utils::WinTZ.calculate("UTC")
28 assert_equal "WTZ+00:00", result
29 end
30 end