packwerk 2.3.0 → 3.0.0
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/.github/workflows/ci.yml +2 -5
- data/.ruby-version +1 -1
- data/Gemfile +0 -1
- data/Gemfile.lock +5 -95
- data/README.md +2 -7
- data/TROUBLESHOOT.md +0 -22
- data/USAGE.md +141 -51
- data/dev.yml +1 -1
- data/exe/packwerk +1 -0
- data/gemfiles/Gemfile-rails-6-1 +1 -1
- data/lib/packwerk/application_validator.rb +54 -285
- data/lib/packwerk/association_inspector.rb +2 -0
- data/lib/packwerk/cache.rb +6 -5
- data/lib/packwerk/checker.rb +54 -0
- data/lib/packwerk/cli/result.rb +11 -0
- data/lib/packwerk/cli.rb +55 -40
- data/lib/packwerk/configuration.rb +61 -40
- data/lib/packwerk/const_node_inspector.rb +2 -0
- data/lib/packwerk/constant_context.rb +8 -0
- data/lib/packwerk/constant_discovery.rb +5 -6
- data/lib/packwerk/constant_name_inspector.rb +2 -0
- data/lib/packwerk/disable_sorbet.rb +41 -0
- data/lib/packwerk/extension_loader.rb +24 -0
- data/lib/packwerk/file_processor.rb +3 -1
- data/lib/packwerk/files_for_processing.rb +25 -12
- data/lib/packwerk/formatters/default_offenses_formatter.rb +77 -0
- data/lib/packwerk/formatters/progress_formatter.rb +31 -12
- data/lib/packwerk/generators/configuration_file.rb +7 -2
- data/lib/packwerk/generators/root_package.rb +5 -1
- data/lib/packwerk/generators/templates/package.yml +0 -10
- data/lib/packwerk/graph.rb +10 -2
- data/lib/packwerk/node.rb +1 -1
- data/lib/packwerk/node_helpers.rb +14 -7
- data/lib/packwerk/node_processor.rb +2 -0
- data/lib/packwerk/node_processor_factory.rb +6 -4
- data/lib/packwerk/node_visitor.rb +10 -1
- data/lib/packwerk/offense_collection.rb +26 -18
- data/lib/packwerk/offenses_formatter.rb +59 -2
- data/lib/packwerk/package.rb +7 -35
- data/lib/packwerk/package_set.rb +1 -1
- data/lib/packwerk/package_todo.rb +15 -9
- data/lib/packwerk/parse_run.rb +27 -34
- data/lib/packwerk/parsed_constant_definitions.rb +28 -5
- data/lib/packwerk/parsers/erb.rb +23 -4
- data/lib/packwerk/parsers/factory.rb +11 -2
- data/lib/packwerk/parsers/parser_interface.rb +1 -1
- data/lib/packwerk/parsers/ruby.rb +13 -3
- data/lib/packwerk/parsers.rb +6 -2
- data/lib/packwerk/{application_load_paths.rb → rails_load_paths.rb} +6 -4
- data/lib/packwerk/reference.rb +7 -1
- data/lib/packwerk/reference_checking/checkers/dependency_checker.rb +29 -6
- data/lib/packwerk/reference_checking/reference_checker.rb +1 -1
- data/lib/packwerk/reference_extractor.rb +24 -12
- data/lib/packwerk/reference_offense.rb +2 -2
- data/lib/packwerk/run_context.rb +7 -10
- data/lib/packwerk/spring_command.rb +11 -2
- data/lib/packwerk/unresolved_reference.rb +9 -1
- data/lib/packwerk/validator/result.rb +18 -0
- data/lib/packwerk/validator.rb +90 -0
- data/lib/packwerk/validators/dependency_validator.rb +154 -0
- data/lib/packwerk/version.rb +1 -1
- data/lib/packwerk.rb +64 -26
- data/packwerk.gemspec +4 -2
- data/sorbet/rbi/gems/{zeitwerk@2.6.0.rbi → zeitwerk@2.6.4.rbi} +291 -228
- data/sorbet/rbi/shims/minitest/test.rb +8 -0
- data/sorbet/rbi/shims/packwerk/reference.rbi +33 -0
- data/sorbet/rbi/shims/packwerk/unresolved_reference.rbi +33 -0
- data/sorbet/rbi/shims/parser.rbi +13 -0
- metadata +34 -15
- data/lib/packwerk/formatters/offenses_formatter.rb +0 -52
- data/lib/packwerk/reference_checking/checkers/checker.rb +0 -34
- data/lib/packwerk/reference_checking/checkers/privacy_checker.rb +0 -76
- data/lib/packwerk/result.rb +0 -9
- data/lib/packwerk/sanity_checker.rb +0 -8
- data/lib/packwerk/violation_type.rb +0 -11
- data/sorbet/rbi/gems/html_tokenizer@0.0.7.rbi +0 -46
- data/sorbet/rbi/gems/mini_portile2@2.8.0.rbi +0 -8
data/lib/packwerk/cli.rb
CHANGED
@@ -14,8 +14,8 @@ module Packwerk
|
|
14
14
|
out: T.any(StringIO, IO),
|
15
15
|
err_out: T.any(StringIO, IO),
|
16
16
|
environment: String,
|
17
|
-
style:
|
18
|
-
offenses_formatter: T.nilable(
|
17
|
+
style: OutputStyle,
|
18
|
+
offenses_formatter: T.nilable(OffensesFormatter)
|
19
19
|
).void
|
20
20
|
end
|
21
21
|
def initialize(
|
@@ -33,7 +33,7 @@ module Packwerk
|
|
33
33
|
@configuration = T.let(configuration || Configuration.from_path, Configuration)
|
34
34
|
@progress_formatter = T.let(Formatters::ProgressFormatter.new(@out, style: style), Formatters::ProgressFormatter)
|
35
35
|
@offenses_formatter = T.let(
|
36
|
-
offenses_formatter ||
|
36
|
+
offenses_formatter || @configuration.offenses_formatter,
|
37
37
|
OffensesFormatter
|
38
38
|
)
|
39
39
|
end
|
@@ -52,22 +52,13 @@ module Packwerk
|
|
52
52
|
init
|
53
53
|
when "check"
|
54
54
|
output_result(parse_run(args).check)
|
55
|
-
when "
|
56
|
-
output_result(parse_run(args).detect_stale_violations)
|
57
|
-
when "update-deprecations"
|
58
|
-
warning = <<~WARNING.squish
|
59
|
-
DEPRECATION WARNING: `update-deprecations` is deprecated in favor of
|
60
|
-
`update-todo`.
|
61
|
-
WARNING
|
62
|
-
|
63
|
-
warn(warning)
|
64
|
-
output_result(parse_run(args).update_todo)
|
65
|
-
when "update-todo"
|
66
|
-
output_result(parse_run(args).update_todo)
|
67
|
-
when "update"
|
55
|
+
when "update-todo", "update"
|
68
56
|
output_result(parse_run(args).update_todo)
|
69
57
|
when "validate"
|
70
58
|
validate(args)
|
59
|
+
when "version"
|
60
|
+
@out.puts(Packwerk::VERSION)
|
61
|
+
true
|
71
62
|
when nil, "help"
|
72
63
|
usage
|
73
64
|
else
|
@@ -89,12 +80,12 @@ module Packwerk
|
|
89
80
|
|
90
81
|
sig { returns(T::Boolean) }
|
91
82
|
def generate_configs
|
92
|
-
configuration_file =
|
83
|
+
configuration_file = Generators::ConfigurationFile.generate(
|
93
84
|
root: @configuration.root_path,
|
94
85
|
out: @out
|
95
86
|
)
|
96
87
|
|
97
|
-
root_package =
|
88
|
+
root_package = Generators::RootPackage.generate(root: @configuration.root_path, out: @out)
|
98
89
|
|
99
90
|
success = configuration_file && root_package
|
100
91
|
|
@@ -124,8 +115,9 @@ module Packwerk
|
|
124
115
|
Subcommands:
|
125
116
|
init - set up packwerk
|
126
117
|
check - run all checks
|
127
|
-
update-
|
118
|
+
update-todo - update package_todo.yml files
|
128
119
|
validate - verify integrity of packwerk and package configuration
|
120
|
+
version - output packwerk version
|
129
121
|
help - display help information about packwerk
|
130
122
|
USAGE
|
131
123
|
true
|
@@ -142,72 +134,95 @@ module Packwerk
|
|
142
134
|
params(
|
143
135
|
relative_file_paths: T::Array[String],
|
144
136
|
ignore_nested_packages: T::Boolean
|
145
|
-
).returns(FilesForProcessing
|
137
|
+
).returns(FilesForProcessing)
|
146
138
|
end
|
147
139
|
def fetch_files_to_process(relative_file_paths, ignore_nested_packages)
|
148
|
-
|
140
|
+
files_for_processing = FilesForProcessing.fetch(
|
149
141
|
relative_file_paths: relative_file_paths,
|
150
142
|
ignore_nested_packages: ignore_nested_packages,
|
151
143
|
configuration: @configuration
|
152
144
|
)
|
153
|
-
|
154
|
-
|
155
|
-
|
145
|
+
@out.puts(<<~MSG.squish) if files_for_processing.files.empty?
|
146
|
+
No files found or given.
|
147
|
+
Specify files or check the include and exclude glob in the config file.
|
148
|
+
MSG
|
149
|
+
|
150
|
+
files_for_processing
|
156
151
|
end
|
157
152
|
|
158
153
|
sig { params(_paths: T::Array[String]).returns(T::Boolean) }
|
159
154
|
def validate(_paths)
|
155
|
+
result = T.let(nil, T.nilable(Validator::Result))
|
156
|
+
|
160
157
|
@progress_formatter.started_validation do
|
161
|
-
result =
|
158
|
+
result = validator.check_all(package_set, @configuration)
|
162
159
|
|
163
160
|
list_validation_errors(result)
|
164
|
-
|
165
|
-
return result.ok?
|
166
161
|
end
|
162
|
+
|
163
|
+
T.must(result).ok?
|
167
164
|
end
|
168
165
|
|
169
166
|
sig { returns(ApplicationValidator) }
|
170
|
-
def
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
167
|
+
def validator
|
168
|
+
ApplicationValidator.new
|
169
|
+
end
|
170
|
+
|
171
|
+
sig { returns(PackageSet) }
|
172
|
+
def package_set
|
173
|
+
PackageSet.load_all_from(
|
174
|
+
@configuration.root_path,
|
175
|
+
package_pathspec: @configuration.package_paths
|
175
176
|
)
|
176
177
|
end
|
177
178
|
|
178
|
-
sig { params(result:
|
179
|
+
sig { params(result: Validator::Result).void }
|
179
180
|
def list_validation_errors(result)
|
180
181
|
@out.puts
|
181
182
|
if result.ok?
|
182
183
|
@out.puts("Validation successful 🎉")
|
183
184
|
else
|
184
185
|
@out.puts("Validation failed ❗")
|
186
|
+
@out.puts
|
185
187
|
@out.puts(result.error_value)
|
186
188
|
end
|
187
189
|
end
|
188
190
|
|
189
|
-
sig { params(
|
190
|
-
def parse_run(
|
191
|
+
sig { params(args: T::Array[String]).returns(ParseRun) }
|
192
|
+
def parse_run(args)
|
191
193
|
relative_file_paths = T.let([], T::Array[String])
|
192
194
|
ignore_nested_packages = nil
|
195
|
+
formatter = @offenses_formatter
|
193
196
|
|
194
|
-
if
|
197
|
+
if args.any? { |arg| arg.include?("--packages") }
|
195
198
|
OptionParser.new do |parser|
|
196
199
|
parser.on("--packages=PACKAGESLIST", Array, "package names, comma separated") do |p|
|
197
200
|
relative_file_paths = p
|
198
201
|
end
|
199
|
-
end.parse!(
|
202
|
+
end.parse!(args)
|
200
203
|
ignore_nested_packages = true
|
201
204
|
else
|
202
|
-
relative_file_paths =
|
205
|
+
relative_file_paths = args
|
203
206
|
ignore_nested_packages = false
|
204
207
|
end
|
205
208
|
|
209
|
+
if args.any? { |arg| arg.include?("--offenses-formatter") }
|
210
|
+
OptionParser.new do |parser|
|
211
|
+
parser.on("--offenses-formatter=FORMATTER", String,
|
212
|
+
"identifier of offenses formatter to use") do |formatter_identifier|
|
213
|
+
formatter = OffensesFormatter.find(formatter_identifier)
|
214
|
+
end
|
215
|
+
end.parse!(args)
|
216
|
+
end
|
217
|
+
|
218
|
+
files_for_processing = fetch_files_to_process(relative_file_paths, ignore_nested_packages)
|
219
|
+
|
206
220
|
ParseRun.new(
|
207
|
-
relative_file_set:
|
221
|
+
relative_file_set: files_for_processing.files,
|
222
|
+
file_set_specified: files_for_processing.files_specified?,
|
208
223
|
configuration: @configuration,
|
209
224
|
progress_formatter: @progress_formatter,
|
210
|
-
offenses_formatter:
|
225
|
+
offenses_formatter: formatter
|
211
226
|
)
|
212
227
|
end
|
213
228
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require "pathname"
|
@@ -6,7 +6,12 @@ require "yaml"
|
|
6
6
|
|
7
7
|
module Packwerk
|
8
8
|
class Configuration
|
9
|
+
extend T::Sig
|
10
|
+
|
9
11
|
class << self
|
12
|
+
extend T::Sig
|
13
|
+
|
14
|
+
sig { params(path: String).returns(Configuration) }
|
10
15
|
def from_path(path = Dir.pwd)
|
11
16
|
raise ArgumentError, "#{File.expand_path(path)} does not exist" unless File.exist?(path)
|
12
17
|
|
@@ -21,6 +26,7 @@ module Packwerk
|
|
21
26
|
|
22
27
|
private
|
23
28
|
|
29
|
+
sig { params(path: String).returns(Configuration) }
|
24
30
|
def from_packwerk_config(path)
|
25
31
|
new(
|
26
32
|
YAML.load_file(path) || {},
|
@@ -30,63 +36,78 @@ module Packwerk
|
|
30
36
|
end
|
31
37
|
|
32
38
|
DEFAULT_CONFIG_PATH = "packwerk.yml"
|
33
|
-
DEFAULT_INCLUDE_GLOBS = ["**/*.{rb,rake,erb}"]
|
34
|
-
DEFAULT_EXCLUDE_GLOBS = ["{bin,node_modules,script,tmp,vendor}/**/*"]
|
39
|
+
DEFAULT_INCLUDE_GLOBS = T.let(["**/*.{rb,rake,erb}"], T::Array[String])
|
40
|
+
DEFAULT_EXCLUDE_GLOBS = T.let(["{bin,node_modules,script,tmp,vendor}/**/*"], T::Array[String])
|
35
41
|
|
36
|
-
|
37
|
-
|
38
|
-
)
|
42
|
+
sig { returns(T::Array[String]) }
|
43
|
+
attr_reader(:include)
|
39
44
|
|
40
|
-
|
41
|
-
|
42
|
-
@exclude = configs["exclude"] || DEFAULT_EXCLUDE_GLOBS
|
43
|
-
root = config_path ? File.dirname(config_path) : "."
|
44
|
-
@root_path = File.expand_path(root)
|
45
|
-
@package_paths = configs["package_paths"] || "**/"
|
46
|
-
@custom_associations = configs["custom_associations"] || []
|
47
|
-
@parallel = configs.key?("parallel") ? configs["parallel"] : true
|
48
|
-
@cache_enabled = configs.key?("cache") ? configs["cache"] : false
|
49
|
-
@cache_directory = Pathname.new(configs["cache_directory"] || "tmp/cache/packwerk")
|
50
|
-
@config_path = config_path
|
45
|
+
sig { returns(T::Array[String]) }
|
46
|
+
attr_reader(:exclude)
|
51
47
|
|
52
|
-
|
53
|
-
|
54
|
-
DEPRECATION WARNING: The 'load_paths' key in `packwerk.yml` is deprecated.
|
55
|
-
This value is no longer cached, and you can remove the key from `packwerk.yml`.
|
56
|
-
WARNING
|
48
|
+
sig { returns(String) }
|
49
|
+
attr_reader(:root_path)
|
57
50
|
|
58
|
-
|
59
|
-
|
51
|
+
sig { returns(T.any(String, T::Array[String])) }
|
52
|
+
attr_reader(:package_paths)
|
60
53
|
|
61
|
-
|
62
|
-
|
63
|
-
DEPRECATION WARNING: The 'inflections_file' key in `packwerk.yml` is deprecated.
|
64
|
-
This value is no longer cached, and you can remove the key from `packwerk.yml`.
|
65
|
-
You can also delete #{configs["inflections_file"]}.
|
66
|
-
WARNING
|
54
|
+
sig { returns(T::Array[Symbol]) }
|
55
|
+
attr_reader(:custom_associations)
|
67
56
|
|
68
|
-
|
69
|
-
|
57
|
+
sig { returns(T.nilable(String)) }
|
58
|
+
attr_reader(:config_path)
|
59
|
+
|
60
|
+
sig { returns(Pathname) }
|
61
|
+
attr_reader(:cache_directory)
|
70
62
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
63
|
+
sig do
|
64
|
+
params(
|
65
|
+
configs: T::Hash[String, T.untyped],
|
66
|
+
config_path: T.nilable(String),
|
67
|
+
).void
|
68
|
+
end
|
69
|
+
def initialize(configs = {}, config_path: nil)
|
70
|
+
@include = T.let(configs["include"] || DEFAULT_INCLUDE_GLOBS, T::Array[String])
|
71
|
+
@exclude = T.let(configs["exclude"] || DEFAULT_EXCLUDE_GLOBS, T::Array[String])
|
72
|
+
root = config_path ? File.dirname(config_path) : "."
|
73
|
+
@root_path = T.let(File.expand_path(root), String)
|
74
|
+
@package_paths = T.let(configs["package_paths"] || "**/", T.any(String, T::Array[String]))
|
75
|
+
@custom_associations = T.let(configs["custom_associations"] || [], T::Array[Symbol])
|
76
|
+
@parallel = T.let(configs.key?("parallel") ? configs["parallel"] : true, T::Boolean)
|
77
|
+
@cache_enabled = T.let(configs.key?("cache") ? configs["cache"] : false, T::Boolean)
|
78
|
+
@cache_directory = T.let(Pathname.new(configs["cache_directory"] || "tmp/cache/packwerk"), Pathname)
|
79
|
+
@config_path = config_path
|
77
80
|
|
78
|
-
|
81
|
+
@offenses_formatter_identifier = T.let(
|
82
|
+
configs["offenses_formatter"] || Formatters::DefaultOffensesFormatter::IDENTIFIER, String
|
83
|
+
)
|
84
|
+
|
85
|
+
if configs.key?("require")
|
86
|
+
configs["require"].each do |require_directive|
|
87
|
+
ExtensionLoader.load(require_directive, @root_path)
|
88
|
+
end
|
79
89
|
end
|
80
90
|
end
|
81
91
|
|
92
|
+
sig { returns(T::Hash[String, Module]) }
|
82
93
|
def load_paths
|
83
|
-
@load_paths ||=
|
94
|
+
@load_paths ||= T.let(
|
95
|
+
RailsLoadPaths.for(@root_path, environment: "test"),
|
96
|
+
T.nilable(T::Hash[String, Module]),
|
97
|
+
)
|
84
98
|
end
|
85
99
|
|
100
|
+
sig { returns(T::Boolean) }
|
86
101
|
def parallel?
|
87
102
|
@parallel
|
88
103
|
end
|
89
104
|
|
105
|
+
sig { returns(OffensesFormatter) }
|
106
|
+
def offenses_formatter
|
107
|
+
OffensesFormatter.find(@offenses_formatter_identifier)
|
108
|
+
end
|
109
|
+
|
110
|
+
sig { returns(T::Boolean) }
|
90
111
|
def cache_enabled?
|
91
112
|
@cache_enabled
|
92
113
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require "constant_resolver"
|
@@ -17,8 +17,6 @@ module Packwerk
|
|
17
17
|
class ConstantDiscovery
|
18
18
|
extend T::Sig
|
19
19
|
|
20
|
-
ConstantContext = Struct.new(:name, :location, :package, :public?)
|
21
|
-
|
22
20
|
# @param constant_resolver [ConstantResolver]
|
23
21
|
# @param packages [Packwerk::PackageSet]
|
24
22
|
sig do
|
@@ -50,12 +48,12 @@ module Packwerk
|
|
50
48
|
# @param const_name [String] The unresolved constant's name.
|
51
49
|
# @param current_namespace_path [Array<String>] (optional) The namespace of the context in which the constant is
|
52
50
|
# used, e.g. ["Apps", "Models"] for `Apps::Models`. Defaults to [] which means top level.
|
53
|
-
# @return [
|
51
|
+
# @return [ConstantContext]
|
54
52
|
sig do
|
55
53
|
params(
|
56
54
|
const_name: String,
|
57
55
|
current_namespace_path: T.nilable(T::Array[String]),
|
58
|
-
).returns(T.nilable(
|
56
|
+
).returns(T.nilable(ConstantContext))
|
59
57
|
end
|
60
58
|
def context_for(const_name, current_namespace_path: [])
|
61
59
|
begin
|
@@ -71,8 +69,9 @@ module Packwerk
|
|
71
69
|
constant.name,
|
72
70
|
constant.location,
|
73
71
|
package,
|
74
|
-
package.public_path?(constant.location),
|
75
72
|
)
|
76
73
|
end
|
77
74
|
end
|
75
|
+
|
76
|
+
private_constant :ConstantDiscovery
|
78
77
|
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# typed: true
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "sorbet-runtime"
|
5
|
+
|
6
|
+
begin
|
7
|
+
T::Configuration.default_checked_level = :never
|
8
|
+
|
9
|
+
T.singleton_class.prepend(
|
10
|
+
Module.new do
|
11
|
+
def cast(value, type, checked: true)
|
12
|
+
value
|
13
|
+
end
|
14
|
+
|
15
|
+
def let(value, type, checked: true)
|
16
|
+
value
|
17
|
+
end
|
18
|
+
|
19
|
+
def must(arg)
|
20
|
+
arg
|
21
|
+
end
|
22
|
+
|
23
|
+
def absurd(value)
|
24
|
+
value
|
25
|
+
end
|
26
|
+
|
27
|
+
def bind(value, type, checked: true)
|
28
|
+
value
|
29
|
+
end
|
30
|
+
end
|
31
|
+
)
|
32
|
+
rescue RuntimeError => error
|
33
|
+
# From https://github.com/sorbet/sorbet/blob/dcf1b069cfb0d6624c027e45e59f4c6ca33de970/gems/sorbet-runtime/lib/types/private/runtime_levels.rb#L54
|
34
|
+
# Sorbet has already evaluated a method call somewhere, so we can't disable it.
|
35
|
+
# In this case, we want to log a warning so Packwerk can still be used (but will be slower).
|
36
|
+
if /Set the default checked level earlier./.match?(error.message)
|
37
|
+
warn("Packwerk couldn't disable Sorbet. Please ensure it isn't being used before Packwerk is loaded.")
|
38
|
+
else
|
39
|
+
raise error
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Packwerk
|
5
|
+
# This class handles loading extensions to packwerk using the `require` directive
|
6
|
+
# in the `packwerk.yml` configuration.
|
7
|
+
module ExtensionLoader
|
8
|
+
class << self
|
9
|
+
extend T::Sig
|
10
|
+
sig { params(require_directive: String, config_dir_path: String).void }
|
11
|
+
def load(require_directive, config_dir_path)
|
12
|
+
# We want to transform the require directive to behave differently
|
13
|
+
# if it's a specific local file being required versus a gem
|
14
|
+
if require_directive.start_with?(".")
|
15
|
+
require File.join(config_dir_path, require_directive)
|
16
|
+
else
|
17
|
+
require require_directive
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
private_constant :ExtensionLoader
|
24
|
+
end
|
@@ -64,7 +64,7 @@ module Packwerk
|
|
64
64
|
references = []
|
65
65
|
|
66
66
|
node_processor = @node_processor_factory.for(relative_file: relative_file, node: node)
|
67
|
-
node_visitor =
|
67
|
+
node_visitor = NodeVisitor.new(node_processor: node_processor)
|
68
68
|
node_visitor.visit(node, ancestors: [], result: references)
|
69
69
|
|
70
70
|
references
|
@@ -82,4 +82,6 @@ module Packwerk
|
|
82
82
|
@parser_factory.for_path(file_path)
|
83
83
|
end
|
84
84
|
end
|
85
|
+
|
86
|
+
private_constant :FileProcessor
|
85
87
|
end
|
@@ -15,10 +15,10 @@ module Packwerk
|
|
15
15
|
relative_file_paths: T::Array[String],
|
16
16
|
configuration: Configuration,
|
17
17
|
ignore_nested_packages: T::Boolean
|
18
|
-
).returns(
|
18
|
+
).returns(FilesForProcessing)
|
19
19
|
end
|
20
20
|
def fetch(relative_file_paths:, configuration:, ignore_nested_packages: false)
|
21
|
-
new(relative_file_paths, configuration, ignore_nested_packages)
|
21
|
+
new(relative_file_paths, configuration, ignore_nested_packages)
|
22
22
|
end
|
23
23
|
end
|
24
24
|
|
@@ -33,37 +33,48 @@ module Packwerk
|
|
33
33
|
@relative_file_paths = relative_file_paths
|
34
34
|
@configuration = configuration
|
35
35
|
@ignore_nested_packages = ignore_nested_packages
|
36
|
-
@
|
36
|
+
@specified_files = T.let(nil, T.nilable(RelativeFileSet))
|
37
|
+
@files = T.let(nil, T.nilable(RelativeFileSet))
|
37
38
|
end
|
38
39
|
|
39
40
|
sig { returns(RelativeFileSet) }
|
40
41
|
def files
|
41
|
-
|
42
|
+
@files ||= files_for_processing
|
43
|
+
end
|
44
|
+
|
45
|
+
sig { returns(T::Boolean) }
|
46
|
+
def files_specified?
|
47
|
+
specified_files.any?
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
sig { returns(RelativeFileSet) }
|
53
|
+
def files_for_processing
|
54
|
+
all_included_files = if specified_files.empty?
|
42
55
|
configured_included_files
|
43
56
|
else
|
44
|
-
configured_included_files &
|
57
|
+
configured_included_files & specified_files
|
45
58
|
end
|
46
59
|
|
47
|
-
|
60
|
+
all_included_files - configured_excluded_files
|
48
61
|
end
|
49
62
|
|
50
|
-
private
|
51
|
-
|
52
63
|
sig { returns(RelativeFileSet) }
|
53
|
-
def
|
54
|
-
@
|
64
|
+
def specified_files
|
65
|
+
@specified_files ||= Set.new(
|
55
66
|
@relative_file_paths.map do |relative_file_path|
|
56
67
|
if File.file?(relative_file_path)
|
57
68
|
relative_file_path
|
58
69
|
else
|
59
|
-
|
70
|
+
specified_included_files(relative_file_path)
|
60
71
|
end
|
61
72
|
end
|
62
73
|
).flatten
|
63
74
|
end
|
64
75
|
|
65
76
|
sig { params(relative_file_path: String).returns(RelativeFileSet) }
|
66
|
-
def
|
77
|
+
def specified_included_files(relative_file_path)
|
67
78
|
# Note, assuming include globs are always relative paths
|
68
79
|
relative_includes = @configuration.include
|
69
80
|
relative_files = Dir.glob([File.join(relative_file_path, "**", "*")]).select do |relative_path|
|
@@ -100,4 +111,6 @@ module Packwerk
|
|
100
111
|
Set.new(relative_globs.flat_map { |glob| Dir[glob] })
|
101
112
|
end
|
102
113
|
end
|
114
|
+
|
115
|
+
private_constant :FilesForProcessing
|
103
116
|
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Packwerk
|
5
|
+
module Formatters
|
6
|
+
class DefaultOffensesFormatter
|
7
|
+
include OffensesFormatter
|
8
|
+
|
9
|
+
IDENTIFIER = T.let("default", String)
|
10
|
+
|
11
|
+
extend T::Sig
|
12
|
+
|
13
|
+
sig { override.params(offenses: T::Array[T.nilable(Offense)]).returns(String) }
|
14
|
+
def show_offenses(offenses)
|
15
|
+
return "No offenses detected" if offenses.empty?
|
16
|
+
|
17
|
+
<<~EOS
|
18
|
+
#{offenses_list(offenses)}
|
19
|
+
#{offenses_summary(offenses)}
|
20
|
+
EOS
|
21
|
+
end
|
22
|
+
|
23
|
+
sig { override.params(offense_collection: OffenseCollection, fileset: T::Set[String]).returns(String) }
|
24
|
+
def show_stale_violations(offense_collection, fileset)
|
25
|
+
if offense_collection.stale_violations?(fileset)
|
26
|
+
"There were stale violations found, please run `packwerk update-todo`"
|
27
|
+
else
|
28
|
+
"No stale violations detected"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
sig { override.returns(String) }
|
33
|
+
def identifier
|
34
|
+
IDENTIFIER
|
35
|
+
end
|
36
|
+
|
37
|
+
sig { override.params(strict_mode_violations: T::Array[ReferenceOffense]).returns(String) }
|
38
|
+
def show_strict_mode_violations(strict_mode_violations)
|
39
|
+
if strict_mode_violations.any?
|
40
|
+
strict_mode_violations.compact.map { |offense| format_strict_mode_violation(offense) }.join("\n")
|
41
|
+
else
|
42
|
+
""
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
sig { returns(OutputStyle) }
|
49
|
+
def style
|
50
|
+
@style ||= T.let(Packwerk::OutputStyles::Coloured.new, T.nilable(Packwerk::OutputStyles::Coloured))
|
51
|
+
end
|
52
|
+
|
53
|
+
sig { params(offense: ReferenceOffense).returns(String) }
|
54
|
+
def format_strict_mode_violation(offense)
|
55
|
+
reference_package = offense.reference.package
|
56
|
+
defining_package = offense.reference.constant.package
|
57
|
+
"#{reference_package} cannot have #{offense.violation_type} violations on #{defining_package} "\
|
58
|
+
"because strict mode is enabled for #{offense.violation_type} violations in "\
|
59
|
+
"the enforcing package's package.yml"
|
60
|
+
end
|
61
|
+
|
62
|
+
sig { params(offenses: T::Array[T.nilable(Offense)]).returns(String) }
|
63
|
+
def offenses_list(offenses)
|
64
|
+
offenses
|
65
|
+
.compact
|
66
|
+
.map { |offense| offense.to_s(style) }
|
67
|
+
.join("\n")
|
68
|
+
end
|
69
|
+
|
70
|
+
sig { params(offenses: T::Array[T.nilable(Offense)]).returns(String) }
|
71
|
+
def offenses_summary(offenses)
|
72
|
+
offenses_string = "offense".pluralize(offenses.length)
|
73
|
+
"#{offenses.length} #{offenses_string} detected"
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|