jekyll-paginate-v3 0.1.0.alpha

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.
Files changed (56) hide show
  1. checksums.yaml +7 -0
  2. data/lib/jekyll-paginate-v3/config/defaults.rb +107 -0
  3. data/lib/jekyll-paginate-v3/config/normaliser/core.rb +127 -0
  4. data/lib/jekyll-paginate-v3/config/normaliser/legacy_compatibility.rb +155 -0
  5. data/lib/jekyll-paginate-v3/config/normaliser/site_and_templates.rb +616 -0
  6. data/lib/jekyll-paginate-v3/config/normaliser.rb +5 -0
  7. data/lib/jekyll-paginate-v3/generators/pagination_generator.rb +256 -0
  8. data/lib/jekyll-paginate-v3/pagination/model/core.rb +554 -0
  9. data/lib/jekyll-paginate-v3/pagination/model/group_navigation_and_pages.rb +214 -0
  10. data/lib/jekyll-paginate-v3/pagination/model/pagination_pipeline.rb +413 -0
  11. data/lib/jekyll-paginate-v3/pagination/model.rb +5 -0
  12. data/lib/jekyll-paginate-v3/pagination/pages/document.rb +89 -0
  13. data/lib/jekyll-paginate-v3/pagination/pages/page.rb +86 -0
  14. data/lib/jekyll-paginate-v3/pagination/pages/shadow_page.rb +32 -0
  15. data/lib/jekyll-paginate-v3/pagination/paginator/compatibility_aliases.rb +18 -0
  16. data/lib/jekyll-paginate-v3/pagination/paginator/core.rb +392 -0
  17. data/lib/jekyll-paginate-v3/pagination/paginator/group_payload.rb +51 -0
  18. data/lib/jekyll-paginate-v3/pagination/paginator/group_reference.rb +48 -0
  19. data/lib/jekyll-paginate-v3/pagination/paginator/index_reference.rb +57 -0
  20. data/lib/jekyll-paginate-v3/pagination/paginator/trail_reference.rb +39 -0
  21. data/lib/jekyll-paginate-v3/pagination/paginator.rb +8 -0
  22. data/lib/jekyll-paginate-v3/query/filter/core.rb +175 -0
  23. data/lib/jekyll-paginate-v3/query/filter/evaluation.rb +212 -0
  24. data/lib/jekyll-paginate-v3/query/filter/formatting.rb +79 -0
  25. data/lib/jekyll-paginate-v3/query/filter/normalisation.rb +603 -0
  26. data/lib/jekyll-paginate-v3/query/filter.rb +7 -0
  27. data/lib/jekyll-paginate-v3/query/parser.rb +149 -0
  28. data/lib/jekyll-paginate-v3/query/sorter.rb +182 -0
  29. data/lib/jekyll-paginate-v3/support/frontmatter_path.rb +218 -0
  30. data/lib/jekyll-paginate-v3/support/loose_scalar.rb +116 -0
  31. data/lib/jekyll-paginate-v3/support/processed_value.rb +187 -0
  32. data/lib/jekyll-paginate-v3/support/string_array.rb +158 -0
  33. data/lib/jekyll-paginate-v3/templates/builder/core.rb +136 -0
  34. data/lib/jekyll-paginate-v3/templates/builder/definition_normalisation.rb +85 -0
  35. data/lib/jekyll-paginate-v3/templates/builder/index_entries.rb +199 -0
  36. data/lib/jekyll-paginate-v3/templates/builder/token_metadata.rb +213 -0
  37. data/lib/jekyll-paginate-v3/templates/builder.rb +5 -0
  38. data/lib/jekyll-paginate-v3/templates/document_template.rb +87 -0
  39. data/lib/jekyll-paginate-v3/templates/grouped_index/alphabetic_and_shared_helpers.rb +305 -0
  40. data/lib/jekyll-paginate-v3/templates/grouped_index/config_normalisation.rb +368 -0
  41. data/lib/jekyll-paginate-v3/templates/grouped_index/core.rb +80 -0
  42. data/lib/jekyll-paginate-v3/templates/grouped_index/mode_detection.rb +272 -0
  43. data/lib/jekyll-paginate-v3/templates/grouped_index/numeric_and_datetime_entries.rb +441 -0
  44. data/lib/jekyll-paginate-v3/templates/grouped_index.rb +9 -0
  45. data/lib/jekyll-paginate-v3/templates/page_template.rb +80 -0
  46. data/lib/jekyll-paginate-v3/templates/variant_expander.rb +744 -0
  47. data/lib/jekyll-paginate-v3/utils/core.rb +102 -0
  48. data/lib/jekyll-paginate-v3/utils/formatting.rb +52 -0
  49. data/lib/jekyll-paginate-v3/utils/items.rb +143 -0
  50. data/lib/jekyll-paginate-v3/utils/logger.rb +77 -0
  51. data/lib/jekyll-paginate-v3/utils/nested_data.rb +61 -0
  52. data/lib/jekyll-paginate-v3/utils/paths.rb +56 -0
  53. data/lib/jekyll-paginate-v3/version.rb +11 -0
  54. data/lib/jekyll-paginate-v3.rb +37 -0
  55. data/readme.md +512 -0
  56. metadata +201 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: cfe3b140435a9e8b5011cc8ff234547046323329bf1fba18dc0cdbc649fb1266
4
+ data.tar.gz: '039c94ad587c94ce7ebf66a828aa856b0092cf9f28a489da2aedc0e7102c2ed7'
5
+ SHA512:
6
+ metadata.gz: cf32cbe5e10d028b59b8f9807ae3d52bb63421eb0ffa57d3f104b7bdc953f483da24d1453d5974bfcba186e969674ebf1ee1ae39a8e8993456f1033661a873da
7
+ data.tar.gz: 2278e72f4b899f3cfaf21e62eb55c776fff719bd1e88c2374206f627a070e4dc35cf6bca7613c189590e8be3c5657c40f0e2ad2fd21b4776f48deb7c6b90aa92
@@ -0,0 +1,107 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jekyll
4
+ module Plugins
5
+ module PaginateV3
6
+
7
+ # Default configuration for jekyll-paginate-v3.
8
+ #
9
+ # These defaults intentionally describe the new v3 behaviour. Legacy
10
+ # v1/v2 behaviour is layered on top through compatibility profiles.
11
+ module Config
12
+ KEYWORD_DEFAULTS = {
13
+ 'pages' => 'pages',
14
+ 'all' => 'all',
15
+ 'everything' => 'everything',
16
+ 'self' => 'self',
17
+ 'shadow' => 'shadow',
18
+ 'clone' => 'clone',
19
+ 'now' => 'now',
20
+ 'today' => 'today',
21
+ 'day' => 'day',
22
+ 'month' => 'month',
23
+ 'year' => 'year',
24
+ 'hour' => 'hour',
25
+ 'minute' => 'minute',
26
+ 'second' => 'second',
27
+ 'items' => 'items'
28
+ }.freeze
29
+
30
+ DEFAULTS = {
31
+ 'enabled' => true,
32
+ 'compatibility' => nil,
33
+ 'debug' => false,
34
+ 'syntax' => {
35
+ 'separator' => '.',
36
+ 'split' => ','
37
+ },
38
+ 'keywords' => {},
39
+ 'equivalents' => [
40
+ ['tag', 'tags'],
41
+ ['category', 'categories']
42
+ ],
43
+ 'items' => nil,
44
+ 'collection' => ['self', 'shadow'],
45
+ 'filters' => nil,
46
+ 'sort' => 'date desc',
47
+ 'per_page' => 10,
48
+ 'limit' => 0,
49
+ 'offset' => 0,
50
+ 'trail' => 5,
51
+ 'title' => ':title - :num',
52
+ 'permalink' => ':num',
53
+ 'layout' => nil,
54
+ 'layouts' => [],
55
+ 'group' => nil,
56
+ 'slugify' => {
57
+ 'mode' => 'default',
58
+ 'lowercase' => true
59
+ },
60
+ 'templates' => {
61
+ 'location' => 'pages',
62
+ 'generate' => []
63
+ }
64
+ }.freeze
65
+
66
+ # Compatibility overlays merged after DEFAULTS but before user config.
67
+ #
68
+ # v2 keeps historical public contract where the paginator payload key is
69
+ # `posts` and where legacy shorthand keys are accepted and up-migrated.
70
+ #
71
+ # v1 keeps legacy config keys working while staying on the shared v3
72
+ # pagination pipeline.
73
+ COMPATIBILITY_PROFILES = {
74
+ 'v2' => {
75
+ 'enabled' => true,
76
+ 'syntax' => {
77
+ 'separator' => ':'
78
+ },
79
+ 'keywords' => {
80
+ 'all' => 'collections',
81
+ 'items' => 'posts'
82
+ },
83
+ 'items' => 'posts',
84
+ 'title' => ':title - page :num',
85
+ 'permalink' => '/page/:num/',
86
+ 'trail' => {
87
+ 'before' => 2,
88
+ 'after' => 2
89
+ }
90
+ },
91
+ 'v1' => {
92
+ 'enabled' => true,
93
+ 'keywords' => {
94
+ 'items' => 'posts'
95
+ },
96
+ 'items' => 'posts',
97
+ 'templates' => {
98
+ 'location' => 'pages',
99
+ 'generate' => []
100
+ }
101
+ }
102
+ }.freeze
103
+ end
104
+
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,127 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jekyll
4
+ module Plugins
5
+ module PaginateV3
6
+ module Config
7
+
8
+ # Normalises site and template pagination config into predictable internal structures consumed by pagination runtime classes.
9
+ #
10
+ # Site-level config is normalised to the nested public v3 structure:
11
+ # - pagination.syntax.*
12
+ # - pagination.templates.location
13
+ # - pagination.templates.generate
14
+ # - pagination.*
15
+ #
16
+ # Template-level config is normalised to a flat hash used during
17
+ # pagination emission for one concrete template page/document.
18
+ class Normaliser
19
+
20
+ LEGACY_FILTER_KEYS = %w[category tag locale].freeze
21
+ LEGACY_TEMPLATE_DEFAULT_KEYS = %w[items collection filters sort per_page limit offset trail title permalink layout layouts group slugify sort_field sort_reverse indexpage extension].freeze
22
+ LEGACY_SITE_KEY_ALIASES = %w[split separator nested_key_separator].freeze
23
+ SITE_RESERVED_KEYS = %w[enabled templates compatibility keywords equivalents syntax debug].freeze
24
+ V2_AUTOPAGE_DEFAULTS = {
25
+ 'tags' => {
26
+ 'layout' => 'autopage_tags.html',
27
+ 'title' => 'Posts tagged with :tag',
28
+ 'permalink' => '/tag/:tag',
29
+ 'slugify' => {
30
+ 'mode' => 'default',
31
+ 'lowercase' => true
32
+ }
33
+ },
34
+ 'categories' => {
35
+ 'layout' => 'autopage_category.html',
36
+ 'title' => 'Posts in category :cat',
37
+ 'permalink' => '/category/:cat',
38
+ 'slugify' => {
39
+ 'mode' => 'default',
40
+ 'lowercase' => true
41
+ }
42
+ },
43
+ 'collections' => {
44
+ 'layout' => 'autopage_collection.html',
45
+ 'title' => 'Posts in collection :coll',
46
+ 'permalink' => '/collection/:coll/',
47
+ 'slugify' => {
48
+ 'mode' => 'default',
49
+ 'lowercase' => true
50
+ }
51
+ }
52
+ }.freeze
53
+
54
+ # Produces canonical site-level pagination config.
55
+ #
56
+ # Merge order:
57
+ # defaults -> compatibility profile -> legacy overlays -> user config.
58
+ def self.normalise_site_config(site_config)
59
+ site_hash = Utils.safe_hash(site_config)
60
+ raw_pagination = normalise_site_pagination_source(site_hash['pagination'])
61
+
62
+ compatibility_mode = normalise_compatibility(raw_pagination['compatibility'])
63
+
64
+ config = Utils.deep_copy(DEFAULTS)
65
+ if compatibility_mode && COMPATIBILITY_PROFILES.key?(compatibility_mode)
66
+ config = Jekyll::Utils.deep_merge_hashes(config, Utils.deep_copy(COMPATIBILITY_PROFILES[compatibility_mode]))
67
+ end
68
+
69
+ if compatibility_mode == 'v1'
70
+ config = Jekyll::Utils.deep_merge_hashes(config, legacy_v1_overlay(site_hash))
71
+ end
72
+
73
+ config = Jekyll::Utils.deep_merge_hashes(config, raw_pagination)
74
+ config['compatibility'] = compatibility_mode unless compatibility_mode.nil?
75
+
76
+ normalise_site_common!(config, compatibility_mode, raw_pagination)
77
+ migrate_legacy_shortcuts!(config, compatibility_mode, raw_pagination)
78
+ apply_v2_legacy_page_templates!(config, raw_pagination, compatibility_mode)
79
+ migrate_v2_autopages!(config, site_hash['autopages'], compatibility_mode)
80
+
81
+ config
82
+ end
83
+
84
+ # Produces template-level pagination config used while paginating one
85
+ # template page/document.
86
+ #
87
+ # The output is intentionally flat so downstream pipeline code can read
88
+ # values directly without re-resolving site defaults.
89
+ def self.normalise_template_config(site_config, template_pagination_config)
90
+ raw_template_pagination = Utils.safe_hash(template_pagination_config)
91
+ compatibility_mode = normalise_compatibility(raw_template_pagination['compatibility']) || normalise_compatibility(site_config['compatibility'])
92
+ site_template_defaults = extract_site_template_defaults(site_config)
93
+
94
+ page_config = Jekyll::Utils.deep_merge_hashes(
95
+ Utils.deep_copy(site_template_defaults),
96
+ raw_template_pagination
97
+ )
98
+
99
+ page_config['enabled'] = if raw_template_pagination.key?('enabled')
100
+ !!raw_template_pagination['enabled']
101
+ else
102
+ !!site_config['enabled']
103
+ end
104
+ page_config['compatibility'] = compatibility_mode unless compatibility_mode.nil?
105
+
106
+ syntax = resolve_template_syntax(raw_template_pagination, site_config['syntax'])
107
+ page_config['split'] = syntax['split']
108
+ page_config['separator'] = syntax['separator']
109
+
110
+ page_config = normalise_template_defaults(
111
+ page_config,
112
+ raw_overrides: raw_template_pagination,
113
+ split_delimiter: syntax['split'],
114
+ keywords: site_config['keywords']
115
+ )
116
+
117
+ migrate_legacy_shortcuts!(page_config, compatibility_mode, raw_template_pagination)
118
+ apply_v2_legacy_page_templates!(page_config, raw_template_pagination, compatibility_mode)
119
+
120
+ page_config
121
+ end
122
+ end
123
+
124
+ end
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,155 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jekyll
4
+ module Plugins
5
+ module PaginateV3
6
+ module Config
7
+
8
+ # Legacy compatibility migration helpers for v1/v2 style configuration.
9
+ # Structure: each helper converts a specific legacy shape into the shared
10
+ # v3 config model consumed by the runtime pipeline.
11
+ class Normaliser
12
+ class << self
13
+
14
+ private
15
+ def migrate_legacy_shortcuts!(template_config, compatibility_mode, raw_overrides = nil)
16
+ return unless compatibility_mode == 'v2'
17
+
18
+ override_hash = Utils.safe_hash(raw_overrides)
19
+ template_overrides = extract_template_defaults_overrides(override_hash)
20
+ explicit_filters = Utils.safe_hash(template_overrides['filters'])
21
+
22
+ LEGACY_FILTER_KEYS.each do |legacy_key|
23
+ next unless override_hash.key?(legacy_key)
24
+ next unless present_config_value?(override_hash[legacy_key])
25
+ next if explicit_filters.key?(legacy_key)
26
+ next if legacy_key == 'category' && override_hash[legacy_key].to_s.strip == 'posts'
27
+
28
+ template_config['filters'][legacy_key] = override_hash[legacy_key]
29
+ end
30
+
31
+ LEGACY_FILTER_KEYS.each { |legacy_key| template_config.delete(legacy_key) }
32
+ end
33
+
34
+ # Applies v2 `indexpage`/`extension` legacy behaviour by translating
35
+ # those keys into internal page1/page2 permalink templates.
36
+ def apply_v2_legacy_page_templates!(template_config, raw_overrides, compatibility_mode)
37
+ return unless compatibility_mode == 'v2'
38
+
39
+ override_hash = extract_template_defaults_overrides(raw_overrides)
40
+ return unless override_hash.key?('indexpage') || override_hash.key?('extension')
41
+
42
+ index_name = override_hash.key?('indexpage') ? override_hash['indexpage'].to_s : 'index'
43
+ extension = override_hash.key?('extension') ? override_hash['extension'].to_s : 'html'
44
+
45
+ template_config['page_templates'] ||= build_page_templates(template_config['title'], template_config['permalink'])
46
+ template_config['page_templates']['page1']['permalink'] = Utils.ensure_full_path('/', index_name, extension)
47
+ template_config['page_templates']['page2']['permalink'] = Utils.ensure_full_path(template_config['permalink'], index_name, extension)
48
+ end
49
+
50
+ # Imports legacy top-level `paginate` settings used by
51
+ # jekyll-paginate v1.
52
+ def legacy_v1_overlay(site_hash)
53
+ overlay = {}
54
+ return overlay if site_hash['paginate'].nil?
55
+
56
+ overlay['enabled'] = true
57
+ overlay['keywords'] = { 'items' => 'posts' }
58
+ overlay['per_page'] = site_hash['paginate'].to_i
59
+ overlay['items'] = 'posts'
60
+ unless site_hash['paginate_path'].nil?
61
+ overlay['permalink'] = site_hash['paginate_path'].to_s
62
+ end
63
+
64
+ overlay
65
+ end
66
+
67
+ # Legacy migration path for v2 `autopages` into
68
+ # `pagination.templates.generate`.
69
+ def migrate_v2_autopages!(config, raw_autopages, compatibility_mode)
70
+ return unless compatibility_mode == 'v2'
71
+
72
+ autopages = Utils.safe_hash(raw_autopages)
73
+ return if autopages.empty? || autopages['enabled'] == false
74
+
75
+ migrated = []
76
+
77
+ migrated.concat(migrate_v2_autopage_group(
78
+ raw_group: autopages['tags'],
79
+ index_key: 'tag',
80
+ items: 'all',
81
+ defaults: V2_AUTOPAGE_DEFAULTS['tags'],
82
+ split_delimiter: config.dig('syntax', 'split')
83
+ ))
84
+ migrated.concat(migrate_v2_autopage_group(
85
+ raw_group: autopages['categories'],
86
+ index_key: 'category',
87
+ items: 'all',
88
+ defaults: V2_AUTOPAGE_DEFAULTS['categories'],
89
+ split_delimiter: config.dig('syntax', 'split')
90
+ ))
91
+ migrated.concat(migrate_v2_autopage_group(
92
+ raw_group: autopages['collections'],
93
+ index_key: 'collection',
94
+ items: 'all',
95
+ defaults: V2_AUTOPAGE_DEFAULTS['collections'],
96
+ split_delimiter: config.dig('syntax', 'split')
97
+ ))
98
+
99
+ return if migrated.empty?
100
+
101
+ config['templates']['generate'].concat(migrated)
102
+ end
103
+
104
+ # Maps one v2 autopages group (tags/categories/collections) to one
105
+ # generate definition.
106
+ def migrate_v2_autopage_group(raw_group:, index_key:, items:, defaults:, split_delimiter:)
107
+ group = Utils.safe_hash(raw_group)
108
+ return [] if group.empty? || group['enabled'] == false
109
+
110
+ layouts = Utils.normalise_layouts(group, split_delimiter: split_delimiter)
111
+ layouts = [defaults['layout']] if layouts.empty?
112
+
113
+ title = group['title']
114
+ title = defaults['title'] unless present_config_value?(title)
115
+
116
+ permalink = group['permalink']
117
+ permalink = defaults['permalink'] unless present_config_value?(permalink)
118
+
119
+ slugify = if group.key?('slugify')
120
+ Utils.safe_hash(group['slugify'])
121
+ else
122
+ Utils.deep_copy(defaults['slugify'])
123
+ end
124
+
125
+ [
126
+ {
127
+ 'items' => items,
128
+ 'group' => index_key,
129
+ 'layouts' => layouts,
130
+ 'frontmatter' => {
131
+ 'title' => title,
132
+ 'permalink' => permalink
133
+ },
134
+ 'slugify' => slugify
135
+ }
136
+ ]
137
+ end
138
+
139
+ # Indicates whether a config value should be treated as explicitly set.
140
+ def present_config_value?(value)
141
+ return false if value.nil?
142
+ return false if value.is_a?(String) && value.strip.empty?
143
+ return false if value.is_a?(Array) && value.empty?
144
+ return false if value.is_a?(Hash) && value.empty?
145
+
146
+ true
147
+ end
148
+
149
+ end
150
+ end
151
+
152
+ end
153
+ end
154
+ end
155
+ end