hanami-assets 1.3.5 → 2.1.0.beta2
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/CHANGELOG.md +22 -0
- data/README.md +92 -314
- data/hanami-assets.gemspec +26 -33
- data/lib/hanami/assets/asset.rb +83 -0
- data/lib/hanami/assets/base_url.rb +64 -0
- data/lib/hanami/assets/config.rb +106 -0
- data/lib/hanami/assets/errors.rb +46 -0
- data/lib/hanami/assets/version.rb +2 -2
- data/lib/hanami/assets.rb +61 -143
- data/lib/hanami-assets.rb +3 -0
- metadata +33 -115
- data/lib/hanami/assets/bundler/asset.rb +0 -100
- data/lib/hanami/assets/bundler/compressor.rb +0 -63
- data/lib/hanami/assets/bundler/manifest_entry.rb +0 -64
- data/lib/hanami/assets/bundler.rb +0 -154
- data/lib/hanami/assets/cache.rb +0 -102
- data/lib/hanami/assets/compiler.rb +0 -287
- data/lib/hanami/assets/compilers/less.rb +0 -31
- data/lib/hanami/assets/compilers/sass.rb +0 -61
- data/lib/hanami/assets/compressors/abstract.rb +0 -119
- data/lib/hanami/assets/compressors/builtin_javascript.rb +0 -36
- data/lib/hanami/assets/compressors/builtin_stylesheet.rb +0 -57
- data/lib/hanami/assets/compressors/closure_javascript.rb +0 -25
- data/lib/hanami/assets/compressors/javascript.rb +0 -77
- data/lib/hanami/assets/compressors/jsmin.rb +0 -284
- data/lib/hanami/assets/compressors/null_compressor.rb +0 -19
- data/lib/hanami/assets/compressors/sass_stylesheet.rb +0 -36
- data/lib/hanami/assets/compressors/stylesheet.rb +0 -77
- data/lib/hanami/assets/compressors/uglifier_javascript.rb +0 -25
- data/lib/hanami/assets/compressors/yui_javascript.rb +0 -25
- data/lib/hanami/assets/compressors/yui_stylesheet.rb +0 -25
- data/lib/hanami/assets/config/global_sources.rb +0 -52
- data/lib/hanami/assets/config/manifest.rb +0 -142
- data/lib/hanami/assets/config/sources.rb +0 -80
- data/lib/hanami/assets/configuration.rb +0 -657
- data/lib/hanami/assets/helpers.rb +0 -945
- data/lib/hanami/assets/precompiler.rb +0 -97
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Hanami
|
|
4
|
+
class Assets
|
|
5
|
+
# Represents a single front end asset.
|
|
6
|
+
#
|
|
7
|
+
# @api public
|
|
8
|
+
# @since 2.1.0
|
|
9
|
+
class Asset
|
|
10
|
+
# @api private
|
|
11
|
+
# @since 2.1.0
|
|
12
|
+
attr_reader :config
|
|
13
|
+
private :config
|
|
14
|
+
|
|
15
|
+
# Returns the asset's absolute URL path.
|
|
16
|
+
#
|
|
17
|
+
# @example Asset from local dev server
|
|
18
|
+
# asset.path # => "/assets/app.js"
|
|
19
|
+
#
|
|
20
|
+
# @example Deployed asset with fingerprinted name
|
|
21
|
+
# asset.path # => "/assets/app-28a6b886de2372ee3922fcaf3f78f2d8.js"
|
|
22
|
+
#
|
|
23
|
+
# @return [String]
|
|
24
|
+
#
|
|
25
|
+
# @api public
|
|
26
|
+
# @since 2.1.0
|
|
27
|
+
attr_reader :path
|
|
28
|
+
|
|
29
|
+
# @api private
|
|
30
|
+
# @since 2.1.0
|
|
31
|
+
attr_reader :base_url
|
|
32
|
+
private :base_url
|
|
33
|
+
|
|
34
|
+
# Returns the asset's subresource integrity value, or nil if none is available.
|
|
35
|
+
#
|
|
36
|
+
# @return [String, nil]
|
|
37
|
+
#
|
|
38
|
+
# @api public
|
|
39
|
+
# @since 2.1.0
|
|
40
|
+
attr_reader :sri
|
|
41
|
+
|
|
42
|
+
# @api private
|
|
43
|
+
# @since 2.1.0
|
|
44
|
+
def initialize(path:, base_url:, sri: nil)
|
|
45
|
+
@path = path
|
|
46
|
+
@base_url = base_url
|
|
47
|
+
@sri = sri
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# @api public
|
|
51
|
+
# @since 2.1.0
|
|
52
|
+
alias_method :subresource_integrity_value, :sri
|
|
53
|
+
|
|
54
|
+
# Returns the asset's full URL.
|
|
55
|
+
#
|
|
56
|
+
# @example Asset from local dev server
|
|
57
|
+
# asset.path # => "https://example.com/assets/app.js"
|
|
58
|
+
#
|
|
59
|
+
# @example Deployed asset with fingerprinted name
|
|
60
|
+
# asset.path # => "https://example.com/assets/app-28a6b886de2372ee3922fcaf3f78f2d8.js"
|
|
61
|
+
#
|
|
62
|
+
# @return [String]
|
|
63
|
+
#
|
|
64
|
+
# @api public
|
|
65
|
+
# @since 2.1.0
|
|
66
|
+
def url
|
|
67
|
+
base_url.join(path)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Returns the asset's full URL
|
|
71
|
+
#
|
|
72
|
+
# @return [String]
|
|
73
|
+
#
|
|
74
|
+
# @see #url
|
|
75
|
+
#
|
|
76
|
+
# @api public
|
|
77
|
+
# @since 2.1.0
|
|
78
|
+
def to_s
|
|
79
|
+
url
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Hanami
|
|
4
|
+
class Assets
|
|
5
|
+
# Base URL
|
|
6
|
+
#
|
|
7
|
+
# @since 2.1.0
|
|
8
|
+
# @api private
|
|
9
|
+
class BaseUrl
|
|
10
|
+
# @since 2.1.0
|
|
11
|
+
# @api private
|
|
12
|
+
SEPARATOR = "/"
|
|
13
|
+
private_constant :SEPARATOR
|
|
14
|
+
|
|
15
|
+
# @since 2.1.0
|
|
16
|
+
# @api private
|
|
17
|
+
attr_reader :url
|
|
18
|
+
private :url
|
|
19
|
+
|
|
20
|
+
# Initialize a base URL
|
|
21
|
+
#
|
|
22
|
+
# @param url [String] the URL
|
|
23
|
+
# @param prefix [String,NilClass] the prefix
|
|
24
|
+
#
|
|
25
|
+
# @since 2.1.0
|
|
26
|
+
# @api private
|
|
27
|
+
def initialize(url, prefix = nil)
|
|
28
|
+
@url = URI(url + prefix.to_s).to_s
|
|
29
|
+
freeze
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Join the base URL with the given paths
|
|
33
|
+
#
|
|
34
|
+
# @param other [String] the paths
|
|
35
|
+
# @return [String] the joined URL
|
|
36
|
+
#
|
|
37
|
+
# @since 2.1.0
|
|
38
|
+
# @api private
|
|
39
|
+
def join(other)
|
|
40
|
+
(url + other).to_s
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# @since 2.1.0
|
|
44
|
+
# @api private
|
|
45
|
+
def to_s
|
|
46
|
+
@url
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Check if the source is a cross origin
|
|
50
|
+
#
|
|
51
|
+
# @param source [String] the source
|
|
52
|
+
# @return [Boolean] true if the source is a cross origin
|
|
53
|
+
#
|
|
54
|
+
# @since 2.1.0
|
|
55
|
+
# @api private
|
|
56
|
+
def crossorigin?(source)
|
|
57
|
+
# TODO: review if this is the right way to check for cross origin
|
|
58
|
+
return true if @url.empty?
|
|
59
|
+
|
|
60
|
+
!source.start_with?(@url)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "dry/configurable"
|
|
4
|
+
require_relative "base_url"
|
|
5
|
+
|
|
6
|
+
module Hanami
|
|
7
|
+
class Assets
|
|
8
|
+
# Framework configuration
|
|
9
|
+
#
|
|
10
|
+
# @since 0.1.0
|
|
11
|
+
class Config
|
|
12
|
+
include Dry::Configurable
|
|
13
|
+
|
|
14
|
+
# @since 2.1.0
|
|
15
|
+
# @api private
|
|
16
|
+
BASE_URL = ""
|
|
17
|
+
private_constant :BASE_URL
|
|
18
|
+
|
|
19
|
+
# @since 2.1.0
|
|
20
|
+
# @api private
|
|
21
|
+
setting :package_manager_executable, default: "npm"
|
|
22
|
+
|
|
23
|
+
# @since 2.1.0
|
|
24
|
+
# @api private
|
|
25
|
+
setting :package_manager_command, default: "exec"
|
|
26
|
+
|
|
27
|
+
# @since 2.1.0
|
|
28
|
+
# @api private
|
|
29
|
+
setting :executable, default: "hanami-assets"
|
|
30
|
+
|
|
31
|
+
# @since 2.1.0
|
|
32
|
+
# @api private
|
|
33
|
+
setting :path_prefix, default: "/assets"
|
|
34
|
+
|
|
35
|
+
# @since 2.1.0
|
|
36
|
+
# @api private
|
|
37
|
+
setting :destination
|
|
38
|
+
|
|
39
|
+
# @since 2.1.0
|
|
40
|
+
# @api private
|
|
41
|
+
setting :subresource_integrity, default: []
|
|
42
|
+
|
|
43
|
+
# @since 2.1.0
|
|
44
|
+
# @api private
|
|
45
|
+
setting :sources, default: [], constructor: -> v { Array(v).flatten }
|
|
46
|
+
|
|
47
|
+
# @since 2.1.0
|
|
48
|
+
# @api private
|
|
49
|
+
setting :base_url, constructor: -> url { BaseUrl.new(url.to_s) }
|
|
50
|
+
|
|
51
|
+
# @since 2.1.0
|
|
52
|
+
# @api private
|
|
53
|
+
setting :entry_points_pattern, default: "index.{js,jsx,ts,tsx}"
|
|
54
|
+
|
|
55
|
+
# @since 2.1.0
|
|
56
|
+
# @api private
|
|
57
|
+
setting :manifest_path
|
|
58
|
+
|
|
59
|
+
# @since 2.1.0
|
|
60
|
+
# @api private
|
|
61
|
+
def initialize(**values)
|
|
62
|
+
super()
|
|
63
|
+
|
|
64
|
+
config.update(values.select { |k| _settings.key?(k) })
|
|
65
|
+
|
|
66
|
+
yield(config) if block_given?
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# @since 2.1.0
|
|
70
|
+
# @api private
|
|
71
|
+
def entry_points
|
|
72
|
+
sources.map do |source|
|
|
73
|
+
Dir.glob(File.join(source, "**", config.entry_points_pattern))
|
|
74
|
+
end.flatten
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Check if the given source is linked via Cross-Origin policy.
|
|
78
|
+
# In other words, the given source, doesn't satisfy the Same-Origin policy.
|
|
79
|
+
#
|
|
80
|
+
# @see https://en.wikipedia.org/wiki/Same-origin_policy#Origin_determination_rules
|
|
81
|
+
# @see https://en.wikipedia.org/wiki/Same-origin_policy#document.domain_property
|
|
82
|
+
#
|
|
83
|
+
# @since 1.2.0
|
|
84
|
+
# @api private
|
|
85
|
+
def crossorigin?(source)
|
|
86
|
+
base_url.crossorigin?(source)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
private
|
|
90
|
+
|
|
91
|
+
# @api private
|
|
92
|
+
def method_missing(name, ...)
|
|
93
|
+
if config.respond_to?(name)
|
|
94
|
+
config.public_send(name, ...)
|
|
95
|
+
else
|
|
96
|
+
super
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# @api private
|
|
101
|
+
def respond_to_missing?(name, _incude_all = false)
|
|
102
|
+
config.respond_to?(name) || super
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Hanami
|
|
4
|
+
class Assets
|
|
5
|
+
# Base error for Hanami::Assets
|
|
6
|
+
#
|
|
7
|
+
# All the errors defined in this framework MUST inherit from it.
|
|
8
|
+
#
|
|
9
|
+
# @since 0.1.0
|
|
10
|
+
class Error < ::StandardError
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# Error raised when assets config is not valid.
|
|
14
|
+
#
|
|
15
|
+
# @since 2.1.0
|
|
16
|
+
# @api public
|
|
17
|
+
class ConfigError < Error
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Error returned when the assets manifest file is missing.
|
|
21
|
+
#
|
|
22
|
+
# @since 2.1.0
|
|
23
|
+
# @api public
|
|
24
|
+
class ManifestMissingError < Error
|
|
25
|
+
def initialize(manifest_path)
|
|
26
|
+
super(<<~TEXT)
|
|
27
|
+
Missing manifest file at #{manifest_path.inspect}
|
|
28
|
+
|
|
29
|
+
Have you run `hanami assets compile` or `hanami assets watch`?
|
|
30
|
+
TEXT
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Error raised when no asset can be found for a source path.
|
|
35
|
+
#
|
|
36
|
+
# @since 2.1.0
|
|
37
|
+
# @api public
|
|
38
|
+
class AssetMissingError < Error
|
|
39
|
+
def initialize(source_path)
|
|
40
|
+
super(<<~TEXT)
|
|
41
|
+
No asset found for #{source_path.inspect}
|
|
42
|
+
TEXT
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
data/lib/hanami/assets.rb
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "
|
|
3
|
+
require "json"
|
|
4
|
+
require "zeitwerk"
|
|
4
5
|
|
|
5
6
|
# Hanami
|
|
6
7
|
#
|
|
@@ -9,167 +10,84 @@ module Hanami
|
|
|
9
10
|
# Assets management for Ruby web applications
|
|
10
11
|
#
|
|
11
12
|
# @since 0.1.0
|
|
12
|
-
|
|
13
|
-
#
|
|
14
|
-
#
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
13
|
+
class Assets
|
|
14
|
+
# @since 2.1.0
|
|
15
|
+
# @api private
|
|
16
|
+
def self.gem_loader
|
|
17
|
+
@gem_loader ||= Zeitwerk::Loader.new.tap do |loader|
|
|
18
|
+
root = File.expand_path("..", __dir__)
|
|
19
|
+
loader.tag = "hanami-assets"
|
|
20
|
+
loader.push_dir(root)
|
|
21
|
+
loader.ignore(
|
|
22
|
+
"#{root}/hanami-assets.rb",
|
|
23
|
+
"#{root}/hanami/assets/version.rb",
|
|
24
|
+
"#{root}/hanami/assets/errors.rb"
|
|
25
|
+
)
|
|
26
|
+
loader.inflector = Zeitwerk::GemInflector.new("#{root}/hanami-assets.rb")
|
|
27
|
+
end
|
|
19
28
|
end
|
|
20
29
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
require "hanami/assets/helpers"
|
|
30
|
+
gem_loader.setup
|
|
31
|
+
require_relative "assets/version"
|
|
32
|
+
require_relative "assets/errors"
|
|
25
33
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
# Configuration
|
|
29
|
-
#
|
|
30
|
-
# @since 0.1.0
|
|
34
|
+
# @since 2.1.0
|
|
31
35
|
# @api private
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
# Configure framework
|
|
36
|
-
#
|
|
37
|
-
# @param blk [Proc] configuration code block
|
|
38
|
-
#
|
|
39
|
-
# @return self
|
|
40
|
-
#
|
|
41
|
-
# @since 0.1.0
|
|
42
|
-
#
|
|
43
|
-
# @see Hanami::Assets::Configuration
|
|
44
|
-
def self.configure(&blk)
|
|
45
|
-
configuration.instance_eval(&blk)
|
|
46
|
-
self
|
|
47
|
-
end
|
|
36
|
+
SEPARATOR = "/"
|
|
37
|
+
private_constant :SEPARATOR
|
|
48
38
|
|
|
49
|
-
|
|
50
|
-
#
|
|
51
|
-
# @since 0.1.0
|
|
52
|
-
def self.deploy
|
|
53
|
-
require "hanami/assets/precompiler"
|
|
54
|
-
require "hanami/assets/bundler"
|
|
39
|
+
attr_reader :config
|
|
55
40
|
|
|
56
|
-
|
|
57
|
-
|
|
41
|
+
# @since 2.1.0
|
|
42
|
+
# @api public
|
|
43
|
+
def initialize(config:)
|
|
44
|
+
@config = config
|
|
58
45
|
end
|
|
59
46
|
|
|
60
|
-
#
|
|
61
|
-
#
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
47
|
+
# @since 2.1.0
|
|
48
|
+
# @api public
|
|
49
|
+
def [](path)
|
|
50
|
+
asset_attrs = manifest
|
|
51
|
+
.fetch(path) { raise AssetMissingError.new(path) }
|
|
52
|
+
.transform_keys(&:to_sym)
|
|
53
|
+
.tap { |attrs|
|
|
54
|
+
# The `url` attribute we receive from the manifest is actually a path; rename it as such
|
|
55
|
+
# so our `Asset` attributes make more sense on their own.
|
|
56
|
+
attrs[:path] = attrs.delete(:url)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
Asset.new(
|
|
60
|
+
**asset_attrs,
|
|
61
|
+
base_url: config.base_url
|
|
62
|
+
)
|
|
69
63
|
end
|
|
70
64
|
|
|
71
|
-
#
|
|
72
|
-
#
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
# @since 0.1.0
|
|
76
|
-
#
|
|
77
|
-
# @example Direct Invocation
|
|
78
|
-
# require 'hanami/assets'
|
|
79
|
-
#
|
|
80
|
-
# Hanami::Assets.load!
|
|
81
|
-
#
|
|
82
|
-
# @example Load Via Configuration Block
|
|
83
|
-
# require 'hanami/assets'
|
|
84
|
-
#
|
|
85
|
-
# Hanami::Assets.configure do
|
|
86
|
-
# # ...
|
|
87
|
-
# end.load!
|
|
88
|
-
def self.load!
|
|
89
|
-
configuration.load!
|
|
65
|
+
# @since 2.1.0
|
|
66
|
+
# @api public
|
|
67
|
+
def subresource_integrity?
|
|
68
|
+
config.subresource_integrity.any?
|
|
90
69
|
end
|
|
91
70
|
|
|
92
|
-
#
|
|
93
|
-
#
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
#
|
|
97
|
-
# Developers can maintain gems that ship static assets for these frameworks
|
|
98
|
-
# and make them available to Hanami::Assets.
|
|
99
|
-
#
|
|
100
|
-
# @return [Hanami::Assets::Config::GlobalSources]
|
|
101
|
-
#
|
|
102
|
-
# @since 0.1.0
|
|
103
|
-
#
|
|
104
|
-
# @example Ember.js Integration
|
|
105
|
-
# # lib/hanami/emberjs.rb (third party gem)
|
|
106
|
-
# require 'hanami/assets'
|
|
107
|
-
#
|
|
108
|
-
# Hanami::Assets.sources << '/path/to/emberjs/assets'
|
|
109
|
-
def self.sources
|
|
110
|
-
synchronize do
|
|
111
|
-
@@sources ||= Config::GlobalSources.new # rubocop:disable Style/ClassVars
|
|
112
|
-
end
|
|
71
|
+
# @since 2.1.0
|
|
72
|
+
# @api public
|
|
73
|
+
def crossorigin?(source_path)
|
|
74
|
+
config.crossorigin?(source_path)
|
|
113
75
|
end
|
|
114
76
|
|
|
115
|
-
|
|
116
|
-
#
|
|
117
|
-
# @param _mod [Module] the Ruby namespace of the application
|
|
118
|
-
# @param blk [Proc] an optional block to configure the framework
|
|
119
|
-
#
|
|
120
|
-
# @return [Module] a copy of Hanami::Assets
|
|
121
|
-
#
|
|
122
|
-
# @since 0.1.0
|
|
123
|
-
#
|
|
124
|
-
# @see Hanami::Assets#dupe
|
|
125
|
-
# @see Hanami::Assets::Configuration
|
|
126
|
-
def self.duplicate(_mod, &blk)
|
|
127
|
-
dupe.tap do |duplicated|
|
|
128
|
-
duplicated.configure(&blk) if block_given?
|
|
129
|
-
duplicates << duplicated
|
|
130
|
-
end
|
|
131
|
-
end
|
|
77
|
+
private
|
|
132
78
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
#
|
|
136
|
-
# The new instance of the framework will be completely decoupled from the
|
|
137
|
-
# original. It will inherit the configuration, but all the changes that
|
|
138
|
-
# happen after the duplication, won't be reflected on the other copies.
|
|
139
|
-
#
|
|
140
|
-
# @return [Module] a copy of Hanami::Assets
|
|
141
|
-
#
|
|
142
|
-
# @since 0.1.0
|
|
143
|
-
# @api private
|
|
144
|
-
def self.dupe
|
|
145
|
-
dup.tap do |duplicated|
|
|
146
|
-
duplicated.configuration = configuration.duplicate
|
|
147
|
-
end
|
|
148
|
-
end
|
|
79
|
+
def manifest
|
|
80
|
+
return @manifest if instance_variable_defined?(:@manifest)
|
|
149
81
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
# @return [Array] a collection of duplicated frameworks
|
|
153
|
-
#
|
|
154
|
-
# @since 0.1.0
|
|
155
|
-
# @api private
|
|
156
|
-
#
|
|
157
|
-
# @see Hanami::Assets#duplicate
|
|
158
|
-
# @see Hanami::Assets#dupe
|
|
159
|
-
def self.duplicates
|
|
160
|
-
synchronize do
|
|
161
|
-
@@duplicates ||= [] # rubocop:disable Style/ClassVars
|
|
82
|
+
unless config.manifest_path
|
|
83
|
+
raise ConfigError, "no manifest_path configured"
|
|
162
84
|
end
|
|
163
|
-
end
|
|
164
85
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
# @since 0.1.0
|
|
169
|
-
# @api private
|
|
170
|
-
def synchronize(&blk)
|
|
171
|
-
Mutex.new.synchronize(&blk)
|
|
86
|
+
unless File.exist?(config.manifest_path)
|
|
87
|
+
raise ManifestMissingError.new(config.manifest_path)
|
|
172
88
|
end
|
|
89
|
+
|
|
90
|
+
@manifest = JSON.parse(File.read(config.manifest_path))
|
|
173
91
|
end
|
|
174
92
|
end
|
|
175
93
|
end
|