-
-
Notifications
You must be signed in to change notification settings - Fork 39
/
generator.rb
130 lines (104 loc) · 3.5 KB
/
generator.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
module JekyllRelativeLinks
class Generator < Jekyll::Generator
attr_accessor :site
# Use Jekyll's native relative_url filter
include Jekyll::Filters::URLFilters
LINK_TEXT_REGEX = %r!([^\]]+)!
FRAGMENT_REGEX = %r!(#.+?)?!
INLINE_LINK_REGEX = %r!\[#{LINK_TEXT_REGEX}\]\(([^\)]+?)#{FRAGMENT_REGEX}\)!
REFERENCE_LINK_REGEX = %r!^\s*?\[#{LINK_TEXT_REGEX}\]: (.+?)#{FRAGMENT_REGEX}\s*?$!
LINK_REGEX = %r!(#{INLINE_LINK_REGEX}|#{REFERENCE_LINK_REGEX})!
CONVERTER_CLASS = Jekyll::Converters::Markdown
CONFIG_KEY = "relative_links".freeze
ENABLED_KEY = "enabled".freeze
COLLECTIONS_KEY = "collections".freeze
safe true
priority :lowest
def initialize(site)
@site = site
@context = context
end
def generate(site)
@site = site
@context = context
return if disabled?
documents = site.pages
documents = site.pages + site.docs_to_write if collections?
documents.each do |document|
next unless markdown_extension?(document.extname)
replace_relative_links!(document)
end
end
def replace_relative_links!(document)
url_base = File.dirname(document.relative_path)
document.content.gsub!(LINK_REGEX) do |original|
link_type, link_text, relative_path, fragment = link_parts(Regexp.last_match)
next original if fragment?(relative_path) || absolute_url?(relative_path)
path = path_from_root(relative_path, url_base)
url = url_for_path(path)
if url
replacement_text(link_type, link_text, url, fragment)
else
original
end
end
rescue ArgumentError => e
raise e unless e.to_s.start_with?("invalid byte sequence in UTF-8")
end
private
def link_parts(matches)
link_type = matches[2] ? :inline : :reference
link_text = matches[link_type == :inline ? 2 : 5]
relative_path = matches[link_type == :inline ? 3 : 6]
fragment = matches[link_type == :inline ? 4 : 7]
[link_type, link_text, relative_path, fragment]
end
def context
JekyllRelativeLinks::Context.new(site)
end
def markdown_extension?(extension)
markdown_converter.matches(extension)
end
def markdown_converter
@markdown_converter ||= site.find_converter_instance(CONVERTER_CLASS)
end
def url_for_path(path)
target = potential_targets.find { |p| p.relative_path.sub(%r!\A/!, "") == path }
relative_url(target.url) if target && target.url
end
def potential_targets
@potential_targets ||= site.pages + site.static_files + site.docs_to_write
end
def path_from_root(relative_path, url_base)
relative_path.sub!(%r!\A/!, "")
absolute_path = File.expand_path(relative_path, url_base)
absolute_path.sub(%r!\A#{Regexp.escape(Dir.pwd)}/!, "")
end
def replacement_text(type, text, url, fragment = nil)
url << fragment if fragment
if type == :inline
"[#{text}](#{url})"
else
"\n[#{text}]: #{url}"
end
end
def absolute_url?(string)
return unless string
Addressable::URI.parse(string).absolute?
rescue Addressable::URI::InvalidURIError
nil
end
def fragment?(string)
string && string.start_with?("#")
end
def option(key)
site.config[CONFIG_KEY] && site.config[CONFIG_KEY][key]
end
def disabled?
option(ENABLED_KEY) == false
end
def collections?
option(COLLECTIONS_KEY) == true
end
end
end