jekyll-mentioji 0.1.0

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.
Files changed (5) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.md +56 -0
  4. data/lib/jekyll-mentioji.rb +181 -0
  5. metadata +146 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 2f7969c91c7033c67e9327d2cb3dac8687bc30bd
4
+ data.tar.gz: a61f334384fc26db61b2adf0a9996768f55a02c9
5
+ SHA512:
6
+ metadata.gz: 6ecf13698e63ef1cd5ce1565108e0d678b5f3610086456202fcb9091594706ae79467e0cd750200ade2d7a6344f029be16ddf0ecbb517a773b9fb6409fad39f6
7
+ data.tar.gz: 8b04cc659afae2e1f9666c9b5ca9be95444351da0e5b38ca3260b757be43310219f0717f630524222daf65518c1072600b75bef623d841533dcb769d25f2f4be
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT LICENSE
2
+
3
+ Copyright (c) 2018 Ashwin Maroli
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,56 @@
1
+ # Jekyll Mentioji
2
+
3
+ If your site uses both [`jekyll-mentions`](https://github.com/jekyll/jekyll-mentions) and [`jemoji`](https://github.com/jekyll/jemoji), then
4
+ using `jekyll-mentioji` *instead*, is going to improve your build times.
5
+
6
+ Internally, this plugin combines the code & logic behind the above two plugins and adapts the associated code in
7
+ [`html-pipeline`](https://github.com/jch/html-pipeline), which the above two plugins wrap around, to emerge as *the faster implementation*
8
+ in comparison to using the two plugins together in a site.
9
+
10
+ ## Usage
11
+
12
+ Replace the two plugins in your `Gemfile` and/or `_config.yml` with this plugin:
13
+
14
+ ```diff
15
+ # Gemfile
16
+
17
+ group :jekyll_plugins do
18
+ - gem "jekyll-mentions"
19
+ - gem "jemoji"
20
+ + gem "jekyll-mentioji"
21
+ end
22
+ ```
23
+ ```sh
24
+ bundle install
25
+ ```
26
+ ```diff
27
+ # _config.yml
28
+
29
+ plugins:
30
+ - - jekyll-mentions
31
+ - - jemoji
32
+ + - jekyll-mentioji
33
+ ```
34
+
35
+ ## Other Notes:
36
+
37
+ - This plugin aims to be a zero-config replacement for the two plugins and therefore should be compatible with existing configuration
38
+ for the two plugins. `jekyll-mentioji` will read both existing `jekyll-mentions:` and `emoji:` settings in your config file.
39
+ - A few diversions from the two official plugins:
40
+ - `@mention` can currently include the underscore (`_`) along with alphanumeric characters and hyphen (`-`)
41
+ - `@mention` will not be rendered within `<script></script>`
42
+ - configuration for emoji assets-path mirror that of `jekyll-mentions`: the value can be both a string or a dictionary
43
+
44
+ ### Build times comparison
45
+
46
+ rvm | jmentions+jemoji | *jmentioji*
47
+ :---: | :----: | :------:
48
+ 2.2.9 | 15.482 | *13.671*
49
+ 2.3.6 | 13.798 | *11.988*
50
+ 2.4.3 | 14.225 | *12.848*
51
+ 2.5.0 | 14.560 | *12.771*
52
+
53
+ (built using `jekyll:master@c7f006`)
54
+
55
+ *Disclaimer: The no.s above are averages of 3 restarted jobs on two separate Travis CI build-commits and therefore cannot be considered as
56
+ solid evidence. However, tests run locally do show an improvement of approx. 2s with `jekyll-mentioji` over the combo.*
@@ -0,0 +1,181 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "gemoji"
4
+ require "nokogiri"
5
+
6
+ module Jekyll
7
+ class Mentioji
8
+ MENTIONPATTERNS = %r~(?:^|\B)@((?>[\w][\w-]*))(?!/)(?=\.+[ \t\W]|\.+$|[^\w.]|$)~i
9
+
10
+ OPENING_BODY_TAG_REGEX = %r!<body(.*?)>\s*!
11
+
12
+ IGNORE_MENTION_PARENTS = %w(pre code a script style).freeze
13
+ IGNORE_EMOJI_PARENTS = %w(pre code tt).freeze
14
+
15
+ class << self
16
+ def transform(doc)
17
+ content = doc.output
18
+ return unless content.include?("@") || content.include?(":")
19
+ setup_transformer(doc.site.config)
20
+ doc.output = if content.include?("<body")
21
+ process_html_body(content)
22
+ else
23
+ process(content)
24
+ end
25
+ end
26
+
27
+ def transformable?(doc)
28
+ (doc.is_a?(Jekyll::Page) || doc.write?) &&
29
+ doc.output_ext == ".html" || (doc.permalink&.end_with?("/"))
30
+ end
31
+
32
+ private
33
+
34
+ def process_html_body(content)
35
+ head, opener, tail = content.partition(OPENING_BODY_TAG_REGEX)
36
+ body_content, *rest = tail.partition("</body>")
37
+
38
+ String.new(head) << opener << process(body_content) << rest.join
39
+ end
40
+
41
+ def process(body_content)
42
+ return body_content unless body_content =~ prelim_check_regex
43
+ parsed_body = Nokogiri::HTML::DocumentFragment.parse(body_content)
44
+ parsed_body.search(".//text()").each do |node|
45
+ content = node.text
46
+ next if !content.include?("@") && !content.include?(":")
47
+ node.replace(
48
+ mention_renderer(
49
+ node, emoji_renderer(node, content)
50
+ )
51
+ )
52
+ end
53
+ parsed_body.to_html
54
+ end
55
+
56
+ # rubocop:disable Naming/MemoizedInstanceVariableName
57
+ def setup_transformer(config)
58
+ @transconfig ||= {
59
+ "mention_base" => compute_base(
60
+ config, "jekyll-mentions", "base_url", default_mention_base
61
+ ),
62
+ "emoji_source" => compute_base(
63
+ config, "emoji", "src", default_asset_root
64
+ ),
65
+ }
66
+ end
67
+ # rubocop:enable Naming/MemoizedInstanceVariableName
68
+
69
+ def compute_base(config, key, subkey, default_value)
70
+ subject = config[key]
71
+ case subject
72
+ when nil, NilClass
73
+ default_value
74
+ when String
75
+ subject
76
+ when Hash
77
+ subject.fetch(subkey, default_value)
78
+ else
79
+ raise TypeError, "Your #{key} config has to either be a string or a hash. It's a " \
80
+ "kind of #{subject.class} right now."
81
+ end
82
+ end
83
+
84
+ def default_mention_base
85
+ if !ENV["SSL"].to_s.empty? && !ENV["GITHUB_HOSTNAME"].to_s.empty?
86
+ scheme = ENV["SSL"] == "true" ? "https://" : "http://"
87
+ "#{scheme}#{ENV["GITHUB_HOSTNAME"].chomp("/")}"
88
+ else
89
+ "https://github.com"
90
+ end
91
+ end
92
+
93
+ def default_asset_root
94
+ if ENV["ASSET_HOST_URL"].to_s.empty?
95
+ File.join("https://assets-cdn.github.com", "/images/icons")
96
+ else
97
+ File.join(ENV["ASSET_HOST_URL"], "/images/icons")
98
+ end
99
+ end
100
+
101
+ def mention_markup(username)
102
+ mention_stash[username] ||= begin
103
+ "<a href='#{@transconfig["mention_base"]}/#{username}' " \
104
+ "class='user-mention'>@#{username}</a>"
105
+ end
106
+ end
107
+
108
+ def mention_renderer(node, text)
109
+ return text unless text.include?("@")
110
+ return text if has_ancestor?(node, IGNORE_MENTION_PARENTS)
111
+ text.gsub(MENTIONPATTERNS) { mention_markup(Regexp.last_match(1)) }
112
+ end
113
+
114
+ def emoji_renderer(node, text)
115
+ return text unless text.include?(":")
116
+ return text if has_ancestor?(node, IGNORE_EMOJI_PARENTS)
117
+ text.gsub(emoji_pattern) { emoji_markup(Regexp.last_match(1)) }
118
+ end
119
+
120
+ def emoji_url(name)
121
+ File.join(
122
+ @transconfig["emoji_source"], "emoji",
123
+ ::Emoji.find_by_alias(name).image_filename
124
+ )
125
+ end
126
+
127
+ def emoji_markup(name)
128
+ emoji_stash[name] ||= begin
129
+ attrs = []
130
+ img_attrs(name).each do |key, value|
131
+ attrs << "#{key}='#{value}'"
132
+ end
133
+ "<img #{attrs.join(" ")}>"
134
+ end
135
+ end
136
+
137
+ def img_attrs(name)
138
+ {
139
+ "class" => "emoji",
140
+ "title" => ":#{name}:",
141
+ "alt" => ":#{name}:",
142
+ "src" => emoji_url(name).to_s,
143
+ "height" => "20",
144
+ "width" => "20",
145
+ }
146
+ end
147
+
148
+ def mention_stash
149
+ @mention_stash ||= {}
150
+ end
151
+
152
+ def emoji_stash
153
+ @emoji_stash ||= {}
154
+ end
155
+
156
+ def emoji_names
157
+ ::Emoji.all.flat_map(&:aliases).sort!.map! { |name| Regexp.escape(name) }
158
+ end
159
+
160
+ def emoji_pattern
161
+ @emoji_pattern ||= %r!:(#{emoji_names.join('|')}):!
162
+ end
163
+
164
+ def prelim_check_regex
165
+ @prelim_check_regex ||= %r!#{MENTIONPATTERNS}|#{emoji_pattern}!
166
+ end
167
+
168
+ # rubocop:disable Naming/PredicateName
169
+ def has_ancestor?(node, tags)
170
+ while (node = node.respond_to?(:parent) && node.parent)
171
+ break true if tags.include?(node.name.downcase)
172
+ end
173
+ end
174
+ # rubocop:enable Naming/PredicateName
175
+ end
176
+ end
177
+ end
178
+
179
+ Jekyll::Hooks.register([:pages, :documents], :post_render) do |doc|
180
+ Jekyll::Mentioji.transform(doc) if Jekyll::Mentioji.transformable?(doc)
181
+ end
metadata ADDED
@@ -0,0 +1,146 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: jekyll-mentioji
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Ashwin Maroli
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-08-15 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: gemoji
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '3.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '3.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: jekyll
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '3.5'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '3.5'
41
+ - !ruby/object:Gem::Dependency
42
+ name: nokogiri
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.4'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.4'
55
+ - !ruby/object:Gem::Dependency
56
+ name: cucumber
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '3.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: jekyll-mentions
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '1.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '1.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: jemoji
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '0.9'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '0.9'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rubocop-jekyll
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '0.1'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '0.1'
111
+ description: Improve build times for sites using both 'jekyll-mentions and 'jemoji'
112
+ plugins
113
+ email:
114
+ - ashmaroli@gmail.com
115
+ executables: []
116
+ extensions: []
117
+ extra_rdoc_files: []
118
+ files:
119
+ - LICENSE
120
+ - README.md
121
+ - lib/jekyll-mentioji.rb
122
+ homepage: https://github.com/ashmaroli/jekyll-mentioji
123
+ licenses:
124
+ - MIT
125
+ metadata: {}
126
+ post_install_message:
127
+ rdoc_options: []
128
+ require_paths:
129
+ - lib
130
+ required_ruby_version: !ruby/object:Gem::Requirement
131
+ requirements:
132
+ - - ">="
133
+ - !ruby/object:Gem::Version
134
+ version: 2.3.0
135
+ required_rubygems_version: !ruby/object:Gem::Requirement
136
+ requirements:
137
+ - - ">="
138
+ - !ruby/object:Gem::Version
139
+ version: '0'
140
+ requirements: []
141
+ rubyforge_project:
142
+ rubygems_version: 2.6.14
143
+ signing_key:
144
+ specification_version: 4
145
+ summary: Faster version of the 'Jekyll::Mentions + Jekyll::Emoji' combo
146
+ test_files: []