jekyll-documents 0.7.2 → 0.7.4
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 +38 -0
- data/README.md +3 -0
- data/_includes/documents_search.html +3 -1
- data/lib/jekyll/documents/generator.rb +43 -32
- data/lib/jekyll/documents/options_parser.rb +10 -6
- data/lib/jekyll/documents/tag_helpers.rb +53 -0
- data/lib/jekyll/documents/tags/doc_category.rb +14 -48
- data/lib/jekyll/documents/tags/doc_link.rb +18 -52
- data/lib/jekyll/documents/tags/document_icon.rb +1 -9
- data/lib/jekyll/documents/tags/latest_documents.rb +25 -23
- data/lib/jekyll/documents/version.rb +1 -1
- data/lib/jekyll-documents.rb +1 -0
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c690ba4e56790f19326b49d3476839028784f9da66b4d46d5a18680cac0ba927
|
|
4
|
+
data.tar.gz: 6007f855de36ef230b789fdab081877f19c9be15c583a342e669eaa811898317
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ecc1cbd701f3b07f70694dd0a9f7314d7d65bb2b2040ad0af3658d763ad540bb40daf90e1494100e49ab72a10540377786d793a2416536cc055f85a16983fcb7
|
|
7
|
+
data.tar.gz: dbab2960b310640f780b3d88695ec16cbdf1902fba99019736bedf7ab3ecf0a6f956c370b9b3c8858ad559f4f40fe7a6f354843a7ceef790e97010f751c898cc
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,43 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [Unreleased]
|
|
4
|
+
|
|
5
|
+
## [0.7.4] - 2026-09-18
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- Added Codecov coverage reporting: CI uploads Cobertura XML (via `simplecov-cobertura`) after the Ruby quality suite, with `codecov.yml` status checks (98% project / 90% patch targets)
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
- Auto-update `Gemfile.lock` on `rake version:bump` via `bundle lock` (use `SKIP_LOCK=1` to skip)
|
|
12
|
+
- Further reduced `latest_documents.rb` `render` complexity by extracting `resolve_count` and `select_documents`
|
|
13
|
+
- Extracted shared `TagHelpers` module (markup parsing, document collection access, path normalization, HTML escaping) used by `doc_link`, `doc_category`, `document_icon`, and `latest_documents` tags — removes SonarCloud-flagged duplication
|
|
14
|
+
- Extracted shared `liquid tag helpers` spec context (`spec/support/shared_context_tags.rb`) for tag spec setup
|
|
15
|
+
- Added `.sonarcloud.properties` excluding `spec/fixtures/` from SonarCloud duplication analysis (fixture templates intentionally mirror shipped includes/layouts)
|
|
16
|
+
|
|
17
|
+
## [0.7.3] - 2026-09-10
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
- Added Semgrep CE security scanning with a custom ReDoS detection rule for pre-commit and CI
|
|
21
|
+
- Added CodeFactor code quality analysis badge and integration
|
|
22
|
+
- Added SEMGREP_APP_TOKEN integration for Cloud dashboard sync from CI
|
|
23
|
+
- Added `rake version:pre_release` task for pre-release security gate with FORCE=1 override
|
|
24
|
+
|
|
25
|
+
### Changed
|
|
26
|
+
- Updated CI to Node.js 26 and current Node 24-based GitHub Actions runtimes
|
|
27
|
+
- Pinned all GitHub Actions to commit SHAs to prevent supply-chain attacks
|
|
28
|
+
- Added cooldown period to Dependabot configuration
|
|
29
|
+
- Pinned Lunr.js CDN to version 2.3.9 with SRI integrity hash
|
|
30
|
+
- Bumped actions/checkout to v7.0.1 (SHA-pinned)
|
|
31
|
+
- Reduced method complexity across 6 flagged CodeFactor findings:
|
|
32
|
+
- `generator.rb`: Extracted `process_file` and `merge_passthrough_fields` from `generate` and `configure_client_search`
|
|
33
|
+
- `doc_link.rb`: Extracted `match_documents` and `report_ambiguous` from `find_document`
|
|
34
|
+
- `doc_category.rb`: Extracted `match_categories` and `report_ambiguous_category` from `resolve_category`
|
|
35
|
+
- `latest_documents.rb`: Extracted `render_list` and `list_item` from `render`
|
|
36
|
+
- `options_parser.rb`: Extracted `read_value` from `parse_options`
|
|
37
|
+
|
|
38
|
+
### Fixed
|
|
39
|
+
- Replaced dangerous subshell in system spec with Open3.capture2
|
|
40
|
+
|
|
3
41
|
## [0.7.2] - 2026-09-09
|
|
4
42
|
|
|
5
43
|
### Fixed
|
data/README.md
CHANGED
|
@@ -6,6 +6,9 @@
|
|
|
6
6
|
[](https://rubygems.org/gems/jekyll-documents)
|
|
7
7
|
[](LICENSE)
|
|
8
8
|
[](https://deepwiki.com/gundestrup/jekyll-documents)
|
|
9
|
+
[](https://www.codefactor.io/repository/github/gundestrup/jekyll-documents)
|
|
10
|
+
[](https://github.com/gundestrup/jekyll-documents/security/code-scanning)
|
|
11
|
+
[](https://sonarcloud.io/summary/new_code?id=gundestrup_jekyll-documents)
|
|
9
12
|
|
|
10
13
|
Turn files in `assets/documents/` into browsable document pages.
|
|
11
14
|
|
|
@@ -9,6 +9,8 @@
|
|
|
9
9
|
window.__jekyllDocumentsIndexPath = {{ index_path | relative_url | jsonify }};
|
|
10
10
|
window.__jekyllDocumentsBaseurl = {{ site.baseurl | jsonify }};
|
|
11
11
|
</script>
|
|
12
|
-
<script src="https://unpkg.com/lunr/lunr.js"
|
|
12
|
+
<script src="https://unpkg.com/lunr@2.3.9/lunr.js"
|
|
13
|
+
integrity="sha384-Mrc0XsDxndfJgV5jLnEeThch4j9dNEXAE1vbWeyzmERxSvF8siQfuEczzgUelQQH"
|
|
14
|
+
crossorigin="anonymous"></script>
|
|
13
15
|
<script src="{{ '/assets/js/documents-search.js' | relative_url }}"></script>
|
|
14
16
|
{% endif %}
|
|
@@ -43,39 +43,10 @@ module Jekyll
|
|
|
43
43
|
Dir.glob("#{root}/**/*").each do |path|
|
|
44
44
|
next unless File.file?(path)
|
|
45
45
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
if @config["strict_extensions"] && !@config["include_extensions"].include?(ext)
|
|
49
|
-
::Jekyll.logger.abort_with "jekyll-documents",
|
|
50
|
-
"Unsupported file type: #{path} (#{ext})"
|
|
51
|
-
end
|
|
52
|
-
next unless @config["include_extensions"].include?(ext)
|
|
46
|
+
doc, rel_path = process_file(path, root, collection)
|
|
47
|
+
next unless doc
|
|
53
48
|
|
|
54
|
-
source_path = source_path_for(path, root)
|
|
55
|
-
category_path = document_category_path(source_path)
|
|
56
|
-
category = remap_category(category_path)
|
|
57
|
-
rel_path = normalize_path(File.join(@config["root"], source_path))
|
|
58
49
|
current_paths << rel_path
|
|
59
|
-
basename = File.basename(path, source_extension)
|
|
60
|
-
|
|
61
|
-
date, title, valid = parse_filename(basename)
|
|
62
|
-
if !valid && @config["strict_filename"]
|
|
63
|
-
::Jekyll.logger.abort_with "jekyll-documents",
|
|
64
|
-
"Filename must be 'YYYY-MM-DD_Title.ext' → #{rel_path}"
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
doc = ::Jekyll::Document.new(
|
|
68
|
-
source_stub_for(source_path),
|
|
69
|
-
site: site,
|
|
70
|
-
collection: collection
|
|
71
|
-
)
|
|
72
|
-
file_info = {
|
|
73
|
-
title: title, date: date, category: category, category_path: category_path,
|
|
74
|
-
category_slug: slugify(category, "uncategorized"), source_path: source_path,
|
|
75
|
-
rel_path: rel_path, ext: ext, file_type: ext.delete_prefix("."),
|
|
76
|
-
icon_set: @config["icon_set"], slug: build_slug(basename), path: path
|
|
77
|
-
}
|
|
78
|
-
bake_document_data(doc, file_info)
|
|
79
50
|
collection.docs << doc
|
|
80
51
|
generated_docs << doc
|
|
81
52
|
end
|
|
@@ -87,6 +58,42 @@ module Jekyll
|
|
|
87
58
|
|
|
88
59
|
private
|
|
89
60
|
|
|
61
|
+
def process_file(path, root, collection)
|
|
62
|
+
source_extension = File.extname(path)
|
|
63
|
+
ext = source_extension.downcase
|
|
64
|
+
if @config["strict_extensions"] && !@config["include_extensions"].include?(ext)
|
|
65
|
+
::Jekyll.logger.abort_with "jekyll-documents",
|
|
66
|
+
"Unsupported file type: #{path} (#{ext})"
|
|
67
|
+
end
|
|
68
|
+
return [nil, nil] unless @config["include_extensions"].include?(ext)
|
|
69
|
+
|
|
70
|
+
source_path = source_path_for(path, root)
|
|
71
|
+
rel_path = normalize_path(File.join(@config["root"], source_path))
|
|
72
|
+
basename = File.basename(path, source_extension)
|
|
73
|
+
|
|
74
|
+
date, title, valid = parse_filename(basename)
|
|
75
|
+
if !valid && @config["strict_filename"]
|
|
76
|
+
::Jekyll.logger.abort_with "jekyll-documents",
|
|
77
|
+
"Filename must be 'YYYY-MM-DD_Title.ext' → #{rel_path}"
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
category_path = document_category_path(source_path)
|
|
81
|
+
category = remap_category(category_path)
|
|
82
|
+
doc = ::Jekyll::Document.new(
|
|
83
|
+
source_stub_for(source_path),
|
|
84
|
+
site: @site,
|
|
85
|
+
collection: collection
|
|
86
|
+
)
|
|
87
|
+
file_info = {
|
|
88
|
+
title: title, date: date, category: category, category_path: category_path,
|
|
89
|
+
category_slug: slugify(category, "uncategorized"), source_path: source_path,
|
|
90
|
+
rel_path: rel_path, ext: ext, file_type: ext.delete_prefix("."),
|
|
91
|
+
icon_set: @config["icon_set"], slug: build_slug(basename), path: path
|
|
92
|
+
}
|
|
93
|
+
bake_document_data(doc, file_info)
|
|
94
|
+
[doc, rel_path]
|
|
95
|
+
end
|
|
96
|
+
|
|
90
97
|
# Auto-injects passthrough_fields into client_search config when the
|
|
91
98
|
# documents collection is indexed. Only acts if client_search is already
|
|
92
99
|
# configured with +documents+ in its collections list — does nothing if
|
|
@@ -96,13 +103,17 @@ module Jekyll
|
|
|
96
103
|
return unless search_config.is_a?(Hash)
|
|
97
104
|
return unless Array(search_config["collections"]).include?("documents")
|
|
98
105
|
|
|
106
|
+
merge_passthrough_fields(search_config)
|
|
107
|
+
search_config["icon_field"] = "icon_url" unless search_config.key?("icon_field")
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def merge_passthrough_fields(search_config)
|
|
99
111
|
fields = search_config["passthrough_fields"] || []
|
|
100
112
|
existing = fields.flat_map { |f| f.is_a?(Hash) ? f.keys : [f.to_s] }
|
|
101
113
|
%w[file_type icon_url icon_set].each do |field|
|
|
102
114
|
fields << field unless existing.include?(field)
|
|
103
115
|
end
|
|
104
116
|
search_config["passthrough_fields"] = fields
|
|
105
|
-
search_config["icon_field"] = "icon_url" unless search_config.key?("icon_field")
|
|
106
117
|
end
|
|
107
118
|
|
|
108
119
|
# Ensures a collection exists and is configured for output
|
|
@@ -27,17 +27,21 @@ module Jekyll
|
|
|
27
27
|
scanner.skip(/\s*/)
|
|
28
28
|
next unless scanner.scan(":")
|
|
29
29
|
|
|
30
|
-
scanner
|
|
31
|
-
value = if scanner.scan(/'([^']*)'/) || scanner.scan(/"([^"]*)"/)
|
|
32
|
-
scanner[1]
|
|
33
|
-
else
|
|
34
|
-
scanner.scan(/\S+/)
|
|
35
|
-
end
|
|
30
|
+
value = read_value(scanner)
|
|
36
31
|
options[key] = value unless value.nil?
|
|
37
32
|
end
|
|
38
33
|
|
|
39
34
|
options
|
|
40
35
|
end
|
|
36
|
+
|
|
37
|
+
def read_value(scanner)
|
|
38
|
+
scanner.skip(/\s*/)
|
|
39
|
+
if scanner.scan(/'([^']*)'/) || scanner.scan(/"([^"]*)"/)
|
|
40
|
+
scanner[1]
|
|
41
|
+
else
|
|
42
|
+
scanner.scan(/\S+/)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
41
45
|
end
|
|
42
46
|
end
|
|
43
47
|
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Jekyll
|
|
4
|
+
module Documents
|
|
5
|
+
# Shared markup parsing, document collection access, path
|
|
6
|
+
# normalization, and HTML escaping for the Liquid tags.
|
|
7
|
+
module TagHelpers
|
|
8
|
+
private
|
|
9
|
+
|
|
10
|
+
def parse_markup(markup)
|
|
11
|
+
text = markup.to_s
|
|
12
|
+
target, remainder = extract_target(text)
|
|
13
|
+
options = extract_options(remainder)
|
|
14
|
+
[target, options]
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def extract_target(text)
|
|
18
|
+
return [nil, text] if text.strip.match?(/\Apath\s*:/)
|
|
19
|
+
|
|
20
|
+
quoted = text.match(/\A["']([^"']+)["']/)
|
|
21
|
+
return [quoted[1], text[quoted.end(0)..]] if quoted
|
|
22
|
+
|
|
23
|
+
bare = text.strip.match(/\A([^\s]+)/)
|
|
24
|
+
return [nil, ""] unless bare
|
|
25
|
+
|
|
26
|
+
[bare[1], text[bare.end(0)..]]
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def extract_options(text)
|
|
30
|
+
OptionsParser.parse_options(text)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def documents_from(context)
|
|
34
|
+
site = context.registers[:site]
|
|
35
|
+
site.collections["documents"]&.docs || []
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def normalize_tag_path(path)
|
|
39
|
+
path.to_s.strip.tr("\\", "/").squeeze("/").delete_prefix("./").delete_prefix("/")
|
|
40
|
+
.delete_suffix("/")
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def escape_html(value)
|
|
44
|
+
value.to_s.gsub(/[&<>"']/,
|
|
45
|
+
"&" => "&",
|
|
46
|
+
"<" => "<",
|
|
47
|
+
">" => ">",
|
|
48
|
+
'"' => """,
|
|
49
|
+
"'" => "'")
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -4,6 +4,7 @@ module Jekyll
|
|
|
4
4
|
module Documents
|
|
5
5
|
module CategoryResolver
|
|
6
6
|
include ResolutionReporter
|
|
7
|
+
include TagHelpers
|
|
7
8
|
|
|
8
9
|
private
|
|
9
10
|
|
|
@@ -13,22 +14,28 @@ module Jekyll
|
|
|
13
14
|
normalized = query.to_s.strip.downcase
|
|
14
15
|
return nil if normalized.empty?
|
|
15
16
|
|
|
16
|
-
|
|
17
|
-
category_values(category).include?(normalized)
|
|
18
|
-
end
|
|
19
|
-
matches = exact.empty? ? partial_categories(categories, normalized) : exact
|
|
17
|
+
matches = match_categories(categories, normalized)
|
|
20
18
|
aggregate = options["aggregate"] == "true" && options["list"] == "true"
|
|
21
19
|
return matches if matches.one? || (aggregate && matches.any?)
|
|
22
20
|
return nil if matches.empty?
|
|
23
21
|
|
|
22
|
+
report_ambiguous_category(site, query, matches)
|
|
23
|
+
nil
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def match_categories(categories, normalized)
|
|
27
|
+
exact = categories.select { |cat| category_values(cat).include?(normalized) }
|
|
28
|
+
exact.empty? ? partial_categories(categories, normalized) : exact
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def report_ambiguous_category(site, query, matches)
|
|
24
32
|
paths = matches.map { |category| category["path"] }.sort.join(", ")
|
|
25
33
|
message = "Ambiguous doc_category #{query.inspect}; matches: #{paths}. Rendering nothing."
|
|
26
34
|
report_resolution_issue(site, message)
|
|
27
|
-
nil
|
|
28
35
|
end
|
|
29
36
|
|
|
30
37
|
def resolve_category_path(path, categories, site)
|
|
31
|
-
normalized =
|
|
38
|
+
normalized = normalize_tag_path(path)
|
|
32
39
|
matches = categories.select { |category| category["path"] == normalized }
|
|
33
40
|
return matches if matches.one?
|
|
34
41
|
|
|
@@ -48,17 +55,13 @@ module Jekyll
|
|
|
48
55
|
leaf = category["path"].split("/").last.to_s.downcase
|
|
49
56
|
[category["category"], category["slug"], leaf].map { |value| value.to_s.downcase }.uniq
|
|
50
57
|
end
|
|
51
|
-
|
|
52
|
-
def normalize_category_path(path)
|
|
53
|
-
path.to_s.strip.tr("\\", "/").squeeze("/").delete_prefix("./").delete_prefix("/")
|
|
54
|
-
.delete_suffix("/")
|
|
55
|
-
end
|
|
56
58
|
end
|
|
57
59
|
|
|
58
60
|
class DocCategoryTag < Liquid::Tag
|
|
59
61
|
public_class_method :new
|
|
60
62
|
|
|
61
63
|
include CategoryResolver
|
|
64
|
+
include TagHelpers
|
|
62
65
|
include Jekyll::Filters::URLFilters
|
|
63
66
|
|
|
64
67
|
def initialize(tag_name, markup, tokens)
|
|
@@ -82,11 +85,6 @@ module Jekyll
|
|
|
82
85
|
|
|
83
86
|
private
|
|
84
87
|
|
|
85
|
-
def documents_from(context)
|
|
86
|
-
site = context.registers[:site]
|
|
87
|
-
site.collections["documents"]&.docs || []
|
|
88
|
-
end
|
|
89
|
-
|
|
90
88
|
def available_categories(docs)
|
|
91
89
|
categories = docs.group_by { |doc| doc.data["category_path"] || doc.data["category"] }
|
|
92
90
|
.map do |path, matches|
|
|
@@ -131,38 +129,6 @@ module Jekyll
|
|
|
131
129
|
out << %(<li><a href="#{url}">#{title}</a> <small>(#{date})</small></li>\n)
|
|
132
130
|
end
|
|
133
131
|
end
|
|
134
|
-
|
|
135
|
-
def parse_markup(markup)
|
|
136
|
-
text = markup.to_s
|
|
137
|
-
category, remainder = extract_category(text)
|
|
138
|
-
options = extract_options(remainder)
|
|
139
|
-
[category, options]
|
|
140
|
-
end
|
|
141
|
-
|
|
142
|
-
def extract_category(text)
|
|
143
|
-
return [nil, text] if text.strip.match?(/\Apath\s*:/)
|
|
144
|
-
|
|
145
|
-
quoted = text.match(/\A["']([^"']+)["']/)
|
|
146
|
-
return [quoted[1], text[quoted.end(0)..]] if quoted
|
|
147
|
-
|
|
148
|
-
bare = text.strip.match(/\A([^\s]+)/)
|
|
149
|
-
return [nil, ""] unless bare
|
|
150
|
-
|
|
151
|
-
[bare[1], text[bare.end(0)..]]
|
|
152
|
-
end
|
|
153
|
-
|
|
154
|
-
def extract_options(text)
|
|
155
|
-
OptionsParser.parse_options(text)
|
|
156
|
-
end
|
|
157
|
-
|
|
158
|
-
def escape_html(value)
|
|
159
|
-
value.to_s.gsub(/[&<>"']/,
|
|
160
|
-
"&" => "&",
|
|
161
|
-
"<" => "<",
|
|
162
|
-
">" => ">",
|
|
163
|
-
'"' => """,
|
|
164
|
-
"'" => "'")
|
|
165
|
-
end
|
|
166
132
|
end
|
|
167
133
|
end
|
|
168
134
|
end
|
|
@@ -4,6 +4,7 @@ module Jekyll
|
|
|
4
4
|
module Documents
|
|
5
5
|
module DocumentResolver
|
|
6
6
|
include ResolutionReporter
|
|
7
|
+
include TagHelpers
|
|
7
8
|
|
|
8
9
|
private
|
|
9
10
|
|
|
@@ -13,25 +14,31 @@ module Jekyll
|
|
|
13
14
|
normalized = query.to_s.strip.downcase
|
|
14
15
|
return nil if normalized.empty?
|
|
15
16
|
|
|
16
|
-
|
|
17
|
-
matches = if exact.empty?
|
|
18
|
-
docs.select do |doc|
|
|
19
|
-
document_values(doc).any? { |value| value.include?(normalized) }
|
|
20
|
-
end
|
|
21
|
-
else
|
|
22
|
-
exact
|
|
23
|
-
end
|
|
17
|
+
matches = match_documents(docs, normalized)
|
|
24
18
|
return matches.first if matches.one?
|
|
25
19
|
return nil if matches.empty?
|
|
26
20
|
|
|
21
|
+
report_ambiguous(site, query, matches)
|
|
22
|
+
nil
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def match_documents(docs, normalized)
|
|
26
|
+
exact = docs.select { |doc| document_values(doc).include?(normalized) }
|
|
27
|
+
return exact unless exact.empty?
|
|
28
|
+
|
|
29
|
+
docs.select do |doc|
|
|
30
|
+
document_values(doc).any? { |value| value.include?(normalized) }
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def report_ambiguous(site, query, matches)
|
|
27
35
|
paths = matches.map { |doc| doc.data["source_path"] || doc.path }.sort.join(", ")
|
|
28
36
|
message = "Ambiguous doc_link #{query.inspect}; matches: #{paths}. Rendering nothing."
|
|
29
37
|
report_resolution_issue(site, message)
|
|
30
|
-
nil
|
|
31
38
|
end
|
|
32
39
|
|
|
33
40
|
def find_document_by_path(docs, path, site)
|
|
34
|
-
normalized =
|
|
41
|
+
normalized = normalize_tag_path(path)
|
|
35
42
|
matches = docs.select { |doc| doc.data["source_path"].to_s == normalized }
|
|
36
43
|
return matches.first if matches.one?
|
|
37
44
|
|
|
@@ -42,11 +49,6 @@ module Jekyll
|
|
|
42
49
|
nil
|
|
43
50
|
end
|
|
44
51
|
|
|
45
|
-
def normalize_identifier_path(path)
|
|
46
|
-
path.to_s.strip.tr("\\", "/").squeeze("/").delete_prefix("./").delete_prefix("/")
|
|
47
|
-
.delete_suffix("/")
|
|
48
|
-
end
|
|
49
|
-
|
|
50
52
|
def document_values(doc)
|
|
51
53
|
[doc.data["title"], doc.data["slug"]].map { |value| value.to_s.downcase }
|
|
52
54
|
end
|
|
@@ -56,6 +58,7 @@ module Jekyll
|
|
|
56
58
|
public_class_method :new
|
|
57
59
|
|
|
58
60
|
include DocumentResolver
|
|
61
|
+
include TagHelpers
|
|
59
62
|
include Jekyll::Filters::URLFilters
|
|
60
63
|
|
|
61
64
|
SIZE_UNITS = %w[B KB MB GB].freeze
|
|
@@ -81,11 +84,6 @@ module Jekyll
|
|
|
81
84
|
|
|
82
85
|
private
|
|
83
86
|
|
|
84
|
-
def documents_from(context)
|
|
85
|
-
site = context.registers[:site]
|
|
86
|
-
site.collections["documents"]&.docs || []
|
|
87
|
-
end
|
|
88
|
-
|
|
89
87
|
def build_link(doc, url, text)
|
|
90
88
|
inner = +""
|
|
91
89
|
inner << icon_html(doc) if @options["icon"] != "false"
|
|
@@ -127,38 +125,6 @@ module Jekyll
|
|
|
127
125
|
value /= 1024
|
|
128
126
|
end
|
|
129
127
|
end
|
|
130
|
-
|
|
131
|
-
def parse_markup(markup)
|
|
132
|
-
text = markup.to_s
|
|
133
|
-
query, remainder = extract_query(text)
|
|
134
|
-
options = extract_options(remainder)
|
|
135
|
-
[query, options]
|
|
136
|
-
end
|
|
137
|
-
|
|
138
|
-
def extract_query(text)
|
|
139
|
-
return [nil, text] if text.strip.match?(/\Apath\s*:/)
|
|
140
|
-
|
|
141
|
-
quoted = text.match(/\A["']([^"']+)["']/)
|
|
142
|
-
return [quoted[1], text[quoted.end(0)..]] if quoted
|
|
143
|
-
|
|
144
|
-
bare = text.strip.match(/\A([^\s]+)/)
|
|
145
|
-
return [nil, ""] unless bare
|
|
146
|
-
|
|
147
|
-
[bare[1], text[bare.end(0)..]]
|
|
148
|
-
end
|
|
149
|
-
|
|
150
|
-
def extract_options(text)
|
|
151
|
-
OptionsParser.parse_options(text)
|
|
152
|
-
end
|
|
153
|
-
|
|
154
|
-
def escape_html(value)
|
|
155
|
-
value.to_s.gsub(/[&<>"']/,
|
|
156
|
-
"&" => "&",
|
|
157
|
-
"<" => "<",
|
|
158
|
-
">" => ">",
|
|
159
|
-
'"' => """,
|
|
160
|
-
"'" => "'")
|
|
161
|
-
end
|
|
162
128
|
end
|
|
163
129
|
end
|
|
164
130
|
end
|
|
@@ -5,6 +5,7 @@ module Jekyll
|
|
|
5
5
|
class DocumentIconTag < Liquid::Tag
|
|
6
6
|
public_class_method :new
|
|
7
7
|
|
|
8
|
+
include TagHelpers
|
|
8
9
|
include Jekyll::Filters::URLFilters
|
|
9
10
|
|
|
10
11
|
def initialize(tag_name, markup, tokens)
|
|
@@ -47,15 +48,6 @@ module Jekyll
|
|
|
47
48
|
expression = markup.to_s.strip[/\A[^\s]+/]
|
|
48
49
|
[expression, OptionsParser.parse_options(markup)]
|
|
49
50
|
end
|
|
50
|
-
|
|
51
|
-
def escape_html(value)
|
|
52
|
-
value.to_s.gsub(/[&<>"']/,
|
|
53
|
-
"&" => "&",
|
|
54
|
-
"<" => "<",
|
|
55
|
-
">" => ">",
|
|
56
|
-
'"' => """,
|
|
57
|
-
"'" => "'")
|
|
58
|
-
end
|
|
59
51
|
end
|
|
60
52
|
end
|
|
61
53
|
end
|
|
@@ -10,45 +10,47 @@ module Jekyll
|
|
|
10
10
|
|
|
11
11
|
public_class_method :new
|
|
12
12
|
|
|
13
|
+
include TagHelpers
|
|
14
|
+
|
|
13
15
|
def render(context)
|
|
14
16
|
site = context.registers[:site]
|
|
15
17
|
cfg = Configuration.read(site)
|
|
16
|
-
count = (
|
|
17
|
-
|
|
18
|
+
count = resolve_count(cfg)
|
|
19
|
+
docs = select_documents(site, @args["category"], count)
|
|
20
|
+
|
|
21
|
+
render_list(docs)
|
|
22
|
+
end
|
|
18
23
|
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
def resolve_count(cfg)
|
|
27
|
+
(@args["count"] || cfg["latest_default_count"] || 5).to_i
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def select_documents(site, category, count)
|
|
19
31
|
docs = site.collections["documents"]&.docs || []
|
|
20
32
|
docs = docs.select { |doc| doc.data["category"] == category } if category
|
|
21
|
-
docs
|
|
33
|
+
docs.sort_by { |doc| doc.data["date"] || Time.at(0) }.reverse.first(count)
|
|
34
|
+
end
|
|
22
35
|
|
|
36
|
+
def render_list(docs)
|
|
23
37
|
out = +"<ul class=\"latest-documents\">\n"
|
|
24
|
-
docs.each
|
|
25
|
-
data = doc.data
|
|
26
|
-
title = escape_html(data["title"])
|
|
27
|
-
url = escape_html(doc.url)
|
|
28
|
-
date = (data["date"] || Time.at(0)).strftime("%Y-%m-%d")
|
|
29
|
-
out << %(<li><a href="#{url}">#{title}</a> <small>(#{date})</small></li>\n)
|
|
30
|
-
end
|
|
38
|
+
docs.each { |doc| out << list_item(doc) }
|
|
31
39
|
out << "</ul>\n"
|
|
32
40
|
out
|
|
33
41
|
end
|
|
34
42
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
"&" => "&",
|
|
42
|
-
"<" => "<",
|
|
43
|
-
">" => ">",
|
|
44
|
-
'"' => """,
|
|
45
|
-
"'" => "'"
|
|
46
|
-
})
|
|
43
|
+
def list_item(doc)
|
|
44
|
+
data = doc.data
|
|
45
|
+
title = escape_html(data["title"])
|
|
46
|
+
url = escape_html(doc.url)
|
|
47
|
+
date = (data["date"] || Time.at(0)).strftime("%Y-%m-%d")
|
|
48
|
+
%(<li><a href="#{url}">#{title}</a> <small>(#{date})</small></li>\n)
|
|
47
49
|
end
|
|
48
50
|
|
|
49
51
|
# supports: count:5 category:'referat'
|
|
50
52
|
def parse_args(markup)
|
|
51
|
-
|
|
53
|
+
extract_options(markup)
|
|
52
54
|
end
|
|
53
55
|
end
|
|
54
56
|
end
|
data/lib/jekyll-documents.rb
CHANGED
|
@@ -12,6 +12,7 @@ require_relative "jekyll/documents/assets_generator"
|
|
|
12
12
|
require_relative "jekyll/documents/json_index_generator"
|
|
13
13
|
require_relative "jekyll/documents/layout_registrar"
|
|
14
14
|
require_relative "jekyll/documents/options_parser"
|
|
15
|
+
require_relative "jekyll/documents/tag_helpers"
|
|
15
16
|
|
|
16
17
|
# Liquid tag(s)
|
|
17
18
|
require_relative "jekyll/documents/tags/latest_documents"
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: jekyll-documents
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.7.
|
|
4
|
+
version: 0.7.4
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Svend Gundestrup
|
|
@@ -155,6 +155,7 @@ files:
|
|
|
155
155
|
- lib/jekyll/documents/json_index_generator.rb
|
|
156
156
|
- lib/jekyll/documents/layout_registrar.rb
|
|
157
157
|
- lib/jekyll/documents/options_parser.rb
|
|
158
|
+
- lib/jekyll/documents/tag_helpers.rb
|
|
158
159
|
- lib/jekyll/documents/tags/doc_category.rb
|
|
159
160
|
- lib/jekyll/documents/tags/doc_link.rb
|
|
160
161
|
- lib/jekyll/documents/tags/document_icon.rb
|