jekyll-stats 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ab21769c8de5f9ff18a6ad985b0a0240dbf00c29be751b3f0aee27f8c313a257
4
- data.tar.gz: '071349d927967afb2c9164d9382a56175d9aa106d818ac267d32930169aca4e7'
3
+ metadata.gz: 3465b65818c34b7a72b5b76d9bb242a37487837e5639927ae65c7d9a00b9ac62
4
+ data.tar.gz: 9e408de4fcfb55f1581a2b1905018852095cd4ae5c9a4a2f7592e252e24e42b0
5
5
  SHA512:
6
- metadata.gz: c1f4bc991fa3230b0e0c3d5c0a471495738a0a853021ba257cc5501cf6748fb8ed567892b0005f5166071c3a2e2aab398380b83fbcb90a11819d2896583957ce
7
- data.tar.gz: de7e7fc767997a55e345f63df9214fc46501790ff9a3f7fba3ee166968f43ce40ec1fec8ed8f4002a4c39a3c5de95ab6bc06307ecb3881fc19b191466b0fc28c
6
+ metadata.gz: bd03f85ad69f529b003a211a7004acdf0a30851f0a2084abab6f3f142a511a620d7c3d4f755f4dcab6abc2286c55bb5f25c7bec610ccf565e0f012e975ba21d7
7
+ data.tar.gz: d42e6b8897070b6529b1cc32f7e6e52c11563e2890bcfdd5c03f4a2237216cb2afb80c62d2ec94489fe6433ffb3706e954d308e31c72a1b4f67fa7a486716ab1
data/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.3.0] - 2026-08-30
4
+
5
+ - Add `internal_links` to stats: posts ranked by inbound links from other posts
6
+ - Source posts can be excluded by tag via `jekyll-stats.link_source_exclude_tags` in `_config.yml`
7
+ - `--json` no longer prints "Loading site..." to stdout, so output pipes cleanly to `jq`
8
+
3
9
  ## [0.2.0] - 2026-01-03
4
10
 
5
11
  - Add `--tags` / `-t` option to filter stats by tags
data/README.md CHANGED
@@ -81,10 +81,27 @@ The `--save` flag writes `_data/stats.json` with this structure:
81
81
  "posts_by_day_of_week": { "monday": 23, "tuesday": 18, "wednesday": 15, "thursday": 20, "friday": 22, "saturday": 14, "sunday": 15 },
82
82
  "tags": [{ "name": "ruby", "count": 34 }, { "name": "opensource", "count": 28 }],
83
83
  "categories": [{ "name": "code", "count": 45 }, { "name": "cars", "count": 32 }],
84
+ "internal_links": [
85
+ { "url": "/2024/01/foo", "title": "Foo", "inbound_count": 7, "inbound_from": ["/2024/03/bar", "/2024/06/baz"] }
86
+ ],
84
87
  "drafts_count": 3
85
88
  }
86
89
  ```
87
90
 
91
+ ### Internal links
92
+
93
+ `internal_links` lists posts ranked by how many other posts link to them. It matches markdown and `<a href>` links that point at another post's URL, either as a root-relative path or an absolute URL under your site's `url:`. Self-links and duplicate links from the same source post are ignored.
94
+
95
+ To stop link-list style posts (year in review, roundups) inflating the counts, exclude them as sources by tag in `_config.yml`:
96
+
97
+ ```yaml
98
+ jekyll-stats:
99
+ link_source_exclude_tags:
100
+ - roundup
101
+ ```
102
+
103
+ Excluded posts still appear as targets; they just don't contribute outbound links.
104
+
88
105
  ## Building a Stats Page
89
106
 
90
107
  After running `jekyll stats --save`, create a stats page using Liquid:
@@ -25,6 +25,7 @@ module JekyllStats
25
25
  end
26
26
 
27
27
  def process(options)
28
+ Jekyll.logger.log_level = :error if options["json"]
28
29
  options = configuration_from_options(options)
29
30
  site = Jekyll::Site.new(options)
30
31
 
@@ -34,6 +34,11 @@ module JekyllStats
34
34
  lines << categories_line
35
35
  end
36
36
 
37
+ if stats[:internal_links].any?
38
+ lines << ""
39
+ lines << most_linked_posts
40
+ end
41
+
37
42
  if stats[:drafts_count].positive?
38
43
  lines << ""
39
44
  lines << "Drafts: #{stats[:drafts_count]}"
@@ -99,6 +104,15 @@ module JekyllStats
99
104
  "Categories:\n #{cat_strs.join(" | ")}"
100
105
  end
101
106
 
107
+ def most_linked_posts
108
+ links = stats[:internal_links].first(5)
109
+ lines = ["Most Linked Posts:"]
110
+ links.each do |l|
111
+ lines << " #{l[:inbound_count].to_s.rjust(2)} #{truncate(l[:title], 50)}"
112
+ end
113
+ lines.join("\n")
114
+ end
115
+
102
116
  def format_number(n)
103
117
  n.to_s.reverse.gsub(/(\d{3})(?=\d)/, '\\1,').reverse
104
118
  end
@@ -40,6 +40,7 @@ module JekyllStats
40
40
  posts_by_day_of_week: posts_by_day_of_week(posts),
41
41
  tags: tag_counts(posts),
42
42
  categories: category_counts(posts),
43
+ internal_links: internal_links(posts),
43
44
  drafts_count: drafts_count
44
45
  }
45
46
  end
@@ -147,6 +148,42 @@ module JekyllStats
147
148
  .map { |name, count| { name: name, count: count } }
148
149
  end
149
150
 
151
+ def internal_links(posts)
152
+ by_url = posts.each_with_object({}) { |p, h| h[normalize_url(p.url)] = p }
153
+ exclude = Array(site.config.dig("jekyll-stats", "link_source_exclude_tags")).map { |t| normalize_tag(t) }
154
+ site_url = site.config["url"].to_s
155
+
156
+ inbound = Hash.new { |h, k| h[k] = [] }
157
+ posts.each do |post|
158
+ post_tags = (post.data["tags"] || []).map { |t| normalize_tag(t) }
159
+ next if exclude.any? && (exclude & post_tags).any?
160
+
161
+ post.content.to_s.scan(%r{(?:\]\(|<a\s[^>]*href=["'])([^"'\s)]+)}i).flatten.each do |href|
162
+ href = href.delete_prefix(site_url) unless site_url.empty?
163
+ next unless href.start_with?("/")
164
+
165
+ target = by_url[normalize_url(href)]
166
+ next unless target
167
+ next if target == post
168
+
169
+ inbound[target] << post
170
+ end
171
+ end
172
+
173
+ inbound.map { |target, sources|
174
+ {
175
+ url: target.url,
176
+ title: target.data["title"] || "(untitled)",
177
+ inbound_count: sources.uniq.size,
178
+ inbound_from: sources.uniq.map(&:url).sort
179
+ }
180
+ }.sort_by { |r| [-r[:inbound_count], r[:url]] }
181
+ end
182
+
183
+ def normalize_url(url)
184
+ url.sub(/[#?].*\z/, "").sub(%r{/index\.html\z}, "").chomp(".html").chomp("/")
185
+ end
186
+
150
187
  def drafts_count
151
188
  return 0 unless site.respond_to?(:drafts) && site.drafts
152
189
 
@@ -171,6 +208,7 @@ module JekyllStats
171
208
  posts_by_day_of_week: %w[sunday monday tuesday wednesday thursday friday saturday].each_with_object({}) { |d, h| h[d] = 0 },
172
209
  tags: [],
173
210
  categories: [],
211
+ internal_links: [],
174
212
  drafts_count: 0
175
213
  }
176
214
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module JekyllStats
4
- VERSION = "0.2.0"
4
+ VERSION = "0.3.0"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jekyll-stats
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Nesbitt
@@ -77,7 +77,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
77
77
  - !ruby/object:Gem::Version
78
78
  version: '0'
79
79
  requirements: []
80
- rubygems_version: 4.0.1
80
+ rubygems_version: 4.0.17
81
81
  specification_version: 4
82
82
  summary: Jekyll plugin that generates site statistics
83
83
  test_files: []