jekyll-stats 0.3.0 → 0.4.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: 3465b65818c34b7a72b5b76d9bb242a37487837e5639927ae65c7d9a00b9ac62
4
- data.tar.gz: 9e408de4fcfb55f1581a2b1905018852095cd4ae5c9a4a2f7592e252e24e42b0
3
+ metadata.gz: 5c9845c1c909307a6ee955d23c51412d24c1a12789d7144a817f8ac619bdaa91
4
+ data.tar.gz: ec4fdcf95f6b96fca65dac435e0edd307fff6c5ac7ea416e44bb473a0ee29d85
5
5
  SHA512:
6
- metadata.gz: bd03f85ad69f529b003a211a7004acdf0a30851f0a2084abab6f3f142a511a620d7c3d4f755f4dcab6abc2286c55bb5f25c7bec610ccf565e0f012e975ba21d7
7
- data.tar.gz: d42e6b8897070b6529b1cc32f7e6e52c11563e2890bcfdd5c03f4a2237216cb2afb80c62d2ec94489fe6433ffb3706e954d308e31c72a1b4f67fa7a486716ab1
6
+ metadata.gz: b88c1fb528e0fa65d53c7d22a3580a875b40b18d74f418128457c8faaa8b3bce8fa323e675ae5c64f06d19933cdb85731d3d8218016559c4f35a2b0d79ef4066
7
+ data.tar.gz: 279c44108897e9686dba753f11aadb8947bdc1fb95ae466ff8bbc5f306364689168ea476ab2cd1500982c4e92e03c62a9d814f6ba91f2599b4d52dea6dd6a494
data/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.4.0] - 2026-08-30
4
+
5
+ - Add `external_links` to stats: total, unique, and per-domain counts of outbound links
6
+ - Each domain entry includes `posts` (distinct source posts) so single-post link lists are visible
7
+
3
8
  ## [0.3.0] - 2026-08-30
4
9
 
5
10
  - Add `internal_links` to stats: posts ranked by inbound links from other posts
data/README.md CHANGED
@@ -84,6 +84,14 @@ The `--save` flag writes `_data/stats.json` with this structure:
84
84
  "internal_links": [
85
85
  { "url": "/2024/01/foo", "title": "Foo", "inbound_count": 7, "inbound_from": ["/2024/03/bar", "/2024/06/baz"] }
86
86
  ],
87
+ "external_links": {
88
+ "total": 4637,
89
+ "unique": 3723,
90
+ "domains": [
91
+ { "host": "github.com", "count": 1380, "posts": 172 },
92
+ { "host": "en.wikipedia.org", "count": 94, "posts": 61 }
93
+ ]
94
+ },
87
95
  "drafts_count": 3
88
96
  }
89
97
  ```
@@ -102,6 +110,10 @@ jekyll-stats:
102
110
 
103
111
  Excluded posts still appear as targets; they just don't contribute outbound links.
104
112
 
113
+ ### External links
114
+
115
+ `external_links` counts outbound `http(s)` links to hosts other than your site's `url:`. `total` is raw link occurrences, `unique` is distinct URLs, and each `domains` entry has `count` (distinct URLs at that host) and `posts` (distinct source posts). A high `count` with a low `posts` means one or two posts account for most links to that host. The same `link_source_exclude_tags` config applies.
116
+
105
117
  ## Building a Stats Page
106
118
 
107
119
  After running `jekyll stats --save`, create a stats page using Liquid:
@@ -39,6 +39,11 @@ module JekyllStats
39
39
  lines << most_linked_posts
40
40
  end
41
41
 
42
+ if stats[:external_links][:domains].any?
43
+ lines << ""
44
+ lines << top_external_domains
45
+ end
46
+
42
47
  if stats[:drafts_count].positive?
43
48
  lines << ""
44
49
  lines << "Drafts: #{stats[:drafts_count]}"
@@ -113,6 +118,15 @@ module JekyllStats
113
118
  lines.join("\n")
114
119
  end
115
120
 
121
+ def top_external_domains
122
+ ext = stats[:external_links]
123
+ lines = ["External Links: #{format_number(ext[:total])} (#{format_number(ext[:unique])} unique, #{ext[:domains].size} hosts)"]
124
+ ext[:domains].first(5).each do |d|
125
+ lines << " #{d[:count].to_s.rjust(4)} #{d[:host]} (#{d[:posts]} posts)"
126
+ end
127
+ lines.join("\n")
128
+ end
129
+
116
130
  def format_number(n)
117
131
  n.to_s.reverse.gsub(/(\d{3})(?=\d)/, '\\1,').reverse
118
132
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "uri"
4
+
3
5
  module JekyllStats
4
6
  class StatsCalculator
5
7
  WORDS_PER_MINUTE = 200
@@ -41,6 +43,7 @@ module JekyllStats
41
43
  tags: tag_counts(posts),
42
44
  categories: category_counts(posts),
43
45
  internal_links: internal_links(posts),
46
+ external_links: external_links(posts),
44
47
  drafts_count: drafts_count
45
48
  }
46
49
  end
@@ -180,6 +183,38 @@ module JekyllStats
180
183
  }.sort_by { |r| [-r[:inbound_count], r[:url]] }
181
184
  end
182
185
 
186
+ def external_links(posts)
187
+ exclude = Array(site.config.dig("jekyll-stats", "link_source_exclude_tags")).map { |t| normalize_tag(t) }
188
+ site_host = (URI.parse(site.config["url"].to_s).host rescue nil)
189
+
190
+ urls = []
191
+ posts.each do |post|
192
+ post_tags = (post.data["tags"] || []).map { |t| normalize_tag(t) }
193
+ next if exclude.any? && (exclude & post_tags).any?
194
+
195
+ post.content.to_s.scan(%r{(?:\]\(|<a\s[^>]*href=["'])(https?://[^"'\s)]+)}i).flatten.each do |href|
196
+ host = URI.parse(href).host rescue nil
197
+ next if host.nil?
198
+ next if site_host && host == site_host
199
+
200
+ urls << [href, host, post]
201
+ end
202
+ end
203
+
204
+ unique = urls.uniq { |u, _, _| u }
205
+ domains = urls.group_by { |_, h, _| h }
206
+ .map { |host, us|
207
+ {
208
+ host: host,
209
+ count: us.map { |u, _, _| u }.uniq.size,
210
+ posts: us.map { |_, _, p| p }.uniq.size
211
+ }
212
+ }
213
+ .sort_by { |d| [-d[:count], d[:host]] }
214
+
215
+ { total: urls.size, unique: unique.size, domains: domains }
216
+ end
217
+
183
218
  def normalize_url(url)
184
219
  url.sub(/[#?].*\z/, "").sub(%r{/index\.html\z}, "").chomp(".html").chomp("/")
185
220
  end
@@ -209,6 +244,7 @@ module JekyllStats
209
244
  tags: [],
210
245
  categories: [],
211
246
  internal_links: [],
247
+ external_links: { total: 0, unique: 0, domains: [] },
212
248
  drafts_count: 0
213
249
  }
214
250
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module JekyllStats
4
- VERSION = "0.3.0"
4
+ VERSION = "0.4.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.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Nesbitt