jekyll-namespaces 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/jekyll-namespaces.rb +238 -0
- data/lib/jekyll-namespaces/context.rb +13 -0
- data/lib/jekyll-namespaces/node.rb +22 -0
- data/lib/jekyll-namespaces/version.rb +5 -0
- metadata +50 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 6b18126c3527c30dd23fe45e39414c918bcc046bcc85a9955e43476457298be2
|
4
|
+
data.tar.gz: 0a61c1b500793f8eded07a8a55575b25f7e6e9983dbac9df4a77c0591c0c220c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 5ff7599ba50975db33b0d72f14e41606e5b9c0d21126732271dfbc152a08d4c9c9a9dcc422a22bf671bf864ebc637ce14bc58f25c1cd792174ea22b6f64894e5
|
7
|
+
data.tar.gz: 9f3c5053343f86090eac806c00bcf806034d34fb31ddbb64f17efda9060bd17bf78378482f84f09902bc557b3581bbcc2c2eb2b24ade705bd516136d08e15656
|
@@ -0,0 +1,238 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "jekyll"
|
3
|
+
|
4
|
+
require_relative "jekyll-namespaces/context"
|
5
|
+
require_relative "jekyll-namespaces/node"
|
6
|
+
require_relative "jekyll-namespaces/version"
|
7
|
+
|
8
|
+
module JekyllNamespaces
|
9
|
+
class Generator < Jekyll::Generator
|
10
|
+
attr_accessor :site, :config
|
11
|
+
|
12
|
+
# Use Jekyll's native relative_url filter
|
13
|
+
include Jekyll::Filters::URLFilters
|
14
|
+
|
15
|
+
CONVERTER_CLASS = Jekyll::Converters::Markdown
|
16
|
+
# config
|
17
|
+
CONFIG_KEY = "namespaces"
|
18
|
+
ENABLED_KEY = "enabled"
|
19
|
+
INCLUDE_KEY = "include"
|
20
|
+
# graph config
|
21
|
+
GRAPH_DATA_KEY = "d3_graph_data"
|
22
|
+
ENABLED_GRAPH_DATA_KEY = "enabled"
|
23
|
+
EXCLUDE_GRAPH_KEY = "exclude"
|
24
|
+
GRAPH_ASSETS_LOCATION_KEY = "path"
|
25
|
+
|
26
|
+
def initialize(config)
|
27
|
+
@config ||= config
|
28
|
+
@testing ||= config['testing'] if config.keys.include?('testing')
|
29
|
+
end
|
30
|
+
|
31
|
+
def generate(site)
|
32
|
+
return if disabled?
|
33
|
+
|
34
|
+
# setup site
|
35
|
+
@site = site
|
36
|
+
@context ||= Context.new(site)
|
37
|
+
|
38
|
+
# setup markdown docs
|
39
|
+
docs = []
|
40
|
+
docs += site.pages if include?(:pages)
|
41
|
+
docs += site.docs_to_write.filter { |d| include?(d.type) }
|
42
|
+
@md_docs = docs.filter {|doc| markdown_extension?(doc.extname) }
|
43
|
+
|
44
|
+
# setup tree
|
45
|
+
root_doc = @md_docs.detect {|doc| doc.data['slug'] == 'root' }
|
46
|
+
root = Node.new(root_doc.data['id'], 'root', root_doc.data['title'], root_doc)
|
47
|
+
# build tree
|
48
|
+
@md_docs.each do |cur_doc|
|
49
|
+
# add path to tree
|
50
|
+
if !cur_doc.data['slug'].nil? and cur_doc.data['slug'] != 'root'
|
51
|
+
self.add_path(root, cur_doc)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# print_tree(root)
|
56
|
+
|
57
|
+
# generate tree metadata
|
58
|
+
@md_docs.each do |cur_doc|
|
59
|
+
if !excluded_in_graph?(cur_doc)
|
60
|
+
# TODO: cur_doc.data['namespace'] = cur_doc.basename_without_ext
|
61
|
+
cur_doc.data['namespace'] = cur_doc.basename[0...-3]
|
62
|
+
cur_doc.data['ancestors'], cur_doc.data['children'] = self.find_doc_immediate_relatives(cur_doc, root)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
# graph
|
66
|
+
if !disabled_graph_data?
|
67
|
+
self.write_graph(root)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# config helpers
|
72
|
+
|
73
|
+
def disabled?
|
74
|
+
option(ENABLED_KEY) == false
|
75
|
+
end
|
76
|
+
|
77
|
+
def include?(type)
|
78
|
+
return false unless option(INCLUDE_KEY)
|
79
|
+
return option(INCLUDE_KEY).include?(type.to_s)
|
80
|
+
end
|
81
|
+
|
82
|
+
def markdown_extension?(extension)
|
83
|
+
markdown_converter.matches(extension)
|
84
|
+
end
|
85
|
+
|
86
|
+
def markdown_converter
|
87
|
+
@markdown_converter ||= @site.find_converter_instance(CONVERTER_CLASS)
|
88
|
+
end
|
89
|
+
|
90
|
+
def option(key)
|
91
|
+
config[CONFIG_KEY] && config[CONFIG_KEY][key]
|
92
|
+
end
|
93
|
+
|
94
|
+
# graph config helpers
|
95
|
+
|
96
|
+
def disabled_graph_data?
|
97
|
+
option_graph(ENABLED_GRAPH_DATA_KEY) == false
|
98
|
+
end
|
99
|
+
|
100
|
+
def excluded_in_graph?(type)
|
101
|
+
return false unless option_graph(EXCLUDE_GRAPH_KEY)
|
102
|
+
return option_graph(EXCLUDE_GRAPH_KEY).include?(type.to_s)
|
103
|
+
end
|
104
|
+
|
105
|
+
def has_custom_assets_path?
|
106
|
+
return !!option_graph(GRAPH_ASSETS_LOCATION_KEY)
|
107
|
+
end
|
108
|
+
|
109
|
+
def option_graph(key)
|
110
|
+
config[GRAPH_DATA_KEY] && config[GRAPH_DATA_KEY][key]
|
111
|
+
end
|
112
|
+
|
113
|
+
# helpers
|
114
|
+
|
115
|
+
# add unique path for the given doc to tree (node-class).
|
116
|
+
def add_path(node, doc, depth=1)
|
117
|
+
chunked_namespace = doc.data['slug'].split(/\s|\./)
|
118
|
+
# handle doc if the given node was not root and we are at depth
|
119
|
+
if depth == chunked_namespace.length
|
120
|
+
cur_nd_namespace = 'root' + '.' + doc.data['slug']
|
121
|
+
cur_nd_id = doc.data['id']
|
122
|
+
cur_nd_title = doc.data['title']
|
123
|
+
# create node if one does not exist
|
124
|
+
unless node.children.any?{ |c| c.namespace == cur_nd_namespace }
|
125
|
+
new_node = Node.new(cur_nd_id, cur_nd_namespace, cur_nd_title, doc)
|
126
|
+
node.children << new_node
|
127
|
+
# fill-in node if one already exists
|
128
|
+
else
|
129
|
+
cur_node = node.children.detect {|c| c.namespace == cur_nd_namespace }
|
130
|
+
cur_node.id = cur_nd_id
|
131
|
+
cur_node.title = cur_nd_title
|
132
|
+
cur_node.doc = doc
|
133
|
+
end
|
134
|
+
return
|
135
|
+
# create temp node and recurse
|
136
|
+
else
|
137
|
+
cur_namespace = 'root' + '.' + chunked_namespace[0..(depth - 1)].join('.')
|
138
|
+
unless node.children.any?{ |c| c.namespace == cur_namespace }
|
139
|
+
new_node = Node.new('', cur_namespace, '', '')
|
140
|
+
node.children << new_node
|
141
|
+
else
|
142
|
+
new_node = node.children.detect {|c| c.namespace == cur_namespace }
|
143
|
+
end
|
144
|
+
end
|
145
|
+
self.add_path(new_node, doc, depth + 1)
|
146
|
+
end
|
147
|
+
|
148
|
+
# find the parent and children of the 'target_doc'.
|
149
|
+
# ('node' as in the current node, which first is root.)
|
150
|
+
def find_doc_immediate_relatives(target_doc, node, ancestors=[])
|
151
|
+
if target_doc.data['id'] == node.id
|
152
|
+
children = []
|
153
|
+
node.children.each do |child|
|
154
|
+
if child.id == ''
|
155
|
+
children << {
|
156
|
+
'id' => '',
|
157
|
+
'title' => child.namespace.match('([^.]*$)')[0].gsub('-', ' ')
|
158
|
+
}
|
159
|
+
else
|
160
|
+
children << child.doc
|
161
|
+
end
|
162
|
+
end
|
163
|
+
return ancestors, children
|
164
|
+
else
|
165
|
+
if node.id == ''
|
166
|
+
ancestors << {
|
167
|
+
'id' => '',
|
168
|
+
'title' => node.namespace.match('([^.]*$)')[0].gsub('-', ' ')
|
169
|
+
}
|
170
|
+
else
|
171
|
+
ancestors << node.doc
|
172
|
+
end
|
173
|
+
results = []
|
174
|
+
node.children.each do |child_node|
|
175
|
+
results.concat self.find_doc_immediate_relatives(target_doc, child_node, ancestors.clone)
|
176
|
+
end
|
177
|
+
return results.select { |r| !r.nil? }
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
# ...for debugging
|
182
|
+
def print_tree(node, ancestors=[])
|
183
|
+
Jekyll.logger.warn "Ancestors: ", ancestors.length
|
184
|
+
Jekyll.logger.warn node
|
185
|
+
Jekyll.logger.warn "Children: ", node.children
|
186
|
+
ancestors.append(node.id)
|
187
|
+
node.children.each do |child_node|
|
188
|
+
self.print_tree(child_node, ancestors.clone)
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
# graph helpers
|
193
|
+
|
194
|
+
# convert tree (node-class) to json
|
195
|
+
def tree_to_json(baseurl, node, json_node={})
|
196
|
+
if node.id.empty?
|
197
|
+
Jekyll.logger.warn "Tree node missing: ", node.namespace
|
198
|
+
label = node.namespace.match('([^.]*$)')[0].gsub('-', ' ')
|
199
|
+
node_url = ''
|
200
|
+
else
|
201
|
+
label = node.title
|
202
|
+
node_url = relative_url(node.doc.url)
|
203
|
+
end
|
204
|
+
json_children = []
|
205
|
+
node.children.each do |child|
|
206
|
+
new_child = self.tree_to_json(baseurl, child)
|
207
|
+
json_children.append(new_child)
|
208
|
+
end
|
209
|
+
json_node = {
|
210
|
+
# "id": node.id,
|
211
|
+
"id": node_url,
|
212
|
+
"namespace": node.namespace,
|
213
|
+
"label": label,
|
214
|
+
"children": json_children,
|
215
|
+
"url": node_url,
|
216
|
+
}
|
217
|
+
return json_node
|
218
|
+
end
|
219
|
+
|
220
|
+
def write_graph(root)
|
221
|
+
assets_path = has_custom_assets_path? ? option_graph(GRAPH_ASSETS_LOCATION_KEY) : "/assets"
|
222
|
+
if !File.directory?(File.join(site.source, assets_path))
|
223
|
+
Jekyll.logger.error "Assets location does not exist, please create required directories for path: ", assets_path
|
224
|
+
end
|
225
|
+
# from: https://github.com/jekyll/jekyll/issues/7195#issuecomment-415696200
|
226
|
+
static_file = Jekyll::StaticFile.new(site, site.source, assets_path, "graph-tree.json")
|
227
|
+
json_formatted_tree = self.tree_to_json(@site.baseurl, root)
|
228
|
+
File.write(@site.source + static_file.relative_path, JSON.dump(
|
229
|
+
json_formatted_tree
|
230
|
+
))
|
231
|
+
# tests fail without manually adding the static file, but actual site builds seem to do ok
|
232
|
+
# ...although there does seem to be a race condition which causes a rebuild to be necessary in order to detect the graph data file
|
233
|
+
if @testing
|
234
|
+
@site.static_files << static_file if !@site.static_files.include?(static_file)
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
238
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# helper class for tree-building.
|
4
|
+
class Node
|
5
|
+
attr_accessor :id, :namespace, :title, :children, :doc
|
6
|
+
|
7
|
+
def initialize(id, namespace, title, doc)
|
8
|
+
@id = id
|
9
|
+
@children = []
|
10
|
+
@namespace = namespace
|
11
|
+
@title = title
|
12
|
+
@doc = doc
|
13
|
+
end
|
14
|
+
|
15
|
+
def type
|
16
|
+
return doc.type
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_s
|
20
|
+
"namespace: #{@namespace}"
|
21
|
+
end
|
22
|
+
end
|
metadata
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: jekyll-namespaces
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- manunamz
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2021-07-23 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description:
|
14
|
+
email:
|
15
|
+
- manunamz@pm.me
|
16
|
+
executables: []
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- lib/jekyll-namespaces.rb
|
21
|
+
- lib/jekyll-namespaces/context.rb
|
22
|
+
- lib/jekyll-namespaces/node.rb
|
23
|
+
- lib/jekyll-namespaces/version.rb
|
24
|
+
homepage: https://github.com/manunamz/jekyll-namespaces
|
25
|
+
licenses:
|
26
|
+
- MIT
|
27
|
+
metadata:
|
28
|
+
homepage_uri: https://github.com/manunamz/jekyll-namespaces
|
29
|
+
source_code_uri: https://github.com/manunamz/jekyll-namespaces
|
30
|
+
changelog_uri: https://github.com/manunamz/jekyll-namespaces/blob/main/CHANGELOG.md
|
31
|
+
post_install_message:
|
32
|
+
rdoc_options: []
|
33
|
+
require_paths:
|
34
|
+
- lib
|
35
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ">="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: 2.4.0
|
40
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
41
|
+
requirements:
|
42
|
+
- - ">="
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
version: '0'
|
45
|
+
requirements: []
|
46
|
+
rubygems_version: 3.2.17
|
47
|
+
signing_key:
|
48
|
+
specification_version: 4
|
49
|
+
summary: Add jekyll support for namespaced.filenames (in markdown files).
|
50
|
+
test_files: []
|