Compare commits
12 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
f449b1af64 | |
|
|
589feb0186 | |
|
|
db0e639259 | |
|
|
c90dcf86d8 | |
|
|
6005f93781 | |
|
|
1980476395 | |
|
|
7d1d478c0b | |
|
|
799da8f3bf | |
|
|
62d7e8af8b | |
|
|
5200f23221 | |
|
|
9e452bf1d6 | |
|
|
b41ebd39a8 |
|
|
@ -21,7 +21,7 @@ jobs:
|
|||
ruby_version:
|
||||
- 2.5
|
||||
- 2.7
|
||||
- 3.0
|
||||
- 3.1
|
||||
jekyll_version:
|
||||
- "~> 4.0"
|
||||
steps:
|
||||
|
|
|
|||
|
|
@ -60,3 +60,4 @@ jobs:
|
|||
bundle exec jekyll build -s sandbox -d sandbox/_site --trace
|
||||
- name: Memory Analysis of Jekyll Build (WITH SEO Tag)
|
||||
run: bash jekyll-seo-tag/.github/workflows/scripts/memprof
|
||||
if: "!contains(matrix.jekyll_version, '3.')"
|
||||
|
|
|
|||
|
|
@ -1,3 +1,16 @@
|
|||
## HEAD
|
||||
|
||||
### Development Fixes
|
||||
|
||||
* Add Ruby 3.1 to CI matrix (#459)
|
||||
* chore: remove git.io (#462)
|
||||
|
||||
### Minor Enhancements
|
||||
|
||||
* Added the `twitter:description` tag (#464)
|
||||
* Support for image paths relative to the page's directory (#466)
|
||||
* Truncate the description value to 100 words (#492)
|
||||
|
||||
## 2.8.0 / 2022-02-04
|
||||
|
||||
### Minor Enhancements
|
||||
|
|
|
|||
|
|
@ -80,6 +80,27 @@ You can set it the same way as the other author properties. For example, you can
|
|||
url: https://example.com/
|
||||
```
|
||||
|
||||
### Customizing description length
|
||||
|
||||
By default, the description is limited to the first 100 words of the full content.
|
||||
|
||||
You can adjust this limit at the page level, by using the `seo_description_max_words` page property:
|
||||
|
||||
```yml
|
||||
seo_description_max_words: 200
|
||||
```
|
||||
|
||||
You can also set a default site-wide value for all pages using [Front Matter defaults](https://jekyllrb.com/docs/configuration/front-matter-defaults/) in your `_config.yml` file:
|
||||
|
||||
```yml
|
||||
defaults:
|
||||
- scope:
|
||||
path: ""
|
||||
values:
|
||||
seo_description_max_words: 200
|
||||
```
|
||||
|
||||
|
||||
### Customizing JSON-LD output
|
||||
|
||||
The following options can be set for any particular page. While the default options are meant to serve most users in the most common circumstances, there may be situations where more precise control is necessary.
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ module Jekyll
|
|||
end
|
||||
|
||||
def payload
|
||||
# site_payload is an instance of UnifiedPayloadDrop. See https://git.io/v5ajm
|
||||
# site_payload is an instance of UnifiedPayloadDrop. See https://github.com/jekyll/jekyll/blob/22f2724a1f117a94cc16d18c499a93d5915ede4f/lib/jekyll/site.rb#L261-L276
|
||||
context.registers[:site].site_payload.tap do |site_payload|
|
||||
site_payload["page"] = context.registers[:page]
|
||||
site_payload["paginator"] = context["paginator"]
|
||||
|
|
|
|||
|
|
@ -89,7 +89,8 @@ module Jekyll
|
|||
|
||||
def description
|
||||
@description ||= begin
|
||||
format_string(page["description"] || page["excerpt"]) || site_description
|
||||
value = format_string(page["description"] || page["excerpt"]) || site_description
|
||||
snippet(value, description_max_words)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -175,6 +176,10 @@ module Jekyll
|
|||
end
|
||||
end
|
||||
|
||||
def description_max_words
|
||||
@description_max_words ||= page["seo_description_max_words"] || 100
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def filters
|
||||
|
|
@ -217,6 +222,13 @@ module Jekyll
|
|||
string unless string.empty?
|
||||
end
|
||||
|
||||
def snippet(string, max_words)
|
||||
return string if string.nil?
|
||||
|
||||
result = string.split(%r!\s+!, max_words + 1)[0...max_words].join(" ")
|
||||
result.length < string.length ? result.concat("…") : result
|
||||
end
|
||||
|
||||
def seo_name
|
||||
@seo_name ||= format_string(page_seo["name"]) if page_seo["name"]
|
||||
end
|
||||
|
|
|
|||
|
|
@ -61,13 +61,18 @@ module Jekyll
|
|||
|
||||
def absolute_url
|
||||
return unless raw_path
|
||||
return @absolute_url if defined? @absolute_url
|
||||
|
||||
@absolute_url = if raw_path.is_a?(String) && absolute_url?(raw_path) == false
|
||||
filters.absolute_url raw_path
|
||||
else
|
||||
raw_path
|
||||
end
|
||||
@absolute_url ||= build_absolute_path
|
||||
end
|
||||
|
||||
def build_absolute_path
|
||||
return raw_path unless raw_path.is_a?(String) && absolute_url?(raw_path) == false
|
||||
return filters.absolute_url(raw_path) if raw_path.start_with?("/")
|
||||
|
||||
page_dir = @page["url"]
|
||||
page_dir = File.dirname(page_dir) unless page_dir.end_with?("/")
|
||||
|
||||
filters.absolute_url File.join(page_dir, raw_path)
|
||||
end
|
||||
|
||||
def filters
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
{% if seo_tag.description %}
|
||||
<meta name="description" content="{{ seo_tag.description }}" />
|
||||
<meta property="og:description" content="{{ seo_tag.description }}" />
|
||||
<meta property="twitter:description" content="{{ seo_tag.description }}" />
|
||||
{% endif %}
|
||||
|
||||
{% if site.url %}
|
||||
|
|
|
|||
|
|
@ -233,6 +233,24 @@ RSpec.describe Jekyll::SeoTag::Drop do
|
|||
expect(subject.description).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context "truncation" do
|
||||
context "without seo_description_max_words" do
|
||||
let(:page_meta) { { "description" => "word " * 150 } }
|
||||
|
||||
it "truncates the description to the first 200 words" do
|
||||
expect(subject.description).to eql(("word " * 100).chop.concat("…"))
|
||||
end
|
||||
end
|
||||
|
||||
context "with an explicit seo_description_max_words property" do
|
||||
let(:page_meta) { { "description" => "For a long time, I went to bed early", "seo_description_max_words" => 6 } }
|
||||
|
||||
it "truncates the description to the configured words count" do
|
||||
expect(subject.description).to eql("For a long time, I went…")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "author" do
|
||||
|
|
@ -415,7 +433,7 @@ RSpec.describe Jekyll::SeoTag::Drop do
|
|||
end
|
||||
|
||||
context "image" do
|
||||
let(:image) { "foo.png" }
|
||||
let(:image) { "/foo.png" }
|
||||
let(:page_meta) { { "image" => image } }
|
||||
|
||||
it "returns a Drop" do
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
RSpec.describe Jekyll::SeoTag::ImageDrop do
|
||||
let(:config) { { "title" => "site title" } }
|
||||
let(:image) { nil }
|
||||
let(:page_meta) { { "image" => image } }
|
||||
let(:page_meta) { { "image" => image, "dir" => "foo" } }
|
||||
let(:page) { make_page(page_meta) }
|
||||
let(:site) { make_site(config) }
|
||||
let(:context) { make_context(:page => page, :site => site) }
|
||||
|
|
@ -14,8 +14,34 @@ RSpec.describe Jekyll::SeoTag::ImageDrop do
|
|||
Jekyll.logger.log_level = :error
|
||||
end
|
||||
|
||||
context "with image as a string" do
|
||||
context "with a post object" do
|
||||
let(:image) { "image.png" }
|
||||
let(:page_meta) { { "image" => image, "date" => "2017-01-01" } }
|
||||
let(:page) { make_post(page_meta) }
|
||||
|
||||
it "returns the image url relative to the post directory" do
|
||||
expect(subject["path"]).to eql("/2017/01/01/image.png")
|
||||
end
|
||||
end
|
||||
|
||||
context "with image as a relative path" do
|
||||
let(:image) { "image.png" }
|
||||
|
||||
it "returns the image with the page dir prepended" do
|
||||
expect(subject["path"]).to eql("/foo/image.png")
|
||||
end
|
||||
|
||||
context "with site.url" do
|
||||
let(:config) { { "url" => "http://example.com" } }
|
||||
|
||||
it "makes the path absolute" do
|
||||
expect(subject["path"]).to eql("http://example.com/foo/image.png")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "with image as an absolute path" do
|
||||
let(:image) { "/image.png" }
|
||||
|
||||
it "returns the image" do
|
||||
expect(subject["path"]).to eql("/image.png")
|
||||
|
|
@ -30,7 +56,7 @@ RSpec.describe Jekyll::SeoTag::ImageDrop do
|
|||
end
|
||||
|
||||
context "with a URL-escaped path" do
|
||||
let(:image) { "some image.png" }
|
||||
let(:image) { "/some image.png" }
|
||||
|
||||
it "URL-escapes the image" do
|
||||
expect(subject["path"]).to eql("/some%20image.png")
|
||||
|
|
@ -39,8 +65,8 @@ RSpec.describe Jekyll::SeoTag::ImageDrop do
|
|||
end
|
||||
|
||||
context "with image as a hash" do
|
||||
context "with a path" do
|
||||
let(:image) { { "path" => "image.png" } }
|
||||
context "with an absolute path" do
|
||||
let(:image) { { "path" => "/image.png" } }
|
||||
|
||||
it "returns the image" do
|
||||
expect(subject["path"]).to eql("/image.png")
|
||||
|
|
@ -48,7 +74,7 @@ RSpec.describe Jekyll::SeoTag::ImageDrop do
|
|||
end
|
||||
|
||||
context "with facebook" do
|
||||
let(:image) { { "facebook" => "image.png" } }
|
||||
let(:image) { { "facebook" => "/image.png" } }
|
||||
|
||||
it "returns the image" do
|
||||
expect(subject["path"]).to eql("/image.png")
|
||||
|
|
@ -56,7 +82,7 @@ RSpec.describe Jekyll::SeoTag::ImageDrop do
|
|||
end
|
||||
|
||||
context "with twitter" do
|
||||
let(:image) { { "twitter" => "image.png" } }
|
||||
let(:image) { { "twitter" => "/image.png" } }
|
||||
|
||||
it "returns the image" do
|
||||
expect(subject["path"]).to eql("/image.png")
|
||||
|
|
|
|||
|
|
@ -113,8 +113,8 @@ RSpec.describe Jekyll::SeoTag::JSONLDDrop do
|
|||
end
|
||||
|
||||
context "image" do
|
||||
context "with image as a string" do
|
||||
let(:image) { "image" }
|
||||
context "with image as an absolute path" do
|
||||
let(:image) { "/image" }
|
||||
|
||||
it "returns the image as a string" do
|
||||
expect(subject).to have_key("image")
|
||||
|
|
@ -124,7 +124,7 @@ RSpec.describe Jekyll::SeoTag::JSONLDDrop do
|
|||
end
|
||||
|
||||
context "with image as a hash" do
|
||||
let(:image) { { "path" => "image", "height" => 5, "width" => 10 } }
|
||||
let(:image) { { "path" => "/image", "height" => 5, "width" => 10 } }
|
||||
|
||||
it "returns the image as a hash" do
|
||||
expect(subject).to have_key("image")
|
||||
|
|
|
|||
|
|
@ -124,6 +124,7 @@ RSpec.describe Jekyll::SeoTag do
|
|||
it "uses the page description" do
|
||||
expect(output).to match(%r!<meta name="description" content="foo" />!)
|
||||
expect(output).to match(%r!<meta property="og:description" content="foo" />!)
|
||||
expect(output).to match(%r!<meta property="twitter:description" content="foo" />!)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -133,6 +134,7 @@ RSpec.describe Jekyll::SeoTag do
|
|||
it "uses the page excerpt when no page description exists" do
|
||||
expect(output).to match(%r!<meta name="description" content="foo" />!)
|
||||
expect(output).to match(%r!<meta property="og:description" content="foo" />!)
|
||||
expect(output).to match(%r!<meta property="twitter:description" content="foo" />!)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -142,6 +144,7 @@ RSpec.describe Jekyll::SeoTag do
|
|||
it "uses the site description when no page description nor excerpt exist" do
|
||||
expect(output).to match(%r!<meta name="description" content="foo" />!)
|
||||
expect(output).to match(%r!<meta property="og:description" content="foo" />!)
|
||||
expect(output).to match(%r!<meta property="twitter:description" content="foo" />!)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -347,6 +350,23 @@ RSpec.describe Jekyll::SeoTag do
|
|||
it "removes null values from JSON-LD" do
|
||||
expect(output).to_not match(%r!:null!)
|
||||
end
|
||||
|
||||
context "description" do
|
||||
context "with page.seo_description_max_words" do
|
||||
let(:meta) do
|
||||
{
|
||||
"title" => "post",
|
||||
"description" => "For a long time, I went to bed early",
|
||||
"image" => "/img.png",
|
||||
"seo_description_max_words" => 6,
|
||||
}
|
||||
end
|
||||
|
||||
it "truncates the description" do
|
||||
expect(json_data["description"]).to eql("For a long time, I went…")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ CONFIG_DEFAULTS = {
|
|||
}.freeze
|
||||
|
||||
def make_page(options = {})
|
||||
page = Jekyll::Page.new site, CONFIG_DEFAULTS["source"], "", "page.md"
|
||||
page = Jekyll::Page.new site, CONFIG_DEFAULTS["source"], options.delete("dir") || "", "page.md"
|
||||
page.data = options
|
||||
page
|
||||
end
|
||||
|
|
|
|||
Loading…
Reference in New Issue