jekyll-theme-zer0 0.4.0 → 0.5.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.
@@ -0,0 +1,275 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'yaml'
5
+ require 'date'
6
+ require 'fileutils'
7
+
8
+ # Statistics Generator for Jekyll Site
9
+ # Analyzes site content and generates comprehensive statistics
10
+
11
+ class SiteStatisticsGenerator
12
+ def initialize(site_root = '.')
13
+ @site_root = File.expand_path(site_root)
14
+ @posts_dir = File.join(@site_root, '_posts')
15
+ @pages_dir = File.join(@site_root, 'pages')
16
+ @collections_dirs = [
17
+ File.join(@site_root, '_quests'),
18
+ File.join(@site_root, '_docs'),
19
+ File.join(@site_root, '_projects')
20
+ ]
21
+ @output_file = File.join(@site_root, '_data', 'content_statistics.yml')
22
+
23
+ @stats = {
24
+ 'generated_at' => Time.now.strftime('%Y-%m-%d %H:%M:%S'),
25
+ 'overview' => {},
26
+ 'categories' => {},
27
+ 'tags' => {},
28
+ 'content_breakdown' => {},
29
+ 'monthly_distribution' => {},
30
+ 'word_statistics' => {}
31
+ }
32
+ end
33
+
34
+ def generate!
35
+ puts "šŸ” Analyzing site content..."
36
+
37
+ analyze_posts
38
+ analyze_pages
39
+ analyze_collections
40
+ calculate_overview_metrics
41
+ sort_and_finalize_data
42
+ write_statistics_file
43
+
44
+ puts "āœ… Statistics generated successfully!"
45
+ puts "šŸ“Š Results saved to: #{@output_file}"
46
+ print_summary
47
+ end
48
+
49
+ private
50
+
51
+ def analyze_posts
52
+ posts_pattern = File.join(@posts_dir, '**', '*.{md,markdown,html}')
53
+ posts_pattern2 = File.join(@pages_dir, '_posts', '**', '*.{md,markdown,html}')
54
+
55
+ post_files = Dir[posts_pattern] + Dir[posts_pattern2]
56
+
57
+ puts "šŸ“ Found #{post_files.length} post files"
58
+
59
+ post_files.each do |file|
60
+ process_content_file(file, 'post')
61
+ end
62
+ end
63
+
64
+ def analyze_pages
65
+ # Look for standalone pages (not in _posts)
66
+ pages_pattern = File.join(@pages_dir, '**', '*.{md,markdown,html}')
67
+ page_files = Dir[pages_pattern].reject { |f| f.include?('_posts') }
68
+
69
+ # Also check root level pages
70
+ root_pages = Dir[File.join(@site_root, '*.{md,markdown,html}')]
71
+ page_files += root_pages
72
+
73
+ puts "šŸ“„ Found #{page_files.length} page files"
74
+
75
+ page_files.each do |file|
76
+ process_content_file(file, 'page')
77
+ end
78
+ end
79
+
80
+ def analyze_collections
81
+ @collections_dirs.each do |dir|
82
+ next unless Dir.exist?(dir)
83
+
84
+ collection_name = File.basename(dir).gsub(/^_/, '')
85
+ collection_files = Dir[File.join(dir, '**', '*.{md,markdown,html}')]
86
+
87
+ puts "šŸ“š Found #{collection_files.length} #{collection_name} files"
88
+
89
+ collection_files.each do |file|
90
+ process_content_file(file, collection_name)
91
+ end
92
+ end
93
+ end
94
+
95
+ def process_content_file(file_path, content_type)
96
+ return unless File.file?(file_path)
97
+
98
+ content = File.read(file_path)
99
+
100
+ # Extract front matter
101
+ if content =~ /\A---\s*\n(.*?)\n---\s*\n(.*)\z/m
102
+ front_matter_str = $1
103
+ body_content = $2
104
+
105
+ begin
106
+ front_matter = YAML.safe_load(front_matter_str, permitted_classes: [Date, Time]) || {}
107
+ rescue YAML::SyntaxError => e
108
+ puts "āš ļø YAML error in #{file_path}: #{e.message}"
109
+ return
110
+ rescue Psych::DisallowedClass => e
111
+ # Try again with basic safe_load
112
+ begin
113
+ front_matter = YAML.safe_load(front_matter_str) || {}
114
+ rescue => e2
115
+ puts "āš ļø YAML parsing error in #{file_path}: #{e2.message}"
116
+ return
117
+ end
118
+ end
119
+ else
120
+ # No front matter found
121
+ front_matter = {}
122
+ body_content = content
123
+ end
124
+
125
+ # Skip drafts
126
+ return if front_matter['draft'] == true
127
+
128
+ # Count this content
129
+ @stats['content_breakdown'][content_type] ||= 0
130
+ @stats['content_breakdown'][content_type] += 1
131
+
132
+ # Process categories
133
+ categories = front_matter['categories'] || front_matter['category']
134
+ if categories
135
+ categories = [categories] unless categories.is_a?(Array)
136
+ categories.each do |category|
137
+ next if category.nil? || category.to_s.strip.empty?
138
+ category_str = category.to_s.strip
139
+ @stats['categories'][category_str] ||= 0
140
+ @stats['categories'][category_str] += 1
141
+ end
142
+ end
143
+
144
+ # Process tags
145
+ tags = front_matter['tags'] || front_matter['tag']
146
+ if tags
147
+ tags = [tags] unless tags.is_a?(Array)
148
+ tags.each do |tag|
149
+ next if tag.nil? || tag.to_s.strip.empty?
150
+ tag_str = tag.to_s.strip.downcase
151
+ @stats['tags'][tag_str] ||= 0
152
+ @stats['tags'][tag_str] += 1
153
+ end
154
+ end
155
+
156
+ # Process dates for monthly distribution
157
+ if front_matter['date']
158
+ begin
159
+ # Handle different date formats
160
+ date_value = front_matter['date']
161
+ if date_value.is_a?(String)
162
+ date = Date.parse(date_value)
163
+ elsif date_value.respond_to?(:to_date)
164
+ date = date_value.to_date
165
+ else
166
+ date = Date.parse(date_value.to_s)
167
+ end
168
+
169
+ month_key = date.strftime('%Y-%m')
170
+ @stats['monthly_distribution'][month_key] ||= 0
171
+ @stats['monthly_distribution'][month_key] += 1
172
+ rescue => e
173
+ puts "āš ļø Date parsing error in #{file_path}: #{e.message} (date: #{front_matter['date']})"
174
+ # Skip this date
175
+ end
176
+ end
177
+
178
+ # Count words in content
179
+ word_count = count_words(body_content)
180
+ @stats['word_statistics'][File.basename(file_path)] = {
181
+ 'words' => word_count,
182
+ 'type' => content_type,
183
+ 'title' => front_matter['title'] || File.basename(file_path, '.*')
184
+ }
185
+ end
186
+
187
+ def count_words(content)
188
+ # Remove markdown syntax and count words
189
+ text = content.gsub(/[#*_`\[\](){}]/, ' ') # Remove markdown syntax
190
+ .gsub(/!\[.*?\]\(.*?\)/, ' ') # Remove images
191
+ .gsub(/\[.*?\]\(.*?\)/, ' ') # Remove links
192
+ .gsub(/```.*?```/m, ' ') # Remove code blocks
193
+ .gsub(/`.*?`/, ' ') # Remove inline code
194
+ .gsub(/<!--.*?-->/m, ' ') # Remove comments
195
+ .gsub(/\s+/, ' ') # Normalize whitespace
196
+ .strip
197
+
198
+ text.split.length
199
+ end
200
+
201
+ def calculate_overview_metrics
202
+ total_content = @stats['content_breakdown'].values.sum
203
+ total_words = @stats['word_statistics'].values.sum { |stat| stat['words'] }
204
+
205
+ @stats['overview'] = {
206
+ 'total_posts' => @stats['content_breakdown']['post'] || 0,
207
+ 'total_pages' => @stats['content_breakdown']['page'] || 0,
208
+ 'total_content' => total_content,
209
+ 'total_categories' => @stats['categories'].keys.length,
210
+ 'total_tags' => @stats['tags'].keys.length,
211
+ 'total_words' => total_words,
212
+ 'average_words_per_post' => total_content > 0 ? (total_words.to_f / total_content).round(1) : 0
213
+ }
214
+ end
215
+
216
+ def sort_and_finalize_data
217
+ # Sort categories and tags by count (descending)
218
+ @stats['categories'] = @stats['categories'].sort_by { |_, count| -count }.to_h
219
+ @stats['tags'] = @stats['tags'].sort_by { |_, count| -count }.to_h
220
+
221
+ # Sort monthly distribution by date
222
+ @stats['monthly_distribution'] = @stats['monthly_distribution'].sort.to_h
223
+
224
+ # Convert to arrays for Jekyll (categories and tags)
225
+ @stats['categories'] = @stats['categories'].map { |name, count| [name, count] }
226
+ @stats['tags'] = @stats['tags'].map { |name, count| [name, count] }
227
+ end
228
+
229
+ def write_statistics_file
230
+ # Ensure _data directory exists
231
+ FileUtils.mkdir_p(File.dirname(@output_file))
232
+
233
+ # Write YAML file
234
+ File.open(@output_file, 'w') do |file|
235
+ file.write(@stats.to_yaml)
236
+ end
237
+ end
238
+
239
+ def print_summary
240
+ puts "\nšŸ“Š STATISTICS SUMMARY"
241
+ puts "=" * 50
242
+ puts "šŸ“ Total Posts: #{@stats['overview']['total_posts']}"
243
+ puts "šŸ“„ Total Pages: #{@stats['overview']['total_pages']}"
244
+ puts "šŸ“š Total Content: #{@stats['overview']['total_content']}"
245
+ puts "šŸ“‚ Categories: #{@stats['overview']['total_categories']}"
246
+ puts "šŸ·ļø Tags: #{@stats['overview']['total_tags']}"
247
+ puts "šŸ“ Total Words: #{@stats['overview']['total_words'].to_s.reverse.gsub(/(\d{3})(?=\d)/, '\\1,').reverse}"
248
+ puts "šŸ“Š Average Words/Post: #{@stats['overview']['average_words_per_post']}"
249
+
250
+ if @stats['categories'].any?
251
+ puts "\nšŸ† TOP CATEGORIES:"
252
+ @stats['categories'].first(5).each_with_index do |(name, count), index|
253
+ puts " #{index + 1}. #{name}: #{count} posts"
254
+ end
255
+ end
256
+
257
+ if @stats['tags'].any?
258
+ puts "\nšŸ·ļø TOP TAGS:"
259
+ @stats['tags'].first(10).each_with_index do |(name, count), index|
260
+ puts " #{index + 1}. #{name}: #{count} uses"
261
+ end
262
+ end
263
+
264
+ puts "\nāœ… Statistics generation complete!"
265
+ end
266
+ end
267
+
268
+ # Run the generator if this script is executed directly
269
+ if __FILE__ == $0
270
+ puts "šŸš€ Starting Jekyll Site Statistics Generation..."
271
+ puts "šŸ“ Working directory: #{Dir.pwd}"
272
+
273
+ generator = SiteStatisticsGenerator.new
274
+ generator.generate!
275
+ end
@@ -0,0 +1,15 @@
1
+ - title: Home
2
+ icon: bi-house
3
+ url: /home
4
+ sublinks:
5
+ - title: Style
6
+ url: /home/style/
7
+ - title: Map
8
+ icon: bi-map
9
+ url: /sitemap/
10
+ sublinks:
11
+ - title: Search
12
+ url: /search/
13
+ - title: Roadmap
14
+ url: /roadmap/
15
+
@@ -14,14 +14,11 @@
14
14
  sublinks:
15
15
  - title: All Posts
16
16
  url: /posts/
17
- - title: Stats
18
- url: /stats/
19
17
  - title: Docs
20
18
  url: /docs/
21
19
  sublinks:
22
20
  - title: Jekyll
23
21
  url: /docs/jekyll/
24
-
25
22
  - title: About
26
23
  url: /about/
27
24
  sublinks:
@@ -29,5 +26,5 @@
29
26
  url: /about/config/
30
27
  - title: Theme
31
28
  url: /about/theme/
32
- - title: Sitemap
33
- url: /about/sitemap/
29
+ - title: Site Map
30
+ url: /sitemap/