summaryrefslogtreecommitdiff
path: root/vendor/bundle/ruby/3.4.0/gems/jekyll-4.4.1/lib/jekyll/hooks.rb
blob: 482d802614f811b070b92b8b9b1205f757966c02 (plain)
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
# frozen_string_literal: true

module Jekyll
  module Hooks
    DEFAULT_PRIORITY = 20

    # compatibility layer for octopress-hooks users
    PRIORITY_MAP = {
      :low    => 10,
      :normal => 20,
      :high   => 30,
    }.freeze

    # initial empty hooks
    @registry = {
      :site      => {
        :after_init  => [],
        :after_reset => [],
        :post_read   => [],
        :pre_render  => [],
        :post_render => [],
        :post_write  => [],
      },
      :pages     => {
        :post_init    => [],
        :pre_render   => [],
        :post_convert => [],
        :post_render  => [],
        :post_write   => [],
      },
      :posts     => {
        :post_init    => [],
        :pre_render   => [],
        :post_convert => [],
        :post_render  => [],
        :post_write   => [],
      },
      :documents => {
        :post_init    => [],
        :pre_render   => [],
        :post_convert => [],
        :post_render  => [],
        :post_write   => [],
      },
      :clean     => {
        :on_obsolete => [],
      },
    }

    # map of all hooks and their priorities
    @hook_priority = {}

    NotAvailable = Class.new(RuntimeError)
    Uncallable = Class.new(RuntimeError)

    # register hook(s) to be called later, public API
    def self.register(owners, event, priority: DEFAULT_PRIORITY, &block)
      Array(owners).each do |owner|
        register_one(owner, event, priority_value(priority), &block)
      end
    end

    # Ensure the priority is a Fixnum
    def self.priority_value(priority)
      return priority if priority.is_a?(Integer)

      PRIORITY_MAP[priority] || DEFAULT_PRIORITY
    end

    # register a single hook to be called later, internal API
    def self.register_one(owner, event, priority, &block)
      @registry[owner] ||= {
        :post_init    => [],
        :pre_render   => [],
        :post_convert => [],
        :post_render  => [],
        :post_write   => [],
      }

      unless @registry[owner][event]
        raise NotAvailable, "Invalid hook. #{owner} supports only the following hooks " \
                            "#{@registry[owner].keys.inspect}"
      end

      raise Uncallable, "Hooks must respond to :call" unless block.respond_to? :call

      insert_hook owner, event, priority, &block
    end

    def self.insert_hook(owner, event, priority, &block)
      @hook_priority[block] = [-priority, @hook_priority.size]
      @registry[owner][event] << block
    end

    # interface for Jekyll core components to trigger hooks
    def self.trigger(owner, event, *args)
      # proceed only if there are hooks to call
      hooks = @registry.dig(owner, event)
      return if hooks.nil? || hooks.empty?

      # sort and call hooks according to priority and load order
      hooks.sort_by { |h| @hook_priority[h] }.each do |hook|
        hook.call(*args)
      end
    end
  end
end