jekyll-awesome-nav 0.0.1
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 +7 -0
- data/.copier-answers.ci.yml +12 -0
- data/.devcontainer/devcontainer.json +35 -0
- data/.devcontainer/post-create.sh +19 -0
- data/.rubocop.yml +43 -0
- data/.ruby-version +1 -0
- data/.vscode/tasks.json +70 -0
- data/AGENTS.md +287 -0
- data/CODE_OF_CONDUCT.md +84 -0
- data/LICENSE.txt +21 -0
- data/README.md +176 -0
- data/Rakefile +11 -0
- data/jekyll-awesome-nav.gemspec +35 -0
- data/lib/jekyll/awesome_nav/config.rb +31 -0
- data/lib/jekyll/awesome_nav/generator.rb +50 -0
- data/lib/jekyll/awesome_nav/nav_file.rb +14 -0
- data/lib/jekyll/awesome_nav/nav_file_loader.rb +140 -0
- data/lib/jekyll/awesome_nav/nav_file_options.rb +69 -0
- data/lib/jekyll/awesome_nav/nav_resolver.rb +370 -0
- data/lib/jekyll/awesome_nav/navigation_result.rb +150 -0
- data/lib/jekyll/awesome_nav/node.rb +64 -0
- data/lib/jekyll/awesome_nav/page_set.rb +31 -0
- data/lib/jekyll/awesome_nav/serializer.rb +26 -0
- data/lib/jekyll/awesome_nav/sort_options.rb +91 -0
- data/lib/jekyll/awesome_nav/tree_builder.rb +75 -0
- data/lib/jekyll/awesome_nav/utils.rb +94 -0
- data/lib/jekyll/awesome_nav/version.rb +19 -0
- data/lib/jekyll/awesome_nav.rb +26 -0
- data/lib/jekyll-awesome-nav.rb +3 -0
- data/site/_config.yml +33 -0
- data/site/_includes/awesome-nav-demo-tree.html +15 -0
- data/site/_includes/awesome-nav-tree.html +19 -0
- data/site/_layouts/awesome_nav_demo.html +128 -0
- data/site/docs/getting-started.md +68 -0
- data/site/docs/guides/.nav.yml +7 -0
- data/site/docs/guides/config.md +37 -0
- data/site/docs/guides/data.md +40 -0
- data/site/docs/guides/index.md +15 -0
- data/site/docs/guides/install.md +53 -0
- data/site/docs/guides/layouts.md +116 -0
- data/site/docs/guides/overrides.md +42 -0
- data/site/docs/index.md +35 -0
- data/site/index.md +66 -0
- metadata +111 -0
data/README.md
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
# Jekyll Awesome Nav
|
|
2
|
+
|
|
3
|
+
`jekyll-awesome-nav` builds a full navigation tree from a folder hierarchy and lets any directory replace its subtree with a local `.nav.yml` file.
|
|
4
|
+
|
|
5
|
+
The plugin is designed around the behavior described in [AGENTS.md](AGENTS.md):
|
|
6
|
+
|
|
7
|
+
- navigation is generated from `site.pages` under one configured root
|
|
8
|
+
- directories become sections and pages become leaves
|
|
9
|
+
- `index.md` sets a section title and URL
|
|
10
|
+
- `.nav.yml` replaces a directory subtree without merging
|
|
11
|
+
- every page under the root gets the same full tree plus local subtree data
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
Add the gem to your Jekyll site's `Gemfile`:
|
|
16
|
+
|
|
17
|
+
```ruby
|
|
18
|
+
gem "jekyll-awesome-nav"
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Then enable it in `_config.yml`:
|
|
22
|
+
|
|
23
|
+
```yaml
|
|
24
|
+
plugins:
|
|
25
|
+
- jekyll-awesome-nav
|
|
26
|
+
|
|
27
|
+
awesome_nav:
|
|
28
|
+
enabled: true
|
|
29
|
+
root: docs
|
|
30
|
+
nav_filename: .nav.yml
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Exposed Page Data
|
|
34
|
+
|
|
35
|
+
Each page under the configured root receives:
|
|
36
|
+
|
|
37
|
+
- `page.awesome_nav`: the full navigation tree rooted at `awesome_nav.root`
|
|
38
|
+
- `page.awesome_nav_local`: the local subtree for the page's directory
|
|
39
|
+
- `page.awesome_nav_dir`: the directory supplying the active nav context
|
|
40
|
+
- `page.breadcrumbs`: breadcrumb items derived from the final tree
|
|
41
|
+
- `page.awesome_nav_previous`: the previous linked nav item, when one exists
|
|
42
|
+
- `page.awesome_nav_next`: the next linked nav item, when one exists
|
|
43
|
+
|
|
44
|
+
The same data is also exposed on `site.config` as `awesome_nav_tree`, `awesome_nav_local_map`, and
|
|
45
|
+
`awesome_nav_files`.
|
|
46
|
+
|
|
47
|
+
Titles resolve in this order:
|
|
48
|
+
|
|
49
|
+
1. `nav_title`
|
|
50
|
+
2. `title`
|
|
51
|
+
3. filename fallback
|
|
52
|
+
|
|
53
|
+
## `.nav.yml` Format
|
|
54
|
+
|
|
55
|
+
Overrides use a top-level `nav:` entry:
|
|
56
|
+
|
|
57
|
+
```yaml
|
|
58
|
+
nav:
|
|
59
|
+
- Guides: index.md
|
|
60
|
+
- Install: install.md
|
|
61
|
+
- Config: config.md
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Override item rules:
|
|
65
|
+
|
|
66
|
+
- paths are resolved through `site.pages`
|
|
67
|
+
- relative paths are resolved from the `.nav.yml` directory first, then from `awesome_nav.root`
|
|
68
|
+
- directory paths insert the generated directory section at that position
|
|
69
|
+
- glob entries expand generated pages or directories using Ruby's stdlib glob matching
|
|
70
|
+
- external URLs are preserved
|
|
71
|
+
- override order is preserved exactly as written
|
|
72
|
+
- manual sections are preserved as grouping sections unless they intentionally wrap the current directory
|
|
73
|
+
|
|
74
|
+
Useful glob examples:
|
|
75
|
+
|
|
76
|
+
```yaml
|
|
77
|
+
nav:
|
|
78
|
+
- "*"
|
|
79
|
+
- "*.md"
|
|
80
|
+
- "*/"
|
|
81
|
+
- "**/*.md"
|
|
82
|
+
- glob: "*"
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Recursive glob entries such as `**/*.md` are inserted as a flat list at that position. They do not preserve
|
|
86
|
+
the matched files' directory nesting.
|
|
87
|
+
|
|
88
|
+
Use manual sections to group items without linking the group itself:
|
|
89
|
+
|
|
90
|
+
```yaml
|
|
91
|
+
nav:
|
|
92
|
+
- Main:
|
|
93
|
+
- getting-started.md
|
|
94
|
+
- guides
|
|
95
|
+
- More Resources:
|
|
96
|
+
- Website: https://example.com
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Use `append_unmatched` to append generated local items that were not matched by the manual nav. Child `.nav.yml`
|
|
100
|
+
files inherit the closest parent setting unless they set their own value:
|
|
101
|
+
|
|
102
|
+
```yaml
|
|
103
|
+
append_unmatched: true
|
|
104
|
+
nav:
|
|
105
|
+
- getting-started.md
|
|
106
|
+
- guides
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Use `sort` to order generated batches from glob entries and `append_unmatched`. Manual entries stay in the order
|
|
110
|
+
you write them:
|
|
111
|
+
|
|
112
|
+
```yaml
|
|
113
|
+
sort:
|
|
114
|
+
direction: asc
|
|
115
|
+
type: natural
|
|
116
|
+
by: filename
|
|
117
|
+
sections: last
|
|
118
|
+
ignore_case: true
|
|
119
|
+
nav:
|
|
120
|
+
- intro.md
|
|
121
|
+
- "*.md"
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
`sort` is a file-level option. Per-glob options such as `- glob: "*"` with nested `sort:` are not currently
|
|
125
|
+
supported.
|
|
126
|
+
|
|
127
|
+
Use `ignore` to exclude generated items from glob entries and `append_unmatched`. Manual entries are still honored
|
|
128
|
+
when you list them explicitly:
|
|
129
|
+
|
|
130
|
+
```yaml
|
|
131
|
+
ignore:
|
|
132
|
+
- "*.hidden.md"
|
|
133
|
+
- drafts/
|
|
134
|
+
nav:
|
|
135
|
+
- visible.md
|
|
136
|
+
- "*.md"
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Use `hide` in a directory's `.nav.yml` to keep that directory out of generated batches, explicit directory
|
|
140
|
+
references, and child nav-file processing:
|
|
141
|
+
|
|
142
|
+
```yaml
|
|
143
|
+
hide: true
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Options-only `.nav.yml` files are valid, so a hidden directory does not need a `nav:` array.
|
|
147
|
+
|
|
148
|
+
## Documentation Site
|
|
149
|
+
|
|
150
|
+
The source for the plugin documentation site lives in [`site/`](site). From the gem root, run:
|
|
151
|
+
|
|
152
|
+
```sh
|
|
153
|
+
bundle exec jekyll serve --source site
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
The docs site renders `page.awesome_nav`, `page.awesome_nav_local`, `page.breadcrumbs`, and previous/next links
|
|
157
|
+
through a small layout in `site/_layouts/docs.html`.
|
|
158
|
+
|
|
159
|
+
## Development
|
|
160
|
+
|
|
161
|
+
Install dependencies and run the test suite:
|
|
162
|
+
|
|
163
|
+
```sh
|
|
164
|
+
bundle install
|
|
165
|
+
bundle exec rake test
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
You can also open an interactive console with:
|
|
169
|
+
|
|
170
|
+
```sh
|
|
171
|
+
bin/console
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## License
|
|
175
|
+
|
|
176
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "lib/jekyll/awesome_nav/version"
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |spec|
|
|
6
|
+
spec.name = "jekyll-awesome-nav"
|
|
7
|
+
spec.version = Jekyll::AwesomeNav::VERSION
|
|
8
|
+
spec.authors = ["Allison Thackston"]
|
|
9
|
+
spec.email = ["allison@allisonthackston.com"]
|
|
10
|
+
|
|
11
|
+
spec.summary = "Folder-based navigation for Jekyll with local subtree overrides."
|
|
12
|
+
spec.description = "Build a full navigation tree from a docs directory and let any folder replace its subtree with a local _nav.yml file."
|
|
13
|
+
spec.homepage = "https://github.com/PrimerPages/jekyll-awesome-nav"
|
|
14
|
+
spec.license = "MIT"
|
|
15
|
+
spec.required_ruby_version = ">= 2.7.0"
|
|
16
|
+
|
|
17
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
|
18
|
+
spec.metadata["source_code_uri"] = spec.homepage
|
|
19
|
+
spec.metadata["bug_tracker_uri"] = "#{spec.homepage}/issues"
|
|
20
|
+
spec.metadata["documentation_uri"] = "https://primerpages.github.io/jekyll-awesome-nav/"
|
|
21
|
+
spec.metadata["rubygems_mfa_required"] = "true"
|
|
22
|
+
|
|
23
|
+
# Specify which files should be added to the gem when it is released.
|
|
24
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
|
25
|
+
spec.files = Dir.chdir(__dir__) do
|
|
26
|
+
`git ls-files -z`.split("\x0").reject do |f|
|
|
27
|
+
(File.expand_path(f) == __FILE__) ||
|
|
28
|
+
f.start_with?(*%w[bin/ test/ spec/ features/ .git .circleci appveyor Gemfile])
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
spec.bindir = "exe"
|
|
32
|
+
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
|
33
|
+
spec.require_paths = ["lib"]
|
|
34
|
+
spec.add_dependency "jekyll", ">= 3.9", "< 5.0"
|
|
35
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Jekyll
|
|
4
|
+
module AwesomeNav
|
|
5
|
+
class Config
|
|
6
|
+
DEFAULTS = {
|
|
7
|
+
"enabled" => true,
|
|
8
|
+
"root" => "docs",
|
|
9
|
+
"nav_filename" => ".nav.yml"
|
|
10
|
+
}.freeze
|
|
11
|
+
|
|
12
|
+
def initialize(raw_config)
|
|
13
|
+
raise Error, "awesome_nav config must be a mapping" unless raw_config.nil? || raw_config.is_a?(Hash)
|
|
14
|
+
|
|
15
|
+
@data = DEFAULTS.merge(raw_config || {})
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def enabled?
|
|
19
|
+
@data["enabled"]
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def root_dir
|
|
23
|
+
Utils.normalize_dir(@data["root"])
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def nav_filename
|
|
27
|
+
@data["nav_filename"].to_s
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Jekyll
|
|
4
|
+
module AwesomeNav
|
|
5
|
+
class Generator < Jekyll::Generator
|
|
6
|
+
safe true
|
|
7
|
+
priority :low
|
|
8
|
+
|
|
9
|
+
def generate(site)
|
|
10
|
+
config = Config.new(site.config["awesome_nav"])
|
|
11
|
+
return unless config.enabled?
|
|
12
|
+
|
|
13
|
+
pages = PageSet.new(site, config)
|
|
14
|
+
return if pages.empty?
|
|
15
|
+
|
|
16
|
+
tree = TreeBuilder.new(pages: pages, root_dir: config.root_dir).build
|
|
17
|
+
nav_map = NavFileLoader.new(site: site, config: config).load
|
|
18
|
+
resolved_tree = NavResolver.new(root_dir: config.root_dir, nav_map: nav_map).apply(tree, config.root_dir)
|
|
19
|
+
result = NavigationResult.new(
|
|
20
|
+
tree: resolved_tree,
|
|
21
|
+
root_dir: config.root_dir,
|
|
22
|
+
root_page: pages.root_page,
|
|
23
|
+
nav_map: nav_map
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
pages.each do |page|
|
|
27
|
+
page_dir = Utils.source_dir_for(page)
|
|
28
|
+
nav_dir = result.nav_dir_for(page_dir)
|
|
29
|
+
page_url = Utils.normalize_url(page.url)
|
|
30
|
+
page.data["awesome_nav"] = deep_copy(result.serialized_tree)
|
|
31
|
+
page.data["awesome_nav_local"] = deep_copy(result.local_nav_for(nav_dir))
|
|
32
|
+
page.data["awesome_nav_dir"] = nav_dir
|
|
33
|
+
page.data["breadcrumbs"] = result.breadcrumbs_for(page)
|
|
34
|
+
page.data["awesome_nav_previous"] = deep_copy(result.nav_entry_for(page_url)&.fetch("previous", nil))
|
|
35
|
+
page.data["awesome_nav_next"] = deep_copy(result.nav_entry_for(page_url)&.fetch("next", nil))
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
site.config["awesome_nav_tree"] = deep_copy(result.serialized_tree)
|
|
39
|
+
site.config["awesome_nav_local_map"] = deep_copy(result.serialized_local_nav_map)
|
|
40
|
+
site.config["awesome_nav_files"] = deep_copy(result.serialized_nav_files)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
private
|
|
44
|
+
|
|
45
|
+
def deep_copy(value)
|
|
46
|
+
Marshal.load(Marshal.dump(value))
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "yaml"
|
|
4
|
+
|
|
5
|
+
module Jekyll
|
|
6
|
+
module AwesomeNav
|
|
7
|
+
class NavFileLoader
|
|
8
|
+
OPTION_KEYS = %w[append_unmatched hide ignore sort].freeze
|
|
9
|
+
|
|
10
|
+
def initialize(site:, config:)
|
|
11
|
+
@site = site
|
|
12
|
+
@config = config
|
|
13
|
+
@page_urls_by_path = build_page_url_index
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def load
|
|
17
|
+
pattern = File.join(@site.source, @config.root_dir, "**", @config.nav_filename)
|
|
18
|
+
|
|
19
|
+
Dir.glob(pattern).each_with_object({}) do |file, memo|
|
|
20
|
+
dir = Utils.normalize_dir(Utils.relative_dir(@site.source, File.dirname(file)))
|
|
21
|
+
items = load_file(file, dir)
|
|
22
|
+
memo[dir] = items if items
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
def load_file(file, dir)
|
|
29
|
+
data = YAML.safe_load_file(file, permitted_classes: [], aliases: false)
|
|
30
|
+
raise Error, "expected a mapping" unless data.is_a?(Hash)
|
|
31
|
+
|
|
32
|
+
nav = data["nav"]
|
|
33
|
+
raise Error, "expected nav to be an array" if data.key?("nav") && !nav.is_a?(Array)
|
|
34
|
+
raise Error, "expected nav to be an array or supported options" unless data.key?("nav") || options_only?(data)
|
|
35
|
+
|
|
36
|
+
items = Array(nav).map.with_index do |item, index|
|
|
37
|
+
normalize_item(item, file, dir, (index + 1).to_s)
|
|
38
|
+
end
|
|
39
|
+
NavFile.new(items: items, options: NavFileOptions.from(data))
|
|
40
|
+
rescue Psych::Exception, Error => e
|
|
41
|
+
Jekyll.logger.warn("AwesomeNav:", "Could not load #{file}: #{e.message}")
|
|
42
|
+
nil
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def options_only?(data)
|
|
46
|
+
data.keys.any? { |key| OPTION_KEYS.include?(key.to_s) }
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def normalize_item(item, file, dir, index_label)
|
|
50
|
+
case item
|
|
51
|
+
when Hash
|
|
52
|
+
normalize_mapping(item, file, dir, index_label)
|
|
53
|
+
when String
|
|
54
|
+
normalize_string(item, dir)
|
|
55
|
+
else
|
|
56
|
+
raise Error, "item #{index_label} in #{file} must be a mapping or path string"
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def normalize_mapping(item, file, dir, index_label)
|
|
61
|
+
raise Error, "item #{index_label} in #{file} must have exactly one entry" unless item.length == 1
|
|
62
|
+
|
|
63
|
+
title, value = item.first
|
|
64
|
+
return normalize_glob(value, dir, index_label, file) if title.to_s == "glob"
|
|
65
|
+
|
|
66
|
+
title = title.to_s.strip
|
|
67
|
+
raise Error, "item #{index_label} in #{file} is missing a title" if title.empty?
|
|
68
|
+
|
|
69
|
+
case value
|
|
70
|
+
when Array
|
|
71
|
+
children = value.map.with_index do |child, child_index|
|
|
72
|
+
normalize_item(child, file, dir, "#{index_label}.#{child_index + 1}")
|
|
73
|
+
end
|
|
74
|
+
Node.section(
|
|
75
|
+
dir: nil,
|
|
76
|
+
title: title,
|
|
77
|
+
url: nil,
|
|
78
|
+
children: children,
|
|
79
|
+
path: nil,
|
|
80
|
+
filename: nil
|
|
81
|
+
)
|
|
82
|
+
when String
|
|
83
|
+
normalize_string(value, dir, title: title)
|
|
84
|
+
else
|
|
85
|
+
raise Error, "value for item #{index_label} in #{file} must be a path or array"
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def normalize_string(value, dir, title: nil)
|
|
90
|
+
value = value.to_s.strip
|
|
91
|
+
raise Error, "navigation path cannot be empty" if value.empty?
|
|
92
|
+
|
|
93
|
+
return Node.page(dir: nil, title: title || value, url: value, path: value, filename: File.basename(value)) if Utils.external_url?(value)
|
|
94
|
+
|
|
95
|
+
Node.reference(dir: dir, title: title, target: value)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def normalize_glob(value, dir, index_label, file)
|
|
99
|
+
raise Error, "glob item #{index_label} in #{file} must be a path string" unless value.is_a?(String)
|
|
100
|
+
|
|
101
|
+
normalize_string(value, dir)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def build_page_url_index
|
|
105
|
+
@site.pages.each_with_object({}) do |page, index|
|
|
106
|
+
[page.path, page.relative_path, page.instance_variable_get(:@relative_path)].compact.each do |path|
|
|
107
|
+
normalized = Utils.normalize_dir(path)
|
|
108
|
+
index[normalized] = Utils.normalize_url(page.url)
|
|
109
|
+
index[without_index(normalized)] = Utils.normalize_url(page.url) if Utils.index_page?(page)
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def section_url_for(dir)
|
|
115
|
+
normalized = Utils.normalize_dir(dir)
|
|
116
|
+
@page_urls_by_path[normalized] || @page_urls_by_path[File.join(normalized, "index.md")]
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def source_path_for_section(dir)
|
|
120
|
+
normalized = Utils.normalize_dir(dir)
|
|
121
|
+
[File.join(normalized, "index.md"), normalized].find { |candidate| @page_urls_by_path.key?(candidate) } || normalized
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def without_index(path)
|
|
125
|
+
path.sub(%r{(^|/)index(\.[^./]+)?\z}, "")
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def dir_for(url)
|
|
129
|
+
return nil unless url
|
|
130
|
+
return nil if Utils.external_url?(url)
|
|
131
|
+
|
|
132
|
+
path = Utils.normalize_url(url).sub(%r{\A/}, "").sub(%r{/\z}, "")
|
|
133
|
+
return "" if path.empty?
|
|
134
|
+
|
|
135
|
+
segments = path.split("/")
|
|
136
|
+
File.extname(segments.last).empty? ? segments.join("/") : segments[0...-1].join("/")
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Jekyll
|
|
4
|
+
module AwesomeNav
|
|
5
|
+
class NavFileOptions
|
|
6
|
+
UNSET = Object.new.freeze
|
|
7
|
+
|
|
8
|
+
attr_reader :ignore_patterns, :sort_options
|
|
9
|
+
|
|
10
|
+
def self.from(data)
|
|
11
|
+
new(
|
|
12
|
+
append_unmatched: data.key?("append_unmatched") ? data["append_unmatched"] : UNSET,
|
|
13
|
+
hide: data.key?("hide") ? data["hide"] : UNSET,
|
|
14
|
+
ignore: data.key?("ignore") ? data["ignore"] : UNSET,
|
|
15
|
+
sort: data.key?("sort") ? data["sort"] : UNSET
|
|
16
|
+
)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def initialize(append_unmatched: UNSET, hide: UNSET, ignore: UNSET, sort: UNSET)
|
|
20
|
+
@append_unmatched = append_unmatched
|
|
21
|
+
@hide = hide
|
|
22
|
+
@ignore_patterns = ignore == UNSET ? UNSET : normalize_ignore_patterns(ignore)
|
|
23
|
+
@sort_options = sort == UNSET ? UNSET : SortOptions.from(sort)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def append_unmatched_or(inherited)
|
|
27
|
+
return inherited if @append_unmatched == UNSET
|
|
28
|
+
|
|
29
|
+
!!@append_unmatched
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def ignore_patterns_or(inherited)
|
|
33
|
+
return inherited if @ignore_patterns == UNSET
|
|
34
|
+
|
|
35
|
+
@ignore_patterns
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def sort_options_or(inherited)
|
|
39
|
+
return inherited if @sort_options == UNSET
|
|
40
|
+
|
|
41
|
+
@sort_options
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def hide?
|
|
45
|
+
@hide != UNSET && !!@hide
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
private
|
|
49
|
+
|
|
50
|
+
def normalize_ignore_patterns(value)
|
|
51
|
+
patterns =
|
|
52
|
+
case value
|
|
53
|
+
when String
|
|
54
|
+
[value]
|
|
55
|
+
when Array
|
|
56
|
+
value
|
|
57
|
+
else
|
|
58
|
+
raise Error, "ignore must be a path string or array of path strings"
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
patterns.map do |pattern|
|
|
62
|
+
raise Error, "ignore patterns must be path strings" unless pattern.is_a?(String)
|
|
63
|
+
|
|
64
|
+
pattern
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|