jekyll-link-decorator 1.3.1 → 1.4.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.
- checksums.yaml +4 -4
- data/README.md +5 -4
- data/lib/jekyll-link-decorator/instrumentation.rb +218 -0
- data/lib/jekyll-link-decorator/version.rb +11 -0
- data/lib/jekyll-link-decorator.rb +7 -3
- metadata +19 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7edfd6ee252b912cb6d3c30f50fcf6c0da0f0e5dc49694f3d2990ac6511e4584
|
|
4
|
+
data.tar.gz: 8cf8478f0a431f2b436356d5cd905586629bc3eea130cd777516435f9260284c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8ece38e9ce327d4c4868ff3d1273cee59abcf0b02f6e7171b4bae061c175eaec359e6a359549f2c9f534a73356f8024a4cf1141ff9b8bb7a38e5a179b55b8994
|
|
7
|
+
data.tar.gz: 3327e7a86426bc7c43931f579fa9924b30970e7a8f6ee38c2a5dbf7d2a3345e388f157e34a89fd069945d2a41513514db82bf02e7a9f8e6032ea85f44530525c
|
data/README.md
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
# jekyll-link-decorator
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
classes, security attributes, Font Awesome icons, and
|
|
5
|
-
|
|
6
|
-
link protection, and icon injection without manual
|
|
3
|
+
`jekyll-link-decorator` is a Jekyll plugin that automatically enhances Markdown
|
|
4
|
+
links with Bootstrap CSS classes, security attributes, Font Awesome icons, and
|
|
5
|
+
context-aware styling. It simplifies link management across Jekyll sites by
|
|
6
|
+
handling styling, external link protection, and icon injection without manual
|
|
7
|
+
intervention.
|
|
7
8
|
|
|
8
9
|
## Documentation
|
|
9
10
|
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Jekyll
|
|
4
|
+
module LinkDecorator
|
|
5
|
+
# OpenTelemetry instrumentation facade for jekyll-link-decorator.
|
|
6
|
+
#
|
|
7
|
+
# All tracing is configured centrally in TRACED_METHODS — no span code lives
|
|
8
|
+
# in business logic classes. To add a span: append one entry. To rename a
|
|
9
|
+
# method: update the one entry. When a method is removed from its class the
|
|
10
|
+
# stale entry raises NoMethodError in tests, signalling the entry to delete.
|
|
11
|
+
#
|
|
12
|
+
# Requires opentelemetry-api at runtime; falls back to a no-op if absent.
|
|
13
|
+
# Users opt in to real tracing by adding opentelemetry-sdk and
|
|
14
|
+
# opentelemetry-exporter-otlp to their site Gemfile and exporting:
|
|
15
|
+
#
|
|
16
|
+
# OTEL_SERVICE_NAME=jekyll-link-decorator
|
|
17
|
+
# OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318
|
|
18
|
+
module Instrumentation
|
|
19
|
+
TRACER_NAME = 'jekyll-link-decorator'
|
|
20
|
+
|
|
21
|
+
# Central instrumentation registry.
|
|
22
|
+
#
|
|
23
|
+
# Each row: [class_name, method_type, method_name, span_name, attribute_proc]
|
|
24
|
+
#
|
|
25
|
+
# method_type:
|
|
26
|
+
# :instance — public instance method
|
|
27
|
+
# :private — private instance method (visibility is preserved on the wrapper)
|
|
28
|
+
# :class — class / module method (prepended on the singleton class)
|
|
29
|
+
#
|
|
30
|
+
# attribute_proc: ->(span, this, args, result) or nil
|
|
31
|
+
# span — OTel span (or NoopSpan); call span.set_attribute after super returns
|
|
32
|
+
# this — receiver object (instance or nil for class methods)
|
|
33
|
+
# args — positional args array as passed to the method
|
|
34
|
+
# result — return value of the original method
|
|
35
|
+
# :nocov: — attribute procs are configuration data; coverage comes from integration tests
|
|
36
|
+
TRACED_METHODS = [
|
|
37
|
+
# decorate_html is the converter's entry point — one span per page converted
|
|
38
|
+
['Jekyll::Converters::LinkDecorator', :instance, :decorate_html, 'link.decorate_page',
|
|
39
|
+
lambda { |span, _this, args, result|
|
|
40
|
+
span.set_attribute('link.html_size_bytes', args[0].to_s.bytesize)
|
|
41
|
+
span.set_attribute('link.output_size_bytes', result.to_s.bytesize)
|
|
42
|
+
}],
|
|
43
|
+
|
|
44
|
+
['Jekyll::Converters::LinkDecorator', :instance, :apply_link_styles, 'link.style_apply',
|
|
45
|
+
lambda { |span, _this, args, _result|
|
|
46
|
+
doc = args[0]
|
|
47
|
+
span.set_attribute('link.styled_link_count', doc.respond_to?(:css) ? doc.css('a:not(.btn)').size : 0)
|
|
48
|
+
}],
|
|
49
|
+
|
|
50
|
+
['Jekyll::Converters::LinkDecorator', :instance, :add_external_link_features,
|
|
51
|
+
'link.external_link_features',
|
|
52
|
+
lambda { |span, _this, args, _result|
|
|
53
|
+
doc = args[0]
|
|
54
|
+
span.set_attribute('link.external_link_count',
|
|
55
|
+
doc.respond_to?(:css) ? doc.css('a[target="_blank"]').size : 0)
|
|
56
|
+
}],
|
|
57
|
+
|
|
58
|
+
# add_heading_anchors is private on LinkDecorator; visibility is preserved on the wrapper
|
|
59
|
+
['Jekyll::Converters::LinkDecorator', :private, :add_heading_anchors, 'link.heading_anchors', nil]
|
|
60
|
+
].freeze
|
|
61
|
+
# :nocov:
|
|
62
|
+
|
|
63
|
+
# Returns the active OTel tracer, or a no-op tracer if opentelemetry-api is absent.
|
|
64
|
+
def self.tracer
|
|
65
|
+
@tracer ||=
|
|
66
|
+
if defined?(OpenTelemetry)
|
|
67
|
+
# :nocov:
|
|
68
|
+
OpenTelemetry.tracer_provider.tracer(TRACER_NAME, Jekyll::LinkDecorator::VERSION)
|
|
69
|
+
# :nocov:
|
|
70
|
+
else
|
|
71
|
+
NoopTracer.new
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Wraps a block in an OTel span.
|
|
76
|
+
#
|
|
77
|
+
# @param span_name [String] Dot-separated span name (e.g. 'link.style_apply')
|
|
78
|
+
# @param attributes [Hash] Initial span attributes
|
|
79
|
+
# @yieldparam span [OpenTelemetry::Trace::Span, NoopSpan] Active span
|
|
80
|
+
# @return [Object] The return value of the block
|
|
81
|
+
def self.instrument(span_name, attributes: {}, &block)
|
|
82
|
+
tracer.in_span(span_name, attributes: attributes, &block)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Resets the cached tracer and installation flag. Call in tests after changing OTel configuration.
|
|
86
|
+
def self.reset!
|
|
87
|
+
@tracer = nil
|
|
88
|
+
@installed = false
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Returns true when OTel is requested via standard environment variables.
|
|
92
|
+
#
|
|
93
|
+
# install! guards on this so the prepend wrappers are only applied when
|
|
94
|
+
# a real exporter is configured. In CI and local tests (no OTel env vars)
|
|
95
|
+
# business logic classes are untouched, keeping allow_any_instance_of stubs
|
|
96
|
+
# and other RSpec mechanics fully functional.
|
|
97
|
+
def self.enabled?
|
|
98
|
+
ENV.key?('OTEL_EXPORTER_OTLP_ENDPOINT') || ENV.key?('OTEL_SERVICE_NAME')
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Installs wrappers on all classes listed in TRACED_METHODS using Module#prepend.
|
|
102
|
+
#
|
|
103
|
+
# Called once at plugin load time (end of jekyll-link-decorator.rb, after all
|
|
104
|
+
# requires), but only when enabled? returns true. Use OTEL_EXPORTER_OTLP_ENDPOINT
|
|
105
|
+
# or OTEL_SERVICE_NAME to activate tracing.
|
|
106
|
+
def self.install!
|
|
107
|
+
return if @installed
|
|
108
|
+
|
|
109
|
+
@installed = true
|
|
110
|
+
setup_sdk!
|
|
111
|
+
TRACED_METHODS.group_by { |e| e[0] }.each do |class_name, entries|
|
|
112
|
+
prepend_wrappers(resolve_class(class_name), entries)
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# Configures the OTel SDK from standard env vars if the SDK gem is available.
|
|
117
|
+
#
|
|
118
|
+
# Called from install! so that spans are exported to the configured backend
|
|
119
|
+
# without requiring sites to add a separate initializer. Silently no-ops when
|
|
120
|
+
# opentelemetry-sdk or opentelemetry-exporter-otlp is not installed.
|
|
121
|
+
#
|
|
122
|
+
# SimpleSpanProcessor is used instead of the SDK default (BatchSpanProcessor)
|
|
123
|
+
# because Jekyll is a short-lived CLI process. BatchSpanProcessor exports on a
|
|
124
|
+
# 5-second schedule; outer spans that close last (decorate_page) are silently
|
|
125
|
+
# dropped when the process exits before the next flush. Simple exports each
|
|
126
|
+
# span synchronously the moment it closes, guaranteeing no span loss.
|
|
127
|
+
#
|
|
128
|
+
# jekyll-theme-centos loads several instrumented plugins in one process
|
|
129
|
+
# (jekyll-l10n uses the 'l10n.' tracer, jekyll-minify uses 'minify.'), and
|
|
130
|
+
# OpenTelemetry::SDK.configure unconditionally overwrites the global
|
|
131
|
+
# tracer_provider on every call — so only the first plugin to reach this
|
|
132
|
+
# method (see sdk_already_configured?) actually configures the SDK; every
|
|
133
|
+
# later plugin's call becomes a no-op and reuses that provider. The
|
|
134
|
+
# at_exit and post_write hooks below are therefore registered once, by
|
|
135
|
+
# whichever plugin wins, and cover every plugin's spans, not just this one's.
|
|
136
|
+
def self.setup_sdk!
|
|
137
|
+
return if sdk_already_configured?
|
|
138
|
+
|
|
139
|
+
# :nocov:
|
|
140
|
+
require 'opentelemetry/sdk'
|
|
141
|
+
require 'opentelemetry/exporter/otlp'
|
|
142
|
+
exporter = OpenTelemetry::Exporter::OTLP::Exporter.new
|
|
143
|
+
processor = OpenTelemetry::SDK::Trace::Export::SimpleSpanProcessor.new(exporter)
|
|
144
|
+
OpenTelemetry::SDK.configure { |c| c.add_span_processor(processor) }
|
|
145
|
+
|
|
146
|
+
at_exit { OpenTelemetry.tracer_provider&.shutdown }
|
|
147
|
+
Jekyll::Hooks.register(:site, :post_write, priority: 0) { OpenTelemetry.tracer_provider&.force_flush }
|
|
148
|
+
rescue LoadError
|
|
149
|
+
nil
|
|
150
|
+
# :nocov:
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
# True when some plugin (this one or another) already installed a real
|
|
154
|
+
# SDK-backed tracer_provider, in which case setup_sdk! must not run again.
|
|
155
|
+
def self.sdk_already_configured?
|
|
156
|
+
defined?(OpenTelemetry::SDK::Trace::TracerProvider) &&
|
|
157
|
+
OpenTelemetry.tracer_provider.is_a?(OpenTelemetry::SDK::Trace::TracerProvider)
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
# Resolves a dot-separated class name to a constant; returns nil on NameError.
|
|
161
|
+
def self.resolve_class(name)
|
|
162
|
+
name.split('::').reduce(Object) { |m, c| m.const_get(c) }
|
|
163
|
+
rescue NameError
|
|
164
|
+
nil
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
# Applies instance and class-method wrappers to klass; skips if klass is nil.
|
|
168
|
+
def self.prepend_wrappers(klass, entries)
|
|
169
|
+
return unless klass
|
|
170
|
+
|
|
171
|
+
class_entries, instance_entries = entries.partition { |e| e[1] == :class }
|
|
172
|
+
klass.prepend(build_wrapper_module(instance_entries)) unless instance_entries.empty?
|
|
173
|
+
klass.singleton_class.prepend(build_wrapper_module(class_entries)) unless class_entries.empty?
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
# Builds an anonymous module that wraps each listed method in a span.
|
|
177
|
+
def self.build_wrapper_module(entries)
|
|
178
|
+
Module.new do
|
|
179
|
+
entries.each do |_class_name, method_type, method_name, span_name, attr_proc|
|
|
180
|
+
define_method(method_name) do |*args, **kwargs, &blk|
|
|
181
|
+
Instrumentation.instrument(span_name) do |span|
|
|
182
|
+
result = super(*args, **kwargs, &blk)
|
|
183
|
+
attr_proc&.call(span, self, args, result)
|
|
184
|
+
result
|
|
185
|
+
rescue StandardError => e
|
|
186
|
+
span.record_exception(e)
|
|
187
|
+
raise
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
private method_name if method_type == :private
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
private_class_method :build_wrapper_module, :prepend_wrappers, :setup_sdk!, :sdk_already_configured?
|
|
196
|
+
|
|
197
|
+
# No-op tracer used when opentelemetry-api is not loaded.
|
|
198
|
+
class NoopTracer
|
|
199
|
+
def in_span(_name, **_opts)
|
|
200
|
+
yield NoopSpan.new
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
# No-op span that silently accepts all attribute and event calls.
|
|
205
|
+
class NoopSpan
|
|
206
|
+
def set_attribute(*)
|
|
207
|
+
self
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
def record_exception(*)
|
|
211
|
+
self
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
def status=(*); end
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Jekyll
|
|
4
|
+
module LinkDecorator
|
|
5
|
+
# Autogenerated by jekyll-link-decorator.gemspec.rb — do not edit by hand.
|
|
6
|
+
# Regenerated alongside the gemspec on every `make gemspec` run (locally
|
|
7
|
+
# or in CI), so it always matches spec.version in the built gem. Any
|
|
8
|
+
# hand edit is overwritten on the next run.
|
|
9
|
+
VERSION = '1.4.0'
|
|
10
|
+
end
|
|
11
|
+
end
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
# are injected. Set to true to enable.
|
|
52
52
|
#
|
|
53
53
|
# * `with_heading_anchor_data`: A dictionary containing optional configuration keys:
|
|
54
|
-
# - `icon`: Font Awesome icon classes (default: "fa-solid fa-
|
|
54
|
+
# - `icon`: Font Awesome icon classes (default: "fa-solid fa-hashtag")
|
|
55
55
|
# - `icon_size`: Font Awesome size modifier appended after icon classes (optional)
|
|
56
56
|
# - `copy_success_message`: Feedback text after copying (consumed by heading-anchor.js,
|
|
57
57
|
# default: "Copied!")
|
|
@@ -75,12 +75,14 @@
|
|
|
75
75
|
#
|
|
76
76
|
# with_heading_anchor: true
|
|
77
77
|
# with_heading_anchor_data:
|
|
78
|
-
# icon: "fa-solid fa-
|
|
78
|
+
# icon: "fa-solid fa-hashtag"
|
|
79
79
|
# copy_success_message: "Copied!"
|
|
80
80
|
# reset_delay: 2000
|
|
81
81
|
# ```
|
|
82
82
|
|
|
83
83
|
require 'nokogiri'
|
|
84
|
+
require_relative 'jekyll-link-decorator/version'
|
|
85
|
+
require_relative 'jekyll-link-decorator/instrumentation'
|
|
84
86
|
|
|
85
87
|
EXISTING_ICON_SELECTORS = [
|
|
86
88
|
'i[class*="fa-external-link"]',
|
|
@@ -103,7 +105,7 @@ module Jekyll
|
|
|
103
105
|
DEFAULT_EXTERNAL_LINK_ICON = true
|
|
104
106
|
DEFAULT_EXTERNAL_LINK_ICON_EXCLUDED_TAGS = [].freeze
|
|
105
107
|
DEFAULT_HEADING_ANCHOR = false
|
|
106
|
-
DEFAULT_HEADING_ANCHOR_ICON = 'fa-solid fa-
|
|
108
|
+
DEFAULT_HEADING_ANCHOR_ICON = 'fa-solid fa-hashtag'
|
|
107
109
|
|
|
108
110
|
def self.name
|
|
109
111
|
'LinkDecorator'
|
|
@@ -381,3 +383,5 @@ module Jekyll
|
|
|
381
383
|
end
|
|
382
384
|
|
|
383
385
|
Liquid::Template.register_filter(Jekyll::Filters::LinkDecoratorFilter)
|
|
386
|
+
|
|
387
|
+
Jekyll::LinkDecorator::Instrumentation.install! if Jekyll::LinkDecorator::Instrumentation.enabled?
|
metadata
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: jekyll-link-decorator
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
|
-
-
|
|
7
|
+
- Alain Reguera Delgado
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
10
|
date: 1980-01-02 00:00:00.000000000 Z
|
|
@@ -49,6 +49,20 @@ dependencies:
|
|
|
49
49
|
- - "<"
|
|
50
50
|
- !ruby/object:Gem::Version
|
|
51
51
|
version: '2.0'
|
|
52
|
+
- !ruby/object:Gem::Dependency
|
|
53
|
+
name: opentelemetry-api
|
|
54
|
+
requirement: !ruby/object:Gem::Requirement
|
|
55
|
+
requirements:
|
|
56
|
+
- - "~>"
|
|
57
|
+
- !ruby/object:Gem::Version
|
|
58
|
+
version: '1.4'
|
|
59
|
+
type: :runtime
|
|
60
|
+
prerelease: false
|
|
61
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
62
|
+
requirements:
|
|
63
|
+
- - "~>"
|
|
64
|
+
- !ruby/object:Gem::Version
|
|
65
|
+
version: '1.4'
|
|
52
66
|
- !ruby/object:Gem::Dependency
|
|
53
67
|
name: bundler
|
|
54
68
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -194,7 +208,7 @@ description: A Jekyll plugin that automatically enhances Markdown links with Boo
|
|
|
194
208
|
Simplifies link management across Jekyll sites by handling styling, external link
|
|
195
209
|
protection, and icon injection without manual intervention.
|
|
196
210
|
email:
|
|
197
|
-
-
|
|
211
|
+
- alain.reguera@gmail.com
|
|
198
212
|
executables: []
|
|
199
213
|
extensions: []
|
|
200
214
|
extra_rdoc_files: []
|
|
@@ -202,6 +216,8 @@ files:
|
|
|
202
216
|
- LICENSE
|
|
203
217
|
- README.md
|
|
204
218
|
- lib/jekyll-link-decorator.rb
|
|
219
|
+
- lib/jekyll-link-decorator/instrumentation.rb
|
|
220
|
+
- lib/jekyll-link-decorator/version.rb
|
|
205
221
|
homepage: https://gitlab.com/CentOS/artwork/centos-web/jekyll-link-decorator
|
|
206
222
|
licenses:
|
|
207
223
|
- MIT
|