jekyll-link-decorator 1.5.0 → 1.5.2

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: 3d02de351b09bc7030ee6411442c275cbda50416e773c3a0cb80f36538608692
4
- data.tar.gz: e68c0eaca4171b2d6a468802ed746008edb02efb5fe31bcc70012aa2ec702338
3
+ metadata.gz: 85919ec3882d487b1545ce0508fde30b74c0fd59e35f2823a8da4b4659437004
4
+ data.tar.gz: b558d2dbd36f937a5f03a670a519439d14f54625d948123977ed38ec67076af4
5
5
  SHA512:
6
- metadata.gz: 1f236c037405191c8fca37700b3fd439e45f7d84f22d5e4bb0bfeae615227004bd0894038088c153f0464fb0c500df8da4f4872ab7dbfede3268442256d95bc0
7
- data.tar.gz: a76901649038a3710b7e8781082397476a17bab5a17ff398ad4dc1f3f7a010a70936b719b6299f59ba44dfb80cec8d924c31852234bd4f79b6aacef925ec60d9
6
+ metadata.gz: 5a1011beabc1ebf913123b8aff0c38c747afd70c2ee572e16aa35c0d5cebd9ae9226d92ee47fda28dd7a6ac5a6ddd77956d29906faee86b62a1bfc08977cd98e
7
+ data.tar.gz: 6498a39d46641219f4939b9c3becf8b5693441c9b69ed0726d1c49cb7bfff0635df4817b13e1fe6c66befd6e5ca5bda64f500abdd681a443199bb28cbbe52408
data/README.md CHANGED
@@ -13,4 +13,4 @@ For complete documentation, configuration options, and examples, visit the
13
13
 
14
14
  ## License
15
15
 
16
- MIT License - see LICENSE file for details.
16
+ MIT License - see [LICENSE](LICENSE) file for details.
@@ -6,6 +6,6 @@ module Jekyll
6
6
  # Regenerated alongside the gemspec on every `make gemspec` run (locally
7
7
  # or in CI), so it always matches spec.version in the built gem. Any
8
8
  # hand edit is overwritten on the next run.
9
- VERSION = '1.5.0'
9
+ VERSION = '1.5.2'
10
10
  end
11
11
  end
@@ -1,67 +1,67 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # This Jekyll plugin, `link_decorator`, automatically enhances `<a>` tags
4
- # within Markdown content by adding specific Bootstrap-related CSS classes and
5
- # external-link icons. This functionality is conditional, applying different
6
- # sets of classes based on whether a link is located inside a Bootstrap alert
7
- # box or is a regular link. It explicitly avoids modifying elements that are
8
- # designated as buttons by having the `.btn` class.
3
+ # This Jekyll plugin, `link_decorator`, enhances `<a>` tags in Markdown content
4
+ # with Bootstrap CSS classes and external-link icons. It applies different
5
+ # classes depending on whether a link sits inside a Bootstrap alert box or
6
+ # stands alone, and skips elements with the `.btn` class.
9
7
  #
10
8
  # === External Link Icon Support ===
11
9
  #
12
10
  # For links pointing to external domains (different from site.url), the plugin
13
- # automatically adds a Font Awesome external-link icon (fa-solid fa-external-link)
14
- # at the end of the link text, similar to the link component behavior.
11
+ # adds a Font Awesome external-link icon (fa-solid fa-external-link) at the
12
+ # end of the link text.
15
13
  #
16
14
  # === External Link Detection & Target Management ===
17
15
  #
18
- # The plugin automatically detects external links and manages their behavior:
16
+ # The plugin detects external links and manages their behavior:
19
17
  # - Relative URLs (starting with /, ../) and same-domain absolute URLs open in the same window
20
- # - Absolute URLs pointing to different domains (http:// or https://) automatically get target="_blank"
21
- # - Links with target="_blank" automatically receive rel="noopener noreferrer" protection
22
- # against security vulnerabilities (window.opener access, referrer leakage)
18
+ # - Absolute URLs pointing to different domains (http:// or https://) get target="_blank"
19
+ # - Links with target="_blank" receive rel="noopener noreferrer" protection against
20
+ # security vulnerabilities (window.opener access, referrer leakage)
23
21
  #
24
22
  # === Configuration ===
25
23
  #
26
- # You can customize the classes applied by this plugin by adding
27
- # `with_link_decorator` and `with_link_decorator_data` sections to your `_config.yml` file.
24
+ # Customize the plugin's classes by adding `with_link_decorator` and
25
+ # `with_link_decorator_data` to your `_config.yml`.
28
26
  #
29
27
  # * `with_link_decorator`: Boolean (default: true) that controls whether the plugin runs.
30
28
  # Set to false to disable automatic link styling and icon insertion.
31
29
  #
32
30
  # * `with_link_decorator_data`: A dictionary containing optional configuration keys:
33
- # - `default_link_classes`: Defines the classes to be applied to all links that are not
34
- # inside an .alert. Example: `default_link_classes: "text-decoration-none"`
35
- # - `alert_link_classes`: Defines the classes for links specifically located within an
36
- # .alert box. Example: `alert_link_classes: "text-decoration-underline"`
37
- # - `external_link_icon`: Boolean (default: true) that controls whether external-link
38
- # icons are added to cross-domain external links. Set to false to disable.
31
+ # - `default_link_classes`: Classes applied to links outside an .alert.
32
+ # Example: `default_link_classes: "text-decoration-none"`
33
+ # If a link already carries its own `link-{color}`, `link-underline-*`,
34
+ # `link-offset-*`, `link-underline-opacity-*`, or `link-opacity-*` class,
35
+ # the plugin skips the matching default token automatically see LINK_CLASS_FAMILIES.
36
+ # - `alert_link_classes`: Classes applied to links inside an .alert box.
37
+ # Example: `alert_link_classes: "text-decoration-underline"`
38
+ # - `external_link_icon`: Boolean (default: true) — show the icon on cross-domain
39
+ # external links. Set to false to disable.
39
40
  # - `external_link_icon_excluded_tags`: List of HTML child tag names (default: []) whose presence
40
41
  # inside an anchor suppresses the external-link icon. Security attributes
41
- # (target="_blank", rel="noopener noreferrer") are still applied.
42
+ # (target="_blank", rel="noopener noreferrer") still apply.
42
43
  # Example: `external_link_icon_excluded_tags: ["img", "svg"]`
43
44
  #
44
45
  # === Heading Anchor Support ===
45
46
  #
46
- # The plugin can inject a Font Awesome link icon inside each heading (h1–h6),
47
- # giving users a direct, copyable URL to every section. This feature is configured
48
- # via two top-level keys (separate from `with_link_decorator_data`):
47
+ # The plugin can inject a permalink icon inside each heading (h1–h6) so every
48
+ # section gets a shareable URL. Configure it via two top-level keys (separate
49
+ # from `with_link_decorator_data`):
49
50
  #
50
- # * `with_heading_anchor`: Boolean (default: false) that controls whether heading anchors
51
- # are injected. Set to true to enable.
51
+ # * `with_heading_anchor`: Boolean (default: false) set to true to inject heading anchors.
52
52
  #
53
53
  # * `with_heading_anchor_data`: A dictionary containing optional configuration keys:
54
- # - `icon`: Font Awesome icon classes (default: "fa-solid fa-hashtag")
55
- # - `icon_size`: Font Awesome size modifier appended after icon classes (optional)
56
- # - `copy_success_message`: Feedback text after copying (consumed by heading-anchor.js,
57
- # default: "Copied!")
58
- # - `reset_delay`: Milliseconds before the icon resets (consumed by heading-anchor.js,
59
- # default: 2000)
54
+ # - `icon`: CSS classes applied to the anchor's glyph element (default:
55
+ # "heading-anchor-hash pe-1", which renders a plain "#" via the
56
+ # jekyll-theme-centos-base theme's CSS). Accepts any class list, including
57
+ # Font Awesome icon classes (e.g. "fa-solid fa-hashtag") for sites that
58
+ # want an icon-font glyph instead.
59
+ # - `icon_size`: Optional size modifier class appended after the icon classes.
60
60
  #
61
- # If no configuration is provided, the plugin will use default fallback classes
62
- # and enable external link icons.
61
+ # Without configuration, the plugin uses its default classes and enables
62
+ # external-link icons.
63
63
  #
64
- # Here is an example `_config.yml` setup:
64
+ # Example `_config.yml`:
65
65
  #
66
66
  # ```yaml
67
67
  # with_link_decorator: true
@@ -75,9 +75,7 @@
75
75
  #
76
76
  # with_heading_anchor: true
77
77
  # with_heading_anchor_data:
78
- # icon: "fa-solid fa-hashtag"
79
- # copy_success_message: "Copied!"
80
- # reset_delay: 2000
78
+ # icon: "heading-anchor-hash pe-1"
81
79
  # ```
82
80
 
83
81
  require 'nokogiri'
@@ -94,18 +92,51 @@ EXISTING_ICON_SELECTORS = [
94
92
 
95
93
  module Jekyll
96
94
  module Converters
97
- # This custom converter applies specific CSS classes to <a> tags
98
- # based on their context (e.g., inside an alert box) and excludes
99
- # buttons, all done via Nokogiri's powerful CSS selectors.
100
- # It also adds external-link icons for cross-domain external links.
95
+ # Applies CSS classes to <a> tags based on context (e.g., inside an alert
96
+ # box) via Nokogiri CSS selectors, excluding buttons. Also adds
97
+ # external-link icons for cross-domain links.
101
98
  class LinkDecorator < Jekyll::Converters::Markdown
102
99
  DEFAULT_LINK_CLASSES = 'link link-offset-3 link-offset-3-hover link-underline-primary ' \
103
100
  'link-underline-opacity-0 link-underline-opacity-100-hover'
104
101
  DEFAULT_ALERT_LINK_CLASSES = 'alert-link'
102
+
103
+ # Bootstrap 5 link-utility "families" whose defaults must not silently
104
+ # override an author's own explicit class from the same family — see
105
+ # classes_to_add_for.
106
+ #
107
+ # Each entry carries a single `pattern:` regex, anchored \A...\z and
108
+ # matched per class token (not against the whole class string), so a
109
+ # compound name like "social-link-danger" or "sidebar-link-offset-2" is
110
+ # never mistaken for an override.
111
+ #
112
+ # Enumerated families (offset, both opacity families) lock to Bootstrap's
113
+ # fixed value set, so an out-of-range value like "link-opacity-42" isn't
114
+ # recognized as an override either. Color families (link-{color},
115
+ # link-underline-{color}) stay generic (\S+) since Bootstrap themes can
116
+ # define arbitrary custom color names.
117
+ LINK_CLASS_FAMILIES = [
118
+ { # link-{color}, e.g. link-primary, link-body-emphasis (bare colored-link class;
119
+ # excludes link-underline-*, link-offset-*, and link-opacity-* families)
120
+ pattern: /\Alink-(?!underline\b|offset-|opacity-)\S+\z/
121
+ },
122
+ { # link-underline-{color}, e.g. link-underline-success (excludes -opacity-*)
123
+ pattern: /\Alink-underline-(?!opacity)\S+\z/
124
+ },
125
+ { # link-offset-{1,2,3}, each with an optional -hover variant
126
+ pattern: /\Alink-offset-[123](-hover)?\z/
127
+ },
128
+ { # link-underline-opacity-{0,10,25,50,75,100}, each with an optional -hover variant
129
+ pattern: /\Alink-underline-opacity-(?:0|10|25|50|75|100)(-hover)?\z/
130
+ },
131
+ { # link-opacity-{10,25,50,75,100}, each with an optional -hover variant
132
+ # (controls link-text opacity, distinct from link-underline-opacity-*)
133
+ pattern: /\Alink-opacity-(?:10|25|50|75|100)(-hover)?\z/
134
+ }
135
+ ].freeze
105
136
  DEFAULT_EXTERNAL_LINK_ICON = true
106
137
  DEFAULT_EXTERNAL_LINK_ICON_EXCLUDED_TAGS = [].freeze
107
138
  DEFAULT_HEADING_ANCHOR = false
108
- DEFAULT_HEADING_ANCHOR_ICON = 'fa-solid fa-hashtag'
139
+ DEFAULT_HEADING_ANCHOR_ICON = 'heading-anchor-hash pe-1'
109
140
 
110
141
  def self.name
111
142
  'LinkDecorator'
@@ -179,9 +210,8 @@ module Jekyll
179
210
  link['target'] = '_blank'
180
211
  end
181
212
 
182
- # Add security attributes to links with target="_blank"
183
- # Adds rel="noopener noreferrer" to prevent security vulnerabilities
184
- # Preserves any existing rel attribute values
213
+ # Add rel="noopener noreferrer" to target="_blank" links, preserving
214
+ # any existing rel attribute values
185
215
  def add_external_target_protection(link)
186
216
  target = link['target'].to_s
187
217
 
@@ -258,12 +288,20 @@ module Jekyll
258
288
  doc.css('a:not(.btn)').each do |link|
259
289
  next if link.ancestors('p.alert, blockquote.alert').any?
260
290
 
261
- classes_to_add = if link['class'].to_s.match?(/\blink-underline-(?!opacity)\S+/)
262
- default_classes.split.grep_v(/\Alink-underline-(?!opacity)/).join(' ')
263
- else
264
- default_classes
265
- end
266
- add_classes(link, classes_to_add)
291
+ add_classes(link, classes_to_add_for(link, default_classes))
292
+ end
293
+ end
294
+
295
+ # Drops any default class belonging to a family the link already customizes,
296
+ # so the author's explicit class wins instead of losing silently to a
297
+ # same-specificity Bootstrap !important rule declared later in the sheet.
298
+ # See LINK_CLASS_FAMILIES for how each family's pattern is matched.
299
+ def classes_to_add_for(link, default_classes)
300
+ link_classes = link['class'].to_s.split
301
+ LINK_CLASS_FAMILIES.reduce(default_classes) do |classes, family|
302
+ next classes unless link_classes.any? { |token| token.match?(family[:pattern]) }
303
+
304
+ classes.split.grep_v(family[:pattern]).join(' ')
267
305
  end
268
306
  end
269
307
 
@@ -349,7 +387,7 @@ module Jekyll
349
387
 
350
388
  private :add_heading_anchors, :resolve_heading_id, :slugify
351
389
 
352
- # Add external-link icon to cross-domain external links
390
+ # Add external-link icon to cross-domain links
353
391
  def add_external_icon(link, site_domain, config, doc)
354
392
  external_link_icon = config.fetch('external_link_icon', DEFAULT_EXTERNAL_LINK_ICON)
355
393
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jekyll-link-decorator
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.5.0
4
+ version: 1.5.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alain Reguera Delgado