jekyll-stats 0.2.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 +4 -4
- data/CHANGELOG.md +11 -0
- data/README.md +29 -0
- data/lib/jekyll-stats/command.rb +1 -0
- data/lib/jekyll-stats/formatter.rb +28 -0
- data/lib/jekyll-stats/stats_calculator.rb +74 -0
- data/lib/jekyll-stats/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5c9845c1c909307a6ee955d23c51412d24c1a12789d7144a817f8ac619bdaa91
|
|
4
|
+
data.tar.gz: ec4fdcf95f6b96fca65dac435e0edd307fff6c5ac7ea416e44bb473a0ee29d85
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b88c1fb528e0fa65d53c7d22a3580a875b40b18d74f418128457c8faaa8b3bce8fa323e675ae5c64f06d19933cdb85731d3d8218016559c4f35a2b0d79ef4066
|
|
7
|
+
data.tar.gz: 279c44108897e9686dba753f11aadb8947bdc1fb95ae466ff8bbc5f306364689168ea476ab2cd1500982c4e92e03c62a9d814f6ba91f2599b4d52dea6dd6a494
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
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
|
+
|
|
8
|
+
## [0.3.0] - 2026-08-30
|
|
9
|
+
|
|
10
|
+
- Add `internal_links` to stats: posts ranked by inbound links from other posts
|
|
11
|
+
- Source posts can be excluded by tag via `jekyll-stats.link_source_exclude_tags` in `_config.yml`
|
|
12
|
+
- `--json` no longer prints "Loading site..." to stdout, so output pipes cleanly to `jq`
|
|
13
|
+
|
|
3
14
|
## [0.2.0] - 2026-01-03
|
|
4
15
|
|
|
5
16
|
- Add `--tags` / `-t` option to filter stats by tags
|
data/README.md
CHANGED
|
@@ -81,10 +81,39 @@ 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
|
+
],
|
|
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
|
+
},
|
|
84
95
|
"drafts_count": 3
|
|
85
96
|
}
|
|
86
97
|
```
|
|
87
98
|
|
|
99
|
+
### Internal links
|
|
100
|
+
|
|
101
|
+
`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.
|
|
102
|
+
|
|
103
|
+
To stop link-list style posts (year in review, roundups) inflating the counts, exclude them as sources by tag in `_config.yml`:
|
|
104
|
+
|
|
105
|
+
```yaml
|
|
106
|
+
jekyll-stats:
|
|
107
|
+
link_source_exclude_tags:
|
|
108
|
+
- roundup
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Excluded posts still appear as targets; they just don't contribute outbound links.
|
|
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
|
+
|
|
88
117
|
## Building a Stats Page
|
|
89
118
|
|
|
90
119
|
After running `jekyll stats --save`, create a stats page using Liquid:
|
data/lib/jekyll-stats/command.rb
CHANGED
|
@@ -34,6 +34,16 @@ 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
|
+
|
|
42
|
+
if stats[:external_links][:domains].any?
|
|
43
|
+
lines << ""
|
|
44
|
+
lines << top_external_domains
|
|
45
|
+
end
|
|
46
|
+
|
|
37
47
|
if stats[:drafts_count].positive?
|
|
38
48
|
lines << ""
|
|
39
49
|
lines << "Drafts: #{stats[:drafts_count]}"
|
|
@@ -99,6 +109,24 @@ module JekyllStats
|
|
|
99
109
|
"Categories:\n #{cat_strs.join(" | ")}"
|
|
100
110
|
end
|
|
101
111
|
|
|
112
|
+
def most_linked_posts
|
|
113
|
+
links = stats[:internal_links].first(5)
|
|
114
|
+
lines = ["Most Linked Posts:"]
|
|
115
|
+
links.each do |l|
|
|
116
|
+
lines << " #{l[:inbound_count].to_s.rjust(2)} #{truncate(l[:title], 50)}"
|
|
117
|
+
end
|
|
118
|
+
lines.join("\n")
|
|
119
|
+
end
|
|
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
|
+
|
|
102
130
|
def format_number(n)
|
|
103
131
|
n.to_s.reverse.gsub(/(\d{3})(?=\d)/, '\\1,').reverse
|
|
104
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
|
|
@@ -40,6 +42,8 @@ module JekyllStats
|
|
|
40
42
|
posts_by_day_of_week: posts_by_day_of_week(posts),
|
|
41
43
|
tags: tag_counts(posts),
|
|
42
44
|
categories: category_counts(posts),
|
|
45
|
+
internal_links: internal_links(posts),
|
|
46
|
+
external_links: external_links(posts),
|
|
43
47
|
drafts_count: drafts_count
|
|
44
48
|
}
|
|
45
49
|
end
|
|
@@ -147,6 +151,74 @@ module JekyllStats
|
|
|
147
151
|
.map { |name, count| { name: name, count: count } }
|
|
148
152
|
end
|
|
149
153
|
|
|
154
|
+
def internal_links(posts)
|
|
155
|
+
by_url = posts.each_with_object({}) { |p, h| h[normalize_url(p.url)] = p }
|
|
156
|
+
exclude = Array(site.config.dig("jekyll-stats", "link_source_exclude_tags")).map { |t| normalize_tag(t) }
|
|
157
|
+
site_url = site.config["url"].to_s
|
|
158
|
+
|
|
159
|
+
inbound = Hash.new { |h, k| h[k] = [] }
|
|
160
|
+
posts.each do |post|
|
|
161
|
+
post_tags = (post.data["tags"] || []).map { |t| normalize_tag(t) }
|
|
162
|
+
next if exclude.any? && (exclude & post_tags).any?
|
|
163
|
+
|
|
164
|
+
post.content.to_s.scan(%r{(?:\]\(|<a\s[^>]*href=["'])([^"'\s)]+)}i).flatten.each do |href|
|
|
165
|
+
href = href.delete_prefix(site_url) unless site_url.empty?
|
|
166
|
+
next unless href.start_with?("/")
|
|
167
|
+
|
|
168
|
+
target = by_url[normalize_url(href)]
|
|
169
|
+
next unless target
|
|
170
|
+
next if target == post
|
|
171
|
+
|
|
172
|
+
inbound[target] << post
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
inbound.map { |target, sources|
|
|
177
|
+
{
|
|
178
|
+
url: target.url,
|
|
179
|
+
title: target.data["title"] || "(untitled)",
|
|
180
|
+
inbound_count: sources.uniq.size,
|
|
181
|
+
inbound_from: sources.uniq.map(&:url).sort
|
|
182
|
+
}
|
|
183
|
+
}.sort_by { |r| [-r[:inbound_count], r[:url]] }
|
|
184
|
+
end
|
|
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
|
+
|
|
218
|
+
def normalize_url(url)
|
|
219
|
+
url.sub(/[#?].*\z/, "").sub(%r{/index\.html\z}, "").chomp(".html").chomp("/")
|
|
220
|
+
end
|
|
221
|
+
|
|
150
222
|
def drafts_count
|
|
151
223
|
return 0 unless site.respond_to?(:drafts) && site.drafts
|
|
152
224
|
|
|
@@ -171,6 +243,8 @@ module JekyllStats
|
|
|
171
243
|
posts_by_day_of_week: %w[sunday monday tuesday wednesday thursday friday saturday].each_with_object({}) { |d, h| h[d] = 0 },
|
|
172
244
|
tags: [],
|
|
173
245
|
categories: [],
|
|
246
|
+
internal_links: [],
|
|
247
|
+
external_links: { total: 0, unique: 0, domains: [] },
|
|
174
248
|
drafts_count: 0
|
|
175
249
|
}
|
|
176
250
|
end
|
data/lib/jekyll-stats/version.rb
CHANGED
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.
|
|
4
|
+
version: 0.4.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.
|
|
80
|
+
rubygems_version: 4.0.17
|
|
81
81
|
specification_version: 4
|
|
82
82
|
summary: Jekyll plugin that generates site statistics
|
|
83
83
|
test_files: []
|