jekyll-wikilinks 0.0.5 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,192 +1,50 @@
1
1
  # frozen_string_literal: true
2
2
  require "jekyll"
3
3
 
4
- require_relative "jekyll-wikilinks/context"
5
- require_relative "jekyll-wikilinks/doc_manager"
6
- require_relative "jekyll-wikilinks/link_index"
7
- require_relative "jekyll-wikilinks/parser"
8
-
9
- require_relative "jekyll-wikilinks/filter"
10
4
  require_relative "jekyll-wikilinks/version"
11
5
 
12
- Liquid::Template.register_filter(JekyllWikiLinks::TypeFilters)
13
-
14
- module JekyllWikiLinks
15
- class Generator < Jekyll::Generator
16
- attr_accessor :site, :config, :md_docs, :doc_manager, :link_index, :parser, :graph_nodes, :graph_links
17
-
18
- # Use Jekyll's native relative_url filter
19
- include Jekyll::Filters::URLFilters
20
-
21
- CONVERTER_CLASS = Jekyll::Converters::Markdown
22
- # config
23
- CONFIG_KEY = "wikilinks"
24
- ENABLED_KEY = "enabled"
25
- EXCLUDE_KEY = "exclude"
26
- # graph config
27
- GRAPH_DATA_KEY = "d3_graph_data"
28
- ENABLED_GRAPH_DATA_KEY = "enabled"
29
- EXCLUDE_GRAPH_KEY = "exclude"
30
- GRAPH_ASSETS_LOCATION_KEY = "assets_rel_path"
31
-
32
- # identify missing links in doc via .invalid-wiki-link class and nested doc-text.
33
- REGEX_INVALID_WIKI_LINK = /invalid-wiki-link#{REGEX_NOT_GREEDY}\[\[(#{REGEX_NOT_GREEDY})\]\]/i
34
-
35
- def initialize(config)
36
- @config ||= config
37
- @testing ||= config['testing'] if config.keys.include?('testing')
38
- end
39
-
40
- def generate(site)
41
- return if disabled?
42
- self.old_config_warn()
43
- Jekyll.logger.debug "Excluded jekyll types: ", option(EXCLUDE_KEY)
44
- Jekyll.logger.debug "Excluded jekyll types in graph: ", option_graph(EXCLUDE_GRAPH_KEY)
45
-
46
- # setup site
47
- @site = site
48
- @context ||= JekyllWikiLinks::Context.new(site)
49
-
50
- # setup markdown docs
51
- docs = []
52
- docs += site.pages if !exclude?(:pages)
53
- docs += site.docs_to_write.filter { |d| !exclude?(d.type) }
54
- @md_docs = docs.filter {|doc| markdown_extension?(doc.extname) }
55
-
56
- # setup helper classes
57
- @doc_manager = DocManager.new(@md_docs, @site.static_files)
58
- @parser = Parser.new(@context, @markdown_converter, @doc_manager)
59
- @link_index = LinkIndex.new(@site, @doc_manager)
60
-
61
- # parse + populate index
62
- @md_docs.each do |doc|
63
- @parser.parse(doc.content)
64
- @link_index.populate_attributes(doc, @parser.typed_link_blocks)
65
- end
66
- @link_index.process
67
-
68
- # handle graph data
69
- @graph_nodes, @graph_links = [], []
70
- @md_docs.each do |doc|
71
- if !disabled_graph_data? && !self.excluded_in_graph?(doc.type)
72
- self.generate_graph_data(doc)
73
- end
74
- end
75
- if !disabled_graph_data?
76
- self.write_graph_data()
77
- end
78
- end
79
-
80
- # config helpers
81
-
82
- def disabled?
83
- option(ENABLED_KEY) == false
84
- end
6
+ # in order of expected execution
85
7
 
86
- def exclude?(type)
87
- return false unless option(EXCLUDE_KEY)
88
- return option(EXCLUDE_KEY).include?(type.to_s)
89
- end
90
-
91
- def has_custom_assets_path?
92
- return !!option_graph(GRAPH_ASSETS_LOCATION_KEY)
93
- end
94
-
95
- def markdown_extension?(extension)
96
- markdown_converter.matches(extension)
97
- end
98
-
99
- def markdown_converter
100
- @markdown_converter ||= site.find_converter_instance(CONVERTER_CLASS)
101
- end
102
-
103
- def option(key)
104
- config[CONFIG_KEY] && config[CONFIG_KEY][key]
105
- end
106
-
107
- # graph config helpers
108
-
109
- def disabled_graph_data?
110
- option_graph(ENABLED_GRAPH_DATA_KEY) == false
111
- end
8
+ # setup config
9
+ require_relative "jekyll-wikilinks/config"
10
+ Jekyll::Hooks.register :site, :after_init do |site|
11
+ # global '$wiki_conf' to ensure that all local jekyll plugins
12
+ # are reading from the same configuration
13
+ # (global var is not ideal, but is DRY)
14
+ $wiki_conf = Jekyll::WikiLinks::PluginConfig.new(site.config)
15
+ end
112
16
 
113
- def excluded_in_graph?(type)
114
- return false unless option_graph(EXCLUDE_KEY)
115
- return option_graph(EXCLUDE_KEY).include?(type.to_s)
116
- end
17
+ # setup docs (based on configs)
18
+ require_relative "jekyll-wikilinks/patch/doc_manager"
19
+ Jekyll::Hooks.register :site, :post_read do |site|
20
+ if !$wiki_conf.disabled?
21
+ site.doc_mngr = Jekyll::WikiLinks::DocManager.new(site)
22
+ end
23
+ end
117
24
 
118
- def option_graph(key)
119
- config[GRAPH_DATA_KEY] && config[GRAPH_DATA_KEY][key]
120
- end
25
+ # convert
26
+ require_relative "jekyll-wikilinks/plugins/converter"
121
27
 
122
- # graph helpers
28
+ # generate
29
+ require_relative "jekyll-wikilinks/plugins/generator"
123
30
 
124
- def generate_graph_data(doc)
125
- Jekyll.logger.debug "Processing graph nodes for doc: ", doc.data['title']
126
- # missing nodes
127
- missing_node_names = doc.content.scan(REGEX_INVALID_WIKI_LINK)
128
- if !missing_node_names.nil?
129
- missing_node_names.each do |missing_node_name_captures|
130
- missing_node_name = missing_node_name_captures[0]
131
- if graph_nodes.none? { |node| node[:id] == missing_node_name }
132
- Jekyll.logger.warn "Net-Web node missing: ", missing_node_name
133
- Jekyll.logger.warn " in: ", doc.data['slug']
134
- graph_nodes << {
135
- id: missing_node_name,
136
- url: '',
137
- label: missing_node_name,
138
- }
139
- end
140
- graph_links << {
141
- source: relative_url(doc.url),
142
- target: missing_node_name,
143
- }
144
- end
145
- end
146
- # existing nodes
147
- graph_nodes << {
148
- id: relative_url(doc.url),
149
- url: relative_url(doc.url),
150
- label: doc.data['title'],
151
- }
152
- # TODO: this link calculation ends up with duplicates -- re-visit this later.
153
- all_links = doc.data['attributed'] + doc.data['backlinks']
154
- all_links.each do |link|
155
- linked_doc = link['doc']
156
- if !excluded_in_graph?(linked_doc.type)
157
- graph_links << {
158
- source: relative_url(linked_doc.url),
159
- target: relative_url(doc.url),
160
- }
161
- end
162
- end
163
- end
31
+ # convert fores
32
+ # Jekyll::Hooks.register :documents, :pre_convert do |doc|
33
+ # Jekyll:WikiLinks::Parser.parse_blocks(doc)
34
+ # @site.link_index.populate_fores(doc, typed_link_blocks, md_docs)
35
+ # end
164
36
 
165
- def write_graph_data()
166
- assets_path = has_custom_assets_path? ? option_graph(GRAPH_ASSETS_LOCATION_KEY) : "/assets"
167
- if !File.directory?(File.join(site.source, assets_path))
168
- Jekyll.logger.error "Assets location does not exist, please create required directories for path: ", assets_path
169
- end
170
- # from: https://github.com/jekyll/jekyll/issues/7195#issuecomment-415696200
171
- static_file = Jekyll::StaticFile.new(site, site.source, assets_path, "graph-net-web.json")
172
- # TODO: make write file location more flexible -- requiring a write location configuration feels messy...
173
- File.write(@site.source + static_file.relative_path, JSON.dump({
174
- links: graph_links,
175
- nodes: graph_nodes,
176
- }))
177
- # tests fail without manually adding the static file, but actual site builds seem to do ok
178
- # ...although there does seem to be a race condition which causes a rebuild to be necessary in order to detect the graph data file
179
- if @testing
180
- @site.static_files << static_file if !@site.static_files.include?(static_file)
181
- end
182
- end
37
+ # convert backs
38
+ # Jekyll::Hooks.register :documents, :pre_convert do |doc|
39
+ # Jekyll:WikiLinks::Parser.parse_inlines(doc)
40
+ # @site.link_index.populate_backs(doc, typed_link_blocks, md_docs)
41
+ # end
183
42
 
184
- # !! deprecated !!
43
+ # generate metadata
44
+ # Jekyll::Hooks.register :documents, :post_convert do |doc|
45
+ # Jekyll:WikiLinks::Generator.generate(doc)
46
+ # end
185
47
 
186
- def old_config_warn()
187
- if config.include?("wikilinks_collection")
188
- Jekyll.logger.warn "As of 0.0.3, 'wikilinks_collection' is no longer used for configs. jekyll-wikilinks will scan all markdown files by default. Check README for details."
189
- end
190
- end
191
- end
192
- end
48
+ # hook up liquid filters
49
+ require_relative "jekyll-wikilinks/plugins/filter"
50
+ Liquid::Template.register_filter(Jekyll::WikiLinks::TypeFilters)
metadata CHANGED
@@ -1,15 +1,43 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jekyll-wikilinks
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5
4
+ version: 0.0.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - manunamz
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-07-03 00:00:00.000000000 Z
12
- dependencies: []
11
+ date: 2021-09-17 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: jekyll
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 4.2.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 4.2.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: nokogiri
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 1.12.3
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 1.12.3
13
41
  description:
14
42
  email:
15
43
  - manunamz@pm.me
@@ -18,20 +46,24 @@ extensions: []
18
46
  extra_rdoc_files: []
19
47
  files:
20
48
  - lib/jekyll-wikilinks.rb
21
- - lib/jekyll-wikilinks/context.rb
22
- - lib/jekyll-wikilinks/doc_manager.rb
23
- - lib/jekyll-wikilinks/filter.rb
24
- - lib/jekyll-wikilinks/jekyll_patch.rb
25
- - lib/jekyll-wikilinks/link_index.rb
26
- - lib/jekyll-wikilinks/naming_const.rb
27
- - lib/jekyll-wikilinks/parser.rb
49
+ - lib/jekyll-wikilinks/config.rb
50
+ - lib/jekyll-wikilinks/patch/context.rb
51
+ - lib/jekyll-wikilinks/patch/doc_manager.rb
52
+ - lib/jekyll-wikilinks/patch/site.rb
53
+ - lib/jekyll-wikilinks/plugins/converter.rb
54
+ - lib/jekyll-wikilinks/plugins/filter.rb
55
+ - lib/jekyll-wikilinks/plugins/generator.rb
56
+ - lib/jekyll-wikilinks/util/link_index.rb
57
+ - lib/jekyll-wikilinks/util/parser.rb
58
+ - lib/jekyll-wikilinks/util/regex.rb
28
59
  - lib/jekyll-wikilinks/version.rb
29
60
  homepage: https://github.com/manunamz/jekyll-wikilinks
30
61
  licenses:
31
- - MIT
62
+ - GPL3
32
63
  metadata:
33
64
  homepage_uri: https://github.com/manunamz/jekyll-wikilinks
34
65
  source_code_uri: https://github.com/manunamz/jekyll-wikilinks
66
+ changelog_uri: https://github.com/manunamz/jekyll-wikilinks/blob/main/CHANGELOG.md
35
67
  post_install_message:
36
68
  rdoc_options: []
37
69
  require_paths:
@@ -50,5 +82,5 @@ requirements: []
50
82
  rubygems_version: 3.2.17
51
83
  signing_key:
52
84
  specification_version: 4
53
- summary: Add jekyll support for [[wikilinks]] (in markdown).
85
+ summary: Add jekyll support for [[wikilinks]] (in markdown files).
54
86
  test_files: []
@@ -1,15 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module JekyllWikiLinks
4
- class Context
5
- attr_reader :site
6
-
7
- def initialize(site)
8
- @site = site
9
- end
10
-
11
- def registers
12
- { :site => site }
13
- end
14
- end
15
- end
@@ -1,68 +0,0 @@
1
- require_relative "naming_const"
2
-
3
- module JekyllWikiLinks
4
- class DocManager
5
- attr_accessor :md_docs, :static_files
6
-
7
- # kramdown header regexes
8
- # atx header: https://github.com/gettalong/kramdown/blob/master/lib/kramdown/parser/kramdown/header.rb#L29
9
- REGEX_ATX_HEADER = /^\#{1,6}[\t ]*([^ \t].*)\n/i
10
- # setext header: https://github.com/gettalong/kramdown/blob/master/lib/kramdown/parser/kramdown/header.rb#L17
11
- REGEX_SETEXT_HEADER = /^ {0,3}([^ \t].*)\n[-=][-=]*[ \t\r\f\v]*\n/i
12
- # obsidian-style
13
- REGEX_BLOCK = /.*\s\^#{REGEX_BLOCK_ID_TXT}^\n/i
14
-
15
- def initialize(md_docs, static_files)
16
- @md_docs ||= md_docs
17
- @static_files ||= static_files
18
- end
19
-
20
- def all
21
- return @md_docs
22
- end
23
-
24
- def get_doc_by_fname(filename)
25
- return nil if filename.nil? || @md_docs.size == 0
26
- docs = @md_docs.select{ |d| File.basename(d.basename, File.extname(d.basename)) == filename }
27
- return nil if docs.nil? || docs.size > 1
28
- return docs[0]
29
- end
30
-
31
- def get_doc_by_url(url)
32
- return nil if url.nil? || @md_docs.size == 0
33
- docs = @md_docs.select{ |d| d.url == url }
34
- return nil if docs.nil? || docs.size > 1
35
- return docs[0]
36
- end
37
-
38
- def get_doc_content(filename)
39
- return nil if filename.nil? || @md_docs.size == 0
40
- docs = @md_docs.select{ |d| File.basename(d.basename, File.extname(d.basename)) == filename }
41
- return docs[0].content if docs.size == 1
42
- return nil
43
- end
44
-
45
- # 'bname' -> 'basename' (filename with)
46
- def get_image_by_bname(filename)
47
- return nil if filename.nil? || @static_files.size == 0 || !SUPPORTED_IMG_FORMATS.any?{ |ext| ext == File.extname(filename).downcase }
48
- docs = @static_files.select{ |d| File.basename(d.relative_path) == filename }
49
- return nil if docs.nil? || docs.size > 1
50
- return docs[0]
51
- end
52
-
53
- def self.doc_has_header?(doc, header)
54
- return nil if header.nil?
55
- # leading + trailing whitespace is ignored when matching headers
56
- header_results = doc.content.scan(REGEX_ATX_HEADER).flatten.map { |htxt| htxt.strip }
57
- setext_header_results = doc.content.scan(REGEX_SETEXT_HEADER).flatten.map { |htxt| htxt.strip }
58
- return header_results.include?(header.strip) || setext_header_results.include?(header.strip)
59
- end
60
-
61
- def self.doc_has_block_id?(doc, block_id)
62
- return nil if block_id.nil?
63
- # leading + trailing whitespace is ignored when matching blocks
64
- block_id_results = doc.content.scan(REGEX_BLOCK).flatten.map { |bid| bid.strip }
65
- return block_id_results.include?(block_id)
66
- end
67
- end
68
- end
@@ -1,39 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module JekyllWikiLinks
4
- module TypeFilters
5
- # 'links' accepts both untyped links, typed links, and attributes; fore and back.
6
-
7
- # usage: {% assign note_links = page.links | doc_type = "notes" %}
8
- # "doc_type" is the jekyll type ("pages", "posts", "<collection-name>")
9
- def doc_type(links, doc_type)
10
- return if links.nil?
11
- target_links = []
12
- links.each do |l|
13
- target_links << l if self.to_string(l['doc'].type) == doc_type.to_str
14
- end
15
- return target_links.uniq
16
- end
17
-
18
- # usage: {% assign author_links = page.links | link_type = "author" %}
19
- # "link_type" is the wikilink's type, the string that appears before the link in `link-type::[[wikilink]]`.
20
- def link_type(links, link_type)
21
- return if links.nil?
22
- target_links = []
23
- link.each do |l|
24
- target_links << l if self.to_string(l['type'].to_str) == link_type.to_str
25
- end
26
- return target_links.uniq
27
- end
28
-
29
- def to_string(type)
30
- return type if type.is_a?(String)
31
- type = type.to_s
32
- begin
33
- String(type)
34
- rescue ::ArgumentError
35
- raise ArgumentError, "invalid type"
36
- end
37
- end
38
- end
39
- end
@@ -1,24 +0,0 @@
1
- # TODO: Would be nice to put LinkIndex-related data into real objects as opposed to sticking tons and tons of data into frontmatter...I think...
2
-
3
- # modelling off of 'related_posts': https://github.com/jekyll/jekyll/blob/6855200ebda6c0e33f487da69e4e02ec3d8286b7/lib/jekyll/document.rb#L402
4
- # module LinkLogic
5
- # attr_accessor :attributed, :attributes, :backlinks, :forelinks
6
-
7
- # # 'links' 'type' is 'nil' for untyped links.
8
- # # 'attributes' are block-level typed forelinks; their 'type' may not be 'nil'.
9
- # # 'attributed' are block-level typed backlinks; their 'type' may not be 'nil'.
10
- # # [{ 'type': str, 'doc': doc }, ...]
11
- # end
12
-
13
- # module Jekyll
14
- # class Page
15
- # # it would be nice if these would only exist if the page is guaranteed to be a markdown file.
16
- # include LinkLogic
17
- # end
18
- # end
19
-
20
- # module Jekyll
21
- # class Document
22
- # include LinkLogic
23
- # end
24
- # end
@@ -1,81 +0,0 @@
1
- module JekyllWikiLinks
2
- class LinkIndex
3
- attr_accessor :index
4
-
5
- # Use Jekyll's native relative_url filter
6
- include Jekyll::Filters::URLFilters
7
-
8
- REGEX_LINK_TYPE = /<a\sclass="wiki-link(\slink-type\s(?<link-type>([^"]+)))?"\shref="(?<link-url>([^"]+))">/i
9
-
10
- def initialize(site, doc_manager)
11
- @context ||= JekyllWikiLinks::Context.new(site)
12
- @doc_manager ||= doc_manager
13
- @index = {}
14
- @doc_manager.all.each do |doc|
15
- @index[doc.url] = LinksInfo.new()
16
- end
17
- end
18
-
19
- def process
20
- self.populate_links()
21
- # apply index info to each document
22
- @doc_manager.all.each do |doc|
23
- doc.data['attributed'] = @index[doc.url].attributed
24
- doc.data['backlinks'] = @index[doc.url].backlinks
25
- doc.data['attributes'] = @index[doc.url].attributes
26
- doc.data['forelinks'] = @index[doc.url].forelinks
27
- end
28
- end
29
-
30
- def populate_attributes(doc, typed_link_blocks)
31
- typed_link_blocks.each do |tl|
32
- attr_doc = @doc_manager.get_doc_by_fname(tl.filename)
33
- @index[doc.url].attributes << {
34
- 'type' => tl.link_type,
35
- 'doc' => attr_doc,
36
- }
37
- @index[attr_doc.url].attributed << {
38
- 'type' => tl.link_type,
39
- 'doc' => doc,
40
- }
41
- end
42
- end
43
-
44
- def populate_links()
45
- # for each document...
46
- @doc_manager.all.each do |doc|
47
- # ...process its forelinks
48
- doc.content.scan(REGEX_LINK_TYPE).each do |m|
49
- ltype, lurl = m[0], m[1]
50
- @index[doc.url].forelinks << {
51
- 'type' => ltype,
52
- 'doc' => @doc_manager.get_doc_by_url(lurl),
53
- }
54
- end
55
- # ...process its backlinks
56
- @doc_manager.all.each do |doc_to_backlink|
57
- doc_to_backlink.content.scan(REGEX_LINK_TYPE).each do |m|
58
- ltype, lurl = m[0], m[1]
59
- if lurl == relative_url(doc.url)
60
- @index[doc.url].backlinks << {
61
- 'type' => ltype,
62
- 'doc' => doc_to_backlink,
63
- }
64
- end
65
- end
66
- end
67
- end
68
- end
69
-
70
- class LinksInfo
71
- attr_accessor :attributed, :attributes, :backlinks, :forelinks
72
-
73
- def initialize
74
- @attributed = [] # (block-level typed forelinks)
75
- @attributes = [] # (block-level typed backlinks)
76
- @backlinks = []
77
- @forelinks = []
78
- end
79
- end
80
- end
81
- end
@@ -1,21 +0,0 @@
1
- # naming_const.rb
2
- # regex constants defining supported file types and valid names for files, variables, or text
3
- #
4
-
5
- module JekyllWikiLinks
6
- # TODO: Fix REGEX_NOT_GREEDY
7
- # REGEX_NOT_GREEDY = /[^(?!\]\])]+/i
8
- # REGEX_NOT_GREEDY = /(?!\]\]).*/i
9
- REGEX_NOT_GREEDY = /[^\]]+/i
10
- # <variables> only work with 'match' function, not with 'scan' function. :/
11
- # oh well...they are there for easier debugging...
12
- # valid naming conventions # capture indeces for WikiLinks class (0 is 'embed')
13
- REGEX_LINK_TYPE_TXT = /(?<link-type-txt>([^\n\s\!\#\^\|\]]+))/i # 1
14
- REGEX_FILENAME = /(?<filename>([^\\\/:\#\^\|\[\]]+))/i # 2
15
- REGEX_HEADER_TXT = /(?<header-txt>([^\!\#\^\|\[\]]+))/i # 3
16
- REGEX_BLOCK_ID_TXT = /(?<block-id>([^\\\/:\!\#\^\|\[\]]+))/i # 4
17
- REGEX_LABEL_TXT = /(?<label-txt>(#{REGEX_NOT_GREEDY}))/i # 5
18
-
19
- # from: https://docs.github.com/en/github/managing-files-in-a-repository/working-with-non-code-files/rendering-and-diffing-images
20
- SUPPORTED_IMG_FORMATS = Set.new(['.png', '.jpg', '.gif', '.psd', '.svg'])
21
- end