summaryrefslogtreecommitdiff
path: root/vendor/bundle/ruby/3.4.0/gems/jekyll-4.4.1/lib/jekyll/static_file.rb
blob: 13479b0de21be7c6f999616fc975b8492b6d0206 (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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
# frozen_string_literal: true

module Jekyll
  class StaticFile
    extend Forwardable

    attr_reader :relative_path, :extname,
                :type, # Returns the type of the collection if present, nil otherwise.
                :name

    def_delegator :to_liquid, :to_json, :to_json

    class << self
      # The cache of last modification times [path] -> mtime.
      def mtimes
        @mtimes ||= {}
      end

      def reset_cache
        @mtimes = nil
      end
    end

    # Initialize a new StaticFile.
    #
    # site - The Site.
    # base - The String path to the <source>.
    # dir  - The String path between <source> and the file.
    # name - The String filename of the file.
    # rubocop: disable Metrics/ParameterLists
    def initialize(site, base, dir, name, collection = nil)
      @site = site
      @base = base
      @dir  = dir
      @name = name
      @collection = collection
      @relative_path = File.join(*[@dir, @name].compact)
      @extname = File.extname(@name)
      @type = @collection&.label&.to_sym
    end
    # rubocop: enable Metrics/ParameterLists

    # Returns source file path.
    def path
      @path ||= if !@collection.nil? && !@site.config["collections_dir"].empty?
                  File.join(*[@base, @site.config["collections_dir"], @dir, @name].compact)
                else
                  File.join(*[@base, @dir, @name].compact)
                end
    end

    # Obtain destination path.
    #
    # dest - The String path to the destination dir.
    #
    # Returns destination file path.
    def destination(dest)
      @destination ||= {}
      @destination[dest] ||= @site.in_dest_dir(dest, Jekyll::URL.unescape_path(url))
    end

    def destination_rel_dir
      if @collection
        File.dirname(url)
      else
        @dir
      end
    end

    def modified_time
      @modified_time ||= File.stat(path).mtime
    end

    # Returns last modification time for this file.
    def mtime
      modified_time.to_i
    end

    # Is source path modified?
    #
    # Returns true if modified since last write.
    def modified?
      self.class.mtimes[path] != mtime
    end

    # Whether to write the file to the filesystem
    #
    # Returns true unless the defaults for the destination path from
    # _config.yml contain `published: false`.
    def write?
      publishable = defaults.fetch("published", true)
      return publishable unless @collection

      publishable && @collection.write?
    end

    # Write the static file to the destination directory (if modified).
    #
    # dest - The String path to the destination dir.
    #
    # Returns false if the file was not modified since last time (no-op).
    def write(dest)
      dest_path = destination(dest)
      return false if File.exist?(dest_path) && !modified?

      self.class.mtimes[path] = mtime

      FileUtils.mkdir_p(File.dirname(dest_path))
      FileUtils.rm(dest_path) if File.exist?(dest_path)
      copy_file(dest_path)

      true
    end

    def data
      @data ||= @site.frontmatter_defaults.all(relative_path, type)
    end

    def to_liquid
      @to_liquid ||= Drops::StaticFileDrop.new(self)
    end

    # Generate "basename without extension" and strip away any trailing periods.
    # NOTE: `String#gsub` removes all trailing periods (in comparison to `String#chomp`)
    def basename
      @basename ||= File.basename(name, extname).gsub(%r!\.*\z!, "")
    end

    def placeholders
      {
        :collection => @collection.label,
        :path       => cleaned_relative_path,
        :output_ext => "",
        :name       => basename,
        :title      => "",
      }
    end

    # Similar to Jekyll::Document#cleaned_relative_path.
    # Generates a relative path with the collection's directory removed when applicable
    #   and additionally removes any multiple periods in the string.
    #
    # NOTE: `String#gsub!` removes all trailing periods (in comparison to `String#chomp!`)
    #
    # Examples:
    #   When `relative_path` is "_methods/site/my-cool-avatar...png":
    #     cleaned_relative_path
    #     # => "/site/my-cool-avatar"
    #
    # Returns the cleaned relative path of the static file.
    def cleaned_relative_path
      @cleaned_relative_path ||= begin
        cleaned = relative_path[0..-extname.length - 1]
        cleaned.gsub!(%r!\.*\z!, "")
        cleaned.sub!(@collection.relative_directory, "") if @collection
        cleaned
      end
    end

    # Applies a similar URL-building technique as Jekyll::Document that takes
    # the collection's URL template into account. The default URL template can
    # be overridden in the collection's configuration in _config.yml.
    def url
      @url ||= begin
        base = if @collection.nil?
                 cleaned_relative_path
               else
                 Jekyll::URL.new(
                   :template     => @collection.url_template,
                   :placeholders => placeholders
                 )
               end.to_s.chomp("/")
        base << extname
      end
    end

    # Returns the front matter defaults defined for the file's URL and/or type
    # as defined in _config.yml.
    def defaults
      @defaults ||= @site.frontmatter_defaults.all url, type
    end

    # Returns a debug string on inspecting the static file.
    # Includes only the relative path of the object.
    def inspect
      "#<#{self.class} @relative_path=#{relative_path.inspect}>"
    end

    private

    def copy_file(dest_path)
      if @site.safe || Jekyll.env == "production"
        FileUtils.cp(path, dest_path)
      else
        FileUtils.copy_entry(path, dest_path)
      end

      unless File.symlink?(dest_path)
        File.utime(self.class.mtimes[path], self.class.mtimes[path], dest_path)
      end
    end
  end
end