File: jekyll-mentions.rb

package info (click to toggle)
ruby-jekyll-mentions 1.6.0-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, bullseye, forky, sid, trixie
  • size: 244 kB
  • sloc: ruby: 305; sh: 18; makefile: 4
file content (128 lines) | stat: -rw-r--r-- 4,136 bytes parent folder | download
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
# frozen_string_literal: true

require "jekyll"
require "html/pipeline"

module Jekyll
  class Mentions
    GITHUB_DOT_COM = "https://github.com"
    BODY_START_TAG = "<body"

    OPENING_BODY_TAG_REGEX = %r!<body(.*?)>\s*!m.freeze

    InvalidJekyllMentionConfig = Class.new(Jekyll::Errors::FatalException)

    class << self
      # rubocop:disable Metrics/AbcSize
      def mentionify(doc)
        content = doc.output
        return unless content.include?("@")

        config = doc.site.config
        config = config.merge(doc.data) if doc.data.key?("jekyll-mentions")

        src = mention_base(config)

        if content.include? BODY_START_TAG
          head, opener, tail  = content.partition(OPENING_BODY_TAG_REGEX)
          body_content, *rest = tail.partition("</body>")

          return unless body_content&.match?(filter_regex)

          processed_markup = filter_with_mention(src).call(body_content)[:output].to_s
          doc.output       = String.new(head) << opener << processed_markup << rest.join
        else
          return unless content&.match?(filter_regex)

          doc.output = filter_with_mention(src).call(content)[:output].to_s
        end
      end
      # rubocop:enable Metrics/AbcSize

      # Public: Create or fetch the filter for the given {{src}} base URL.
      #
      # src - the base URL (e.g. https://github.com)
      #
      # Returns an HTML::Pipeline instance for the given base URL.
      def filter_with_mention(src)
        filters[src] ||= HTML::Pipeline.new([
          HTML::Pipeline::MentionFilter,
        ], :base_url => src, :username_pattern => mention_username_pattern)
      end

      def mention_username_pattern
        @mention_username_pattern ||= %r![\w][\w-]*!
      end

      # Public: Filters hash where the key is the mention base URL.
      # Effectively a cache.
      def filters
        @filters ||= {}
      end

      # Public: Calculate the base URL to use for mentioning.
      #
      # The custom base URL can be defined in either the site config or a document's
      # front matter as `jekyll-mentions.base_url` or `jekyll-mentions`, and must be
      # a valid URL (i.e. it must include a protocol and valid domain).
      # It should _not_ have a trailing slash.
      #
      # config - The effective configuration that includes configurations for mentions.
      #
      # Returns a URL to use as the base URL for mentions.
      # Defaults to the https://github.com.
      def mention_base(config = {})
        mention_config = config["jekyll-mentions"]
        case mention_config
        when nil, NilClass
          default_mention_base
        when String
          mention_config.to_s
        when Hash
          mention_config.fetch("base_url", default_mention_base)
        else
          raise InvalidJekyllMentionConfig,
                "Your jekyll-mentions config has to either be a string or a hash. " \
                "It's a #{mention_config.class} right now."
        end
      end

      # Public: Defines the conditions for a document to be mentionable.
      #
      # doc - the Jekyll::Document or Jekyll::Page
      #
      # Returns true if the doc is written & is HTML.
      def mentionable?(doc)
        (doc.is_a?(Jekyll::Page) || doc.write?) &&
          (doc.output_ext == ".html" || (doc.permalink&.end_with?("/"))) &&
          (doc.data["jekyll-mentions"] != false)
      end

      private

      def filter_regex
        @filter_regex ||=
          begin
            Regexp.new(
              HTML::Pipeline::MentionFilter::MentionPatterns[mention_username_pattern]
            )
          rescue TypeError
            %r!@\w+!
          end
      end

      def default_mention_base
        if !ENV["SSL"].to_s.empty? && !ENV["GITHUB_HOSTNAME"].to_s.empty?
          scheme = ENV["SSL"] == "true" ? "https://" : "http://"
          "#{scheme}#{ENV["GITHUB_HOSTNAME"].chomp("/")}"
        else
          GITHUB_DOT_COM
        end
      end
    end
  end
end

Jekyll::Hooks.register [:pages, :documents], :post_render do |doc|
  Jekyll::Mentions.mentionify(doc) if Jekyll::Mentions.mentionable?(doc)
end