File: base.rb

package info (click to toggle)
ruby-sprockets 4.2.1-4
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 2,964 kB
  • sloc: ruby: 13,014; javascript: 157; makefile: 4
file content (147 lines) | stat: -rw-r--r-- 4,107 bytes parent folder | download | duplicates (2)
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
# frozen_string_literal: true
require 'sprockets/asset'
require 'sprockets/bower'
require 'sprockets/cache'
require 'sprockets/configuration'
require 'sprockets/digest_utils'
require 'sprockets/errors'
require 'sprockets/loader'
require 'sprockets/npm'
require 'sprockets/path_dependency_utils'
require 'sprockets/path_digest_utils'
require 'sprockets/path_utils'
require 'sprockets/resolve'
require 'sprockets/server'
require 'sprockets/source_map_utils'
require 'sprockets/uri_tar'

module Sprockets

  class DoubleLinkError < Sprockets::Error
    def initialize(parent_filename:, logical_path:, last_filename:, filename:)
      super <<~MSG
        Multiple files with the same output path cannot be linked (#{logical_path.inspect})
        In #{parent_filename.inspect} these files were linked:
          - #{last_filename}
          - #{filename}
      MSG
    end
  end

  # `Base` class for `Environment` and `CachedEnvironment`.
  class Base
    include PathUtils, PathDependencyUtils, PathDigestUtils, DigestUtils, SourceMapUtils
    include Configuration
    include Server
    include Resolve, Loader
    include Bower
    include Npm

    # Get persistent cache store
    attr_reader :cache

    # Set persistent cache store
    #
    # The cache store must implement a pair of getters and
    # setters. Either `get(key)`/`set(key, value)`,
    # `[key]`/`[key]=value`, `read(key)`/`write(key, value)`.
    def cache=(cache)
      @cache = Cache.new(cache, logger)
    end

    # Return an `CachedEnvironment`. Must be implemented by the subclass.
    def cached
      raise NotImplementedError
    end
    alias_method :index, :cached

    # Internal: Compute digest for path.
    #
    # path - String filename or directory path.
    #
    # Returns a String digest or nil.
    def file_digest(path)
      if stat = self.stat(path)
        # Caveat: Digests are cached by the path's current mtime. Its possible
        # for a files contents to have changed and its mtime to have been
        # negligently reset thus appearing as if the file hasn't changed on
        # disk. Also, the mtime is only read to the nearest second. It's
        # also possible the file was updated more than once in a given second.
        key = UnloadedAsset.new(path, self).file_digest_key(stat.mtime.to_i)
        cache.fetch(key) do
          self.stat_digest(path, stat)
        end
      end
    end

    # Find asset by logical path or expanded path.
    def find_asset(*args, **options)
      uri, _ = resolve(*args, **options)
      if uri
        load(uri)
      end
    end

    def find_all_linked_assets(*args)
      return to_enum(__method__, *args) unless block_given?

      parent_asset = asset = find_asset(*args)
      return unless asset

      yield asset
      stack = asset.links.to_a
      linked_paths = {}

      while uri = stack.shift
        yield asset = load(uri)

        last_filename = linked_paths[asset.logical_path]
        if last_filename && last_filename != asset.filename
          raise DoubleLinkError.new(
            parent_filename: parent_asset.filename,
            last_filename:   last_filename,
            logical_path:    asset.logical_path,
            filename:        asset.filename
          )
        end
        linked_paths[asset.logical_path] = asset.filename
        stack = asset.links.to_a + stack
      end

      nil
    end

    # Preferred `find_asset` shorthand.
    #
    #     environment['application.js']
    #
    def [](*args, **options)
      find_asset(*args, **options)
    end

    # Find asset by logical path or expanded path.
    #
    # If the asset is not found an error will be raised.
    def find_asset!(*args)
      uri, _ = resolve!(*args)
      if uri
        load(uri)
      end
    end

    # Pretty inspect
    def inspect
      "#<#{self.class}:0x#{object_id.to_s(16)} " +
        "root=#{root.to_s.inspect}, " +
        "paths=#{paths.inspect}>"
    end

    def compress_from_root(uri)
      URITar.new(uri, self).compress
    end

    def expand_from_root(uri)
      URITar.new(uri, self).expand
    end
  end
end