jekyll-link-attributes 1.0.1 → 2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ed356ca2469b67757d91ce556bd251a74c77568a8e59df78566484ca905f23fc
4
- data.tar.gz: 3de4e708cf72fc5f7bf6e0b59cbf0fa36d316858e67fe760e5997aa49d4b58e6
3
+ metadata.gz: d791371caa0d64c3b9f0be8b0166c0dee989a8ba462e58c07ccef19625652fae
4
+ data.tar.gz: 56525b68f3c8ffb2568792b64b1e0e7c271d82cb742d77eaba772db3f7cd5112
5
5
  SHA512:
6
- metadata.gz: dbe5f18b8e6e205293abe1c6cdea0c793fa4badc5e7c362f848ae099c7d01d2999736f0540c9bec6e1f47d3e0c6f32f8bc52cd213f9998892a39565828723538
7
- data.tar.gz: a7318d90c5a161b75bbf56591b635f78c40902ac2c0dec5d1e62511bc982d29e7fa41fdddd023992da2e91e34c9452ad80eea82b346a162052b724de2681cd3c
6
+ metadata.gz: 0de1970490f37a7cce17f43303b234957aaf23f9f6fa10514d2e04da2a2c654d4d6b2aa4c9f0099ae1a555701da14f5724c72606c9f2ac610dd3b50a9dbf3bb7
7
+ data.tar.gz: 10bdda6fbaa8ef263b9a3648391fef15031ef507e36307632fceb011037f38c201b131dc718ce8dd6abd8154afec57cdfbd8febb4bd6290c37ea617d6227f050
data/.gitignore CHANGED
@@ -1,2 +1,2 @@
1
- .idea
1
+ .idea
2
2
  jekyll-link-attributes-*.gem
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 3.1.0
1
+ 3.1.0
data/Gemfile CHANGED
@@ -1,9 +1,9 @@
1
- # frozen_string_literal: true
2
-
3
- source 'https://rubygems.org'
4
-
5
- gemspec
6
-
7
- group :test do
8
- gem 'rspec'
9
- end
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ gemspec
6
+
7
+ group :test do
8
+ gem 'rspec'
9
+ end
data/Gemfile.lock CHANGED
@@ -1,98 +1,98 @@
1
- PATH
2
- remote: .
3
- specs:
4
- jekyll-link-attributes (0.0.3)
5
-
6
- GEM
7
- remote: https://rubygems.org/
8
- specs:
9
- addressable (2.8.1)
10
- public_suffix (>= 2.0.2, < 6.0)
11
- colorator (1.1.0)
12
- concurrent-ruby (1.1.10)
13
- diff-lcs (1.5.0)
14
- em-websocket (0.5.3)
15
- eventmachine (>= 0.12.9)
16
- http_parser.rb (~> 0)
17
- eventmachine (1.2.7)
18
- ffi (1.15.5)
19
- forwardable-extended (2.6.0)
20
- http_parser.rb (0.8.0)
21
- i18n (1.12.0)
22
- concurrent-ruby (~> 1.0)
23
- jekyll (4.2.2)
24
- addressable (~> 2.4)
25
- colorator (~> 1.0)
26
- em-websocket (~> 0.5)
27
- i18n (~> 1.0)
28
- jekyll-sass-converter (~> 2.0)
29
- jekyll-watch (~> 2.0)
30
- kramdown (~> 2.3)
31
- kramdown-parser-gfm (~> 1.0)
32
- liquid (~> 4.0)
33
- mercenary (~> 0.4.0)
34
- pathutil (~> 0.9)
35
- rouge (~> 3.0)
36
- safe_yaml (~> 1.0)
37
- terminal-table (~> 2.0)
38
- jekyll-sass-converter (2.2.0)
39
- sassc (> 2.0.1, < 3.0)
40
- jekyll-watch (2.2.1)
41
- listen (~> 3.0)
42
- kramdown (2.4.0)
43
- rexml
44
- kramdown-parser-gfm (1.1.0)
45
- kramdown (~> 2.0)
46
- liquid (4.0.3)
47
- listen (3.7.1)
48
- rb-fsevent (~> 0.10, >= 0.10.3)
49
- rb-inotify (~> 0.9, >= 0.9.10)
50
- mercenary (0.4.0)
51
- nokogiri (1.13.8-x86_64-darwin)
52
- racc (~> 1.4)
53
- nokogiri (1.13.8-x86_64-linux)
54
- racc (~> 1.4)
55
- pathutil (0.16.2)
56
- forwardable-extended (~> 2.6)
57
- public_suffix (5.0.0)
58
- racc (1.6.0)
59
- rake (10.5.0)
60
- rb-fsevent (0.11.2)
61
- rb-inotify (0.10.1)
62
- ffi (~> 1.0)
63
- rexml (3.2.5)
64
- rouge (3.30.0)
65
- rspec (3.11.0)
66
- rspec-core (~> 3.11.0)
67
- rspec-expectations (~> 3.11.0)
68
- rspec-mocks (~> 3.11.0)
69
- rspec-core (3.11.0)
70
- rspec-support (~> 3.11.0)
71
- rspec-expectations (3.11.0)
72
- diff-lcs (>= 1.2.0, < 2.0)
73
- rspec-support (~> 3.11.0)
74
- rspec-mocks (3.11.1)
75
- diff-lcs (>= 1.2.0, < 2.0)
76
- rspec-support (~> 3.11.0)
77
- rspec-support (3.11.0)
78
- safe_yaml (1.0.5)
79
- sassc (2.4.0)
80
- ffi (~> 1.9)
81
- terminal-table (2.0.0)
82
- unicode-display_width (~> 1.1, >= 1.1.1)
83
- unicode-display_width (1.8.0)
84
-
85
- PLATFORMS
86
- x86_64-darwin-20
87
- x86_64-linux
88
-
89
- DEPENDENCIES
90
- bundler (>= 2.0.0)
91
- jekyll (>= 4.0.0)
92
- jekyll-link-attributes!
93
- nokogiri (>= 1.0.0)
94
- rake (>= 10.0.0)
95
- rspec
96
-
97
- BUNDLED WITH
98
- 2.3.21
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ jekyll-link-attributes (2.0.1)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ addressable (2.8.1)
10
+ public_suffix (>= 2.0.2, < 6.0)
11
+ colorator (1.1.0)
12
+ concurrent-ruby (1.1.10)
13
+ diff-lcs (1.5.0)
14
+ em-websocket (0.5.3)
15
+ eventmachine (>= 0.12.9)
16
+ http_parser.rb (~> 0)
17
+ eventmachine (1.2.7)
18
+ ffi (1.15.5)
19
+ forwardable-extended (2.6.0)
20
+ http_parser.rb (0.8.0)
21
+ i18n (1.12.0)
22
+ concurrent-ruby (~> 1.0)
23
+ jekyll (4.2.2)
24
+ addressable (~> 2.4)
25
+ colorator (~> 1.0)
26
+ em-websocket (~> 0.5)
27
+ i18n (~> 1.0)
28
+ jekyll-sass-converter (~> 2.0)
29
+ jekyll-watch (~> 2.0)
30
+ kramdown (~> 2.3)
31
+ kramdown-parser-gfm (~> 1.0)
32
+ liquid (~> 4.0)
33
+ mercenary (~> 0.4.0)
34
+ pathutil (~> 0.9)
35
+ rouge (~> 3.0)
36
+ safe_yaml (~> 1.0)
37
+ terminal-table (~> 2.0)
38
+ jekyll-sass-converter (2.2.0)
39
+ sassc (> 2.0.1, < 3.0)
40
+ jekyll-watch (2.2.1)
41
+ listen (~> 3.0)
42
+ kramdown (2.4.0)
43
+ rexml
44
+ kramdown-parser-gfm (1.1.0)
45
+ kramdown (~> 2.0)
46
+ liquid (4.0.3)
47
+ listen (3.7.1)
48
+ rb-fsevent (~> 0.10, >= 0.10.3)
49
+ rb-inotify (~> 0.9, >= 0.9.10)
50
+ mercenary (0.4.0)
51
+ nokogiri (1.13.8-x86_64-darwin)
52
+ racc (~> 1.4)
53
+ nokogiri (1.13.8-x86_64-linux)
54
+ racc (~> 1.4)
55
+ pathutil (0.16.2)
56
+ forwardable-extended (~> 2.6)
57
+ public_suffix (5.0.0)
58
+ racc (1.6.0)
59
+ rake (10.5.0)
60
+ rb-fsevent (0.11.2)
61
+ rb-inotify (0.10.1)
62
+ ffi (~> 1.0)
63
+ rexml (3.2.5)
64
+ rouge (3.30.0)
65
+ rspec (3.11.0)
66
+ rspec-core (~> 3.11.0)
67
+ rspec-expectations (~> 3.11.0)
68
+ rspec-mocks (~> 3.11.0)
69
+ rspec-core (3.11.0)
70
+ rspec-support (~> 3.11.0)
71
+ rspec-expectations (3.11.0)
72
+ diff-lcs (>= 1.2.0, < 2.0)
73
+ rspec-support (~> 3.11.0)
74
+ rspec-mocks (3.11.1)
75
+ diff-lcs (>= 1.2.0, < 2.0)
76
+ rspec-support (~> 3.11.0)
77
+ rspec-support (3.11.0)
78
+ safe_yaml (1.0.5)
79
+ sassc (2.4.0)
80
+ ffi (~> 1.9)
81
+ terminal-table (2.0.0)
82
+ unicode-display_width (~> 1.1, >= 1.1.1)
83
+ unicode-display_width (1.8.0)
84
+
85
+ PLATFORMS
86
+ x86_64-darwin-20
87
+ x86_64-linux
88
+
89
+ DEPENDENCIES
90
+ bundler (>= 2.0.0)
91
+ jekyll (>= 4.0.0)
92
+ jekyll-link-attributes!
93
+ nokogiri (>= 1.0.0)
94
+ rake (>= 10.0.0)
95
+ rspec
96
+
97
+ BUNDLED WITH
98
+ 2.3.21
data/LICENSE CHANGED
@@ -1,25 +1,25 @@
1
- Copyright 2022 Twin Sun, LLC
2
-
3
- Redistribution and use in source and binary forms, with or without modification,
4
- are permitted provided that the following conditions are met:
5
-
6
- * Redistributions of source code must retain the above copyright
7
- notice, this list of conditions and the following disclaimer.
8
- * Redistributions in binary form must reproduce the above
9
- copyright notice, this list of conditions and the following
10
- disclaimer in the documentation and/or other materials provided
11
- with the distribution.
12
- * Neither the name of Twin Sun, LLC nor the names of its
13
- contributors may be used to endorse or promote products derived
14
- from this software without specific prior written permission.
15
-
16
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17
- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19
- DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
20
- ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21
- (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22
- LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
23
- ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24
- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
1
+ Copyright 2022 Twin Sun, LLC
2
+
3
+ Redistribution and use in source and binary forms, with or without modification,
4
+ are permitted provided that the following conditions are met:
5
+
6
+ * Redistributions of source code must retain the above copyright
7
+ notice, this list of conditions and the following disclaimer.
8
+ * Redistributions in binary form must reproduce the above
9
+ copyright notice, this list of conditions and the following
10
+ disclaimer in the documentation and/or other materials provided
11
+ with the distribution.
12
+ * Neither the name of Twin Sun, LLC nor the names of its
13
+ contributors may be used to endorse or promote products derived
14
+ from this software without specific prior written permission.
15
+
16
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
20
+ ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
23
+ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25
25
  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
data/README.md CHANGED
@@ -1,64 +1,102 @@
1
- # Jekyll Link Attributes
2
-
3
- This plugin adds `rel` and `target` attributes to all external links in your Jekyll site.
4
- The default configuration opens external links in a new tab and conserves domain authority for your site.
5
-
6
- ## Setup
7
-
8
- 1. Add the gem to your `Gemfile`:
9
- ```ruby
10
- gem 'jekyll-link-attributes'
11
- ```
12
- 2. Run `bundle install` to install the gem
13
- 3. Add the following to your `_config.yml`:
14
- ```yaml
15
- plugins:
16
- - jekyll-link-attributes
17
- ```
18
-
19
- ## Usage
20
-
21
- ### Configuration
22
-
23
- You can override the default configuration by adding the following section to your Jekyll site's `_config.yml`:
24
-
25
- ```yaml
26
- external_links:
27
- enabled: true
28
- rel: external nofollow noopener
29
- target: _blank
30
- exclude:
31
- - https://example.com
32
- - https://another.example.com/test.html
33
- - https://regex.example.com/.+
34
- ```
35
-
36
- #### Default Configuration
37
- | Key | Default Value | Description |
38
- | ---------------------------- | ---------------------------- | -------------------------------------------------- |
39
- | `external_links.enabled` | `true` | Enable attribute modifications for external links. |
40
- | `external_links.rel` | `external nofollow noopener` | The `rel` attribute to add to external links. |
41
- | `external_links.target` | `_blank` | The `target` attribute to add to external links. |
42
- | `external_links.exclude` | `[]` | A list of URLs to exclude from processing. |
43
-
44
-
45
- ### Skipping individual links
46
-
47
- The `rel` or `target` attributes will not be modified for links that already have those existing attributes.
48
- This allows you to skip individual links without having to modify the plugin's configuration.
49
-
50
- ```html
51
- <a href"https://example.com" rel="nofollow">Example</a> <!-- `rel` will not be modified, but the configured `target` will be added. -->
52
- <a href"https://example.com" target="_self">Example</a> <!-- `target` will not be modified, but the configured `rel` will be added. -->
53
- <a href"https://example.com" rel="nofollow" target="_self">Example</a> <!-- Neither `rel` nor `target` will be modified. -->
54
- ```
55
-
56
- ## Contributing
57
-
58
- Pull requests are welcome!
59
- If you wish to change existing behavior, please open an issue to discuss the change before investing time in a PR.
60
- RSpec tests are encouraged for any new features.
61
-
62
- ## Supported by Twin Sun
63
-
64
- This project is maintained by [Twin Sun](https://twinsunsolutions.com/), a custom mobile and web app development agency in Nashville, TN.
1
+ # Jekyll Link Attributes
2
+
3
+ A Jekyll plugin for managing external link behavior: `rel` attributes, `target` attributes, and UTM tracking parameters.
4
+ Each concern is independently configurable with its own value and exclude list.
5
+
6
+ ## Setup
7
+
8
+ 1. Add the gem to your `Gemfile`:
9
+ ```ruby
10
+ gem 'jekyll-link-attributes'
11
+ ```
12
+ 2. Run `bundle install` to install the gem
13
+ 3. Add the following to your `_config.yml`:
14
+ ```yaml
15
+ plugins:
16
+ - jekyll-link-attributes
17
+ ```
18
+
19
+ ## Configuration
20
+
21
+ ### Recommended (v2+)
22
+
23
+ Each attribute type is its own section with a `value` and optional `exclude` list:
24
+
25
+ ```yaml
26
+ external_links:
27
+ enabled: true
28
+
29
+ rel:
30
+ value: external nofollow noopener
31
+ exclude:
32
+ - https://myotherapp.com(/?|/.*)?
33
+
34
+ target:
35
+ value: _blank
36
+ exclude:
37
+ - https://myotherapp.com(/?|/.*)?
38
+
39
+ utm:
40
+ enabled: true
41
+ source: mysite.com
42
+ medium: website
43
+ exclude:
44
+ - https://github.com(/?|/.*)?
45
+ ```
46
+
47
+ ### Legacy (v1, still supported)
48
+
49
+ The original flat configuration style continues to work. The top-level `rel`, `target`, and `exclude` keys are used as fallbacks when the new-style section config is not present:
50
+
51
+ ```yaml
52
+ external_links:
53
+ enabled: true
54
+ rel: external nofollow noopener
55
+ target: _blank
56
+ exclude:
57
+ - https://example.com(/?|/.*)?
58
+ ```
59
+
60
+ ### Resolution order
61
+
62
+ | Setting | Resolved from | Fallback |
63
+ | ------- | ------------- | -------- |
64
+ | rel value | `external_links.rel.value` | `external_links.rel` (string) or `external nofollow noopener` |
65
+ | rel excludes | `external_links.rel.exclude` | `external_links.exclude` |
66
+ | target value | `external_links.target.value` | `external_links.target` (string) or `_blank` |
67
+ | target excludes | `external_links.target.exclude` | `external_links.exclude` |
68
+ | utm excludes | `external_links.utm.exclude` | *(none, defaults to empty)* |
69
+
70
+ ### UTM tracking parameters
71
+
72
+ When `external_links.utm.enabled` is `true`, UTM query parameters are automatically appended to external links:
73
+
74
+ | Param | Value | Source |
75
+ | -------------- | -------------------- | ---------------------------------------------------------------- |
76
+ | `utm_source` | Configured `source` | Falls back to the site `url` with the protocol stripped. |
77
+ | `utm_medium` | Configured `medium` | Falls back to `website`. |
78
+ | `utm_campaign` | Auto-derived | `blog` for post/blog layouts, otherwise the first URL path segment (e.g., `about`), or `homepage` for the root page. |
79
+ | `utm_content` | Auto-derived | The page slug (e.g., `my-great-post` or `index`). |
80
+
81
+ Existing query parameters on links are preserved. UTM parameters already present on a link will not be overwritten.
82
+
83
+ ### Skipping individual links
84
+
85
+ The `rel` or `target` attributes will not be modified for links that already have those existing attributes.
86
+ This allows you to skip individual links without having to modify the plugin's configuration.
87
+
88
+ ```html
89
+ <a href="https://example.com" rel="nofollow">Example</a> <!-- rel will not be modified, but target will be added. -->
90
+ <a href="https://example.com" target="_self">Example</a> <!-- target will not be modified, but rel will be added. -->
91
+ <a href="https://example.com" rel="nofollow" target="_self">Example</a> <!-- Neither rel nor target will be modified. -->
92
+ ```
93
+
94
+ ## Contributing
95
+
96
+ Pull requests are welcome!
97
+ If you wish to change existing behavior, please open an issue to discuss the change before investing time in a PR.
98
+ RSpec tests are encouraged for any new features.
99
+
100
+ ## Supported by Twin Sun
101
+
102
+ This project is maintained by [Twin Sun](https://twinsunsolutions.com/), a custom mobile and web app development agency in Nashville, TN.
data/Rakefile CHANGED
@@ -1,3 +1,3 @@
1
- # frozen_string_literal: true
2
-
3
- require 'bundler/gem_tasks'
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
@@ -1,33 +1,33 @@
1
- lib = File.expand_path('../lib', __FILE__)
2
- $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
- require 'jekyll-link-attributes/version'
4
-
5
- Gem::Specification.new do |spec|
6
- spec.name = 'jekyll-link-attributes'
7
- spec.version = Jekyll::LinkAttributes::VERSION
8
- spec.authors = ['twinsunllc']
9
- spec.email = ['contact@twinsunsolutions.com']
10
-
11
- spec.summary = 'This plugin adds `rel` and `target` attributes to all external links in your Jekyll site.'
12
- spec.description = spec.summary
13
- spec.homepage = 'https://github.com/twinsunllc/jekyll-link-attributes'
14
- spec.license = 'BSD 3-Clause'
15
-
16
- # Prevent pushing this gem to RubyGems.org by setting 'allowed_push_host', or
17
- # delete this section to allow pushing this gem to any host.
18
- if spec.respond_to?(:metadata)
19
- spec.metadata['allowed_push_host'] = 'https://rubygems.org'
20
- else
21
- raise 'RubyGems 2.0 or newer is required to protect against public gem pushes.'
22
- end
23
-
24
- spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
25
- spec.bindir = 'exe'
26
- spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
27
- spec.require_paths = ['lib']
28
-
29
- spec.add_development_dependency 'bundler', '>= 2.0.0'
30
- spec.add_development_dependency 'jekyll', '>= 4.0.0'
31
- spec.add_development_dependency 'nokogiri', '>= 1.0.0'
32
- spec.add_development_dependency 'rake', '>= 10.0.0'
33
- end
1
+ lib = File.expand_path('../lib', __FILE__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require 'jekyll-link-attributes/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'jekyll-link-attributes'
7
+ spec.version = Jekyll::LinkAttributes::VERSION
8
+ spec.authors = ['twinsunllc']
9
+ spec.email = ['contact@twinsunsolutions.com']
10
+
11
+ spec.summary = 'This plugin adds `rel` and `target` attributes to all external links in your Jekyll site.'
12
+ spec.description = spec.summary
13
+ spec.homepage = 'https://github.com/twinsunllc/jekyll-link-attributes'
14
+ spec.license = 'BSD 3-Clause'
15
+
16
+ # Prevent pushing this gem to RubyGems.org by setting 'allowed_push_host', or
17
+ # delete this section to allow pushing this gem to any host.
18
+ if spec.respond_to?(:metadata)
19
+ spec.metadata['allowed_push_host'] = 'https://rubygems.org'
20
+ else
21
+ raise 'RubyGems 2.0 or newer is required to protect against public gem pushes.'
22
+ end
23
+
24
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
25
+ spec.bindir = 'exe'
26
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
27
+ spec.require_paths = ['lib']
28
+
29
+ spec.add_development_dependency 'bundler', '>= 2.0.0'
30
+ spec.add_development_dependency 'jekyll', '>= 4.0.0'
31
+ spec.add_development_dependency 'nokogiri', '>= 1.0.0'
32
+ spec.add_development_dependency 'rake', '>= 10.0.0'
33
+ end
@@ -1,18 +1,18 @@
1
- # frozen_string_literal: true
2
-
3
- require 'jekyll/hooks'
4
- require 'jekyll-link-attributes'
5
-
6
- Jekyll::Hooks.register :documents, :post_render do |document|
7
- Jekyll::LinkAttributes.post_render_html(document)
8
- end
9
-
10
- Jekyll::Hooks.register :pages, :post_render do |page|
11
- next unless page.output_ext.eql?('.html')
12
-
13
- Jekyll::LinkAttributes.post_render_html(page)
14
- end
15
-
16
- Jekyll::Hooks.register :posts, :post_render do |post|
17
- Jekyll::LinkAttributes.post_render_html(post)
18
- end
1
+ # frozen_string_literal: true
2
+
3
+ require 'jekyll/hooks'
4
+ require 'jekyll-link-attributes'
5
+
6
+ Jekyll::Hooks.register :documents, :post_render do |document|
7
+ Jekyll::LinkAttributes.post_render_html(document)
8
+ end
9
+
10
+ Jekyll::Hooks.register :pages, :post_render do |page|
11
+ next unless page.output_ext.eql?('.html')
12
+
13
+ Jekyll::LinkAttributes.post_render_html(page)
14
+ end
15
+
16
+ Jekyll::Hooks.register :posts, :post_render do |post|
17
+ Jekyll::LinkAttributes.post_render_html(post)
18
+ end
@@ -1,7 +1,7 @@
1
- # frozen_string_literal: true
2
-
3
- module Jekyll
4
- class LinkAttributes
5
- VERSION = '1.0.1'
6
- end
7
- end
1
+ # frozen_string_literal: true
2
+
3
+ module Jekyll
4
+ class LinkAttributes
5
+ VERSION = '2.0.1'
6
+ end
7
+ end
@@ -1,56 +1,134 @@
1
- # frozen_string_literal: true
2
-
3
- require 'jekyll-link-attributes/hooks'
4
- require 'jekyll-link-attributes/version'
5
- require 'nokogiri'
6
-
7
- module Jekyll
8
-
9
- # Adjusts external links in HTML documents.
10
- class LinkAttributes
11
-
12
- # Perform post_render processing on the specified document/page/post
13
- # @param [Object] article a Jekyll document, page, or post
14
- def self.post_render_html(article)
15
- config = article.site.config
16
- return unless external_links_enabled?(config: config)
17
-
18
- output = Nokogiri::HTML(article.output)
19
- output.css('a').each do |a|
20
- next unless external_link?(config: config, url: a['href'])
21
- next if excludes_external_link?(config: config, url: a['href'])
22
-
23
- # only set rel and target if they're not already set
24
- a['rel'] = external_link_rel(config: config) unless a['rel']
25
- a['target'] = external_link_target(config: config) unless a['target']
26
- end
27
-
28
- article.output = output.to_s
29
- end
30
-
31
- private
32
-
33
- def self.excludes_external_link?(config:, url:)
34
- excludes = (config.dig('external_links', 'exclude') || [])
35
- excludes.any? { |exclude| Regexp.new("^#{exclude}$").match? url }
36
- end
37
-
38
- def self.external_link?(config:, url:)
39
- site_url = config['url']
40
- !(url =~ %r{^https?://}).nil? && (site_url.nil? || !url.start_with?(site_url))
41
- end
42
-
43
- def self.external_links_enabled?(config:)
44
- enabled = config.dig('external_links', 'enabled')
45
- enabled.nil? || enabled
46
- end
47
-
48
- def self.external_link_rel(config:)
49
- config.dig('external_links', 'rel') || 'external nofollow noopener'
50
- end
51
-
52
- def self.external_link_target(config:)
53
- config.dig('external_links', 'target') || '_blank'
54
- end
55
- end
56
- end
1
+ # frozen_string_literal: true
2
+
3
+ require 'jekyll-link-attributes/hooks'
4
+ require 'jekyll-link-attributes/version'
5
+ require 'nokogiri'
6
+ require 'uri'
7
+
8
+ module Jekyll
9
+
10
+ # Adjusts external links in HTML documents.
11
+ class LinkAttributes
12
+
13
+ # Perform post_render processing on the specified document/page/post
14
+ # @param [Object] article a Jekyll document, page, or post
15
+ def self.post_render_html(article)
16
+ config = article.site.config
17
+ return unless external_links_enabled?(config: config)
18
+
19
+ ext_config = config['external_links'] || {}
20
+ utm_params = build_utm_params(ext_config: ext_config, site_config: config, article: article)
21
+
22
+ output = Nokogiri::HTML(article.output)
23
+ output.css('a').each do |a|
24
+ next unless external_link?(config: config, url: a['href'])
25
+
26
+ original_href = a['href']
27
+
28
+ # UTM: applied to all external links with its own exclude list
29
+ if utm_params && !excluded?(ext_config: ext_config, section: 'utm', url: original_href)
30
+ a['href'] = append_utm_params(url: a['href'], utm_params: utm_params)
31
+ end
32
+
33
+ # rel: new-style section config falls back to legacy top-level keys
34
+ unless a['rel']
35
+ rel_value = resolve_value(ext_config: ext_config, section: 'rel', legacy_key: 'rel',
36
+ default: 'external nofollow noopener')
37
+ unless excluded?(ext_config: ext_config, section: 'rel', url: original_href)
38
+ a['rel'] = rel_value
39
+ end
40
+ end
41
+
42
+ # target: new-style section config falls back to legacy top-level keys
43
+ unless a['target']
44
+ target_value = resolve_value(ext_config: ext_config, section: 'target', legacy_key: 'target',
45
+ default: '_blank')
46
+ unless excluded?(ext_config: ext_config, section: 'target', url: original_href)
47
+ a['target'] = target_value
48
+ end
49
+ end
50
+ end
51
+
52
+ article.output = output.to_s
53
+ end
54
+
55
+ private
56
+
57
+ # Resolve value for a section, falling back to legacy top-level key.
58
+ # New style: external_links.rel.value / external_links.target.value
59
+ # Legacy: external_links.rel / external_links.target (string value)
60
+ def self.resolve_value(ext_config:, section:, legacy_key:, default:)
61
+ section_config = ext_config[section]
62
+ if section_config.is_a?(Hash)
63
+ section_config['value'] || default
64
+ else
65
+ section_config || default
66
+ end
67
+ end
68
+
69
+ # Check if a URL is excluded for a given section.
70
+ # New style: external_links.<section>.exclude
71
+ # Legacy fallback (rel/target only): external_links.exclude
72
+ def self.excluded?(ext_config:, section:, url:)
73
+ section_config = ext_config[section]
74
+ excludes = if section_config.is_a?(Hash)
75
+ section_config['exclude'] || []
76
+ elsif section == 'utm'
77
+ []
78
+ else
79
+ ext_config['exclude'] || []
80
+ end
81
+
82
+ excludes.any? { |pattern| Regexp.new("^#{pattern}$").match?(url) }
83
+ end
84
+
85
+ def self.external_link?(config:, url:)
86
+ site_url = config['url']
87
+ !(url =~ %r{^https?://}).nil? && (site_url.nil? || !url.start_with?(site_url))
88
+ end
89
+
90
+ def self.external_links_enabled?(config:)
91
+ enabled = config.dig('external_links', 'enabled')
92
+ enabled.nil? || enabled
93
+ end
94
+
95
+ def self.utm_enabled?(ext_config:)
96
+ ext_config.dig('utm', 'enabled') == true
97
+ end
98
+
99
+ def self.build_utm_params(ext_config:, site_config:, article:)
100
+ return nil unless utm_enabled?(ext_config: ext_config)
101
+
102
+ utm_config = ext_config['utm'] || {}
103
+ source = utm_config['source'] || site_config['url']&.sub(%r{\Ahttps?://}, '') || 'website'
104
+ medium = utm_config['medium'] || 'website'
105
+
106
+ campaign = case article.data['layout']
107
+ when 'post', 'blog' then 'blog'
108
+ else
109
+ path = article.url.to_s.gsub(%r{\A/|/\z}, '')
110
+ path.empty? ? 'homepage' : path.split('/').first
111
+ end
112
+
113
+ content = article.data['slug'] || File.basename(article.url.to_s.chomp('/'))
114
+ content = 'index' if content.empty?
115
+
116
+ {
117
+ 'utm_source' => source,
118
+ 'utm_medium' => medium,
119
+ 'utm_campaign' => campaign,
120
+ 'utm_content' => content,
121
+ }
122
+ end
123
+
124
+ def self.append_utm_params(url:, utm_params:)
125
+ uri = URI.parse(url)
126
+ existing = URI.decode_www_form(uri.query || '').to_h
127
+ utm_params.each { |k, v| existing[k] = v unless existing.key?(k) }
128
+ uri.query = URI.encode_www_form(existing)
129
+ uri.to_s
130
+ rescue URI::InvalidURIError
131
+ url
132
+ end
133
+ end
134
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jekyll-link-attributes
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 2.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - twinsunllc
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-09-14 00:00:00.000000000 Z
11
+ date: 2026-03-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -90,7 +90,7 @@ licenses:
90
90
  - BSD 3-Clause
91
91
  metadata:
92
92
  allowed_push_host: https://rubygems.org
93
- post_install_message:
93
+ post_install_message:
94
94
  rdoc_options: []
95
95
  require_paths:
96
96
  - lib
@@ -106,7 +106,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
106
106
  version: '0'
107
107
  requirements: []
108
108
  rubygems_version: 3.3.3
109
- signing_key:
109
+ signing_key:
110
110
  specification_version: 4
111
111
  summary: This plugin adds `rel` and `target` attributes to all external links in your
112
112
  Jekyll site.