packwerk 3.0.1 → 3.1.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 +36 -8
- data/.ruby-version +1 -1
- data/Gemfile.lock +11 -6
- data/README.md +4 -2
- data/Rakefile +10 -1
- data/USAGE.md +6 -0
- data/dev.yml +1 -1
- data/lib/packwerk/application_validator.rb +3 -0
- data/lib/packwerk/checker.rb +13 -4
- data/lib/packwerk/cli.rb +14 -177
- data/lib/packwerk/commands/base_command.rb +69 -0
- data/lib/packwerk/commands/check_command.rb +60 -0
- data/lib/packwerk/commands/help_command.rb +33 -0
- data/lib/packwerk/commands/init_command.rb +42 -0
- data/lib/packwerk/commands/lazy_loaded_entry.rb +37 -0
- data/lib/packwerk/commands/update_todo_command.rb +60 -0
- data/lib/packwerk/commands/uses_parse_run.rb +92 -0
- data/lib/packwerk/commands/validate_command.rb +46 -0
- data/lib/packwerk/commands/version_command.rb +18 -0
- data/lib/packwerk/commands.rb +54 -0
- data/lib/packwerk/configuration.rb +4 -1
- data/lib/packwerk/file_processor.rb +12 -1
- data/lib/packwerk/formatters/default_offenses_formatter.rb +3 -3
- data/lib/packwerk/formatters/progress_formatter.rb +11 -0
- data/lib/packwerk/generators/templates/packwerk.yml.erb +1 -1
- data/lib/packwerk/offense_collection.rb +32 -12
- data/lib/packwerk/offenses_formatter.rb +13 -4
- data/lib/packwerk/package_todo.rb +87 -60
- data/lib/packwerk/parse_run.rb +42 -82
- data/lib/packwerk/validator.rb +18 -4
- data/lib/packwerk/version.rb +1 -1
- data/lib/packwerk.rb +4 -28
- data/sorbet/rbi/gems/parser@3.2.2.0.rbi +7250 -0
- metadata +14 -5
- data/lib/packwerk/cli/result.rb +0 -11
- data/sorbet/rbi/gems/parser@3.1.2.1.rbi +0 -9029
@@ -0,0 +1,37 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Packwerk
|
5
|
+
module Commands
|
6
|
+
class LazyLoadedEntry
|
7
|
+
extend T::Sig
|
8
|
+
|
9
|
+
sig { returns(String) }
|
10
|
+
attr_reader :name
|
11
|
+
|
12
|
+
sig { params(name: String, aliases: T::Array[String]).void }
|
13
|
+
def initialize(name, aliases: [])
|
14
|
+
@name = name
|
15
|
+
@aliases = aliases
|
16
|
+
end
|
17
|
+
|
18
|
+
sig { returns(T.class_of(BaseCommand)) }
|
19
|
+
def command_class
|
20
|
+
classname = @name.sub(" ", "_").underscore.classify + "Command"
|
21
|
+
Commands.const_get(classname) # rubocop:disable Sorbet/ConstantsFromStrings
|
22
|
+
end
|
23
|
+
|
24
|
+
sig { returns(String) }
|
25
|
+
def description
|
26
|
+
command_class.description
|
27
|
+
end
|
28
|
+
|
29
|
+
sig { params(name_or_alias: String).returns(T::Boolean) }
|
30
|
+
def matches_command?(name_or_alias)
|
31
|
+
@name == name_or_alias || @aliases.include?(name_or_alias)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
private_constant :LazyLoadedEntry
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Packwerk
|
5
|
+
module Commands
|
6
|
+
class UpdateTodoCommand < BaseCommand
|
7
|
+
extend T::Sig
|
8
|
+
include UsesParseRun
|
9
|
+
|
10
|
+
description "update package_todo.yml files"
|
11
|
+
|
12
|
+
sig { override.returns(T::Boolean) }
|
13
|
+
def run
|
14
|
+
if @files_for_processing.files_specified?
|
15
|
+
out.puts(<<~MSG.squish)
|
16
|
+
⚠️ update-todo must be called without any file arguments.
|
17
|
+
MSG
|
18
|
+
|
19
|
+
return false
|
20
|
+
end
|
21
|
+
if @files_for_processing.files.empty?
|
22
|
+
out.puts(<<~MSG.squish)
|
23
|
+
No files found or given.
|
24
|
+
Specify files or check the include and exclude glob in the config file.
|
25
|
+
MSG
|
26
|
+
|
27
|
+
return true
|
28
|
+
end
|
29
|
+
|
30
|
+
run_context = RunContext.from_configuration(configuration)
|
31
|
+
offenses = T.let([], T::Array[Offense])
|
32
|
+
progress_formatter.started_inspection(@files_for_processing.files) do
|
33
|
+
offenses = parse_run.find_offenses(run_context, on_interrupt: -> { progress_formatter.interrupted }) do
|
34
|
+
progress_formatter.increment_progress
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
offense_collection = OffenseCollection.new(configuration.root_path)
|
39
|
+
offense_collection.add_offenses(offenses)
|
40
|
+
offense_collection.persist_package_todo_files(run_context.package_set)
|
41
|
+
|
42
|
+
unlisted_strict_mode_violations = offense_collection.unlisted_strict_mode_violations
|
43
|
+
|
44
|
+
messages = [
|
45
|
+
offenses_formatter.show_offenses(offense_collection.errors + unlisted_strict_mode_violations),
|
46
|
+
]
|
47
|
+
|
48
|
+
messages << if unlisted_strict_mode_violations.any?
|
49
|
+
"⚠️ `package_todo.yml` has been updated, but unlisted strict mode violations were not added."
|
50
|
+
else
|
51
|
+
"✅ `package_todo.yml` has been updated."
|
52
|
+
end
|
53
|
+
|
54
|
+
out.puts(messages.select(&:present?).join("\n") + "\n")
|
55
|
+
|
56
|
+
unlisted_strict_mode_violations.empty? && offense_collection.errors.empty?
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "optparse"
|
5
|
+
|
6
|
+
module Packwerk
|
7
|
+
module Commands
|
8
|
+
module UsesParseRun
|
9
|
+
extend T::Sig
|
10
|
+
extend T::Helpers
|
11
|
+
|
12
|
+
requires_ancestor { BaseCommand }
|
13
|
+
|
14
|
+
sig do
|
15
|
+
params(
|
16
|
+
args: T::Array[String],
|
17
|
+
configuration: Configuration,
|
18
|
+
out: T.any(StringIO, IO),
|
19
|
+
err_out: T.any(StringIO, IO),
|
20
|
+
progress_formatter: Formatters::ProgressFormatter,
|
21
|
+
offenses_formatter: OffensesFormatter,
|
22
|
+
).void
|
23
|
+
end
|
24
|
+
def initialize(args, configuration:, out:, err_out:, progress_formatter:, offenses_formatter:)
|
25
|
+
super
|
26
|
+
@files_for_processing = T.let(fetch_files_to_process, FilesForProcessing)
|
27
|
+
@offenses_formatter = T.let(offenses_formatter_from_options || @offenses_formatter, OffensesFormatter)
|
28
|
+
configuration.parallel = parsed_options[:parallel]
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
sig { returns(FilesForProcessing) }
|
34
|
+
def fetch_files_to_process
|
35
|
+
FilesForProcessing.fetch(
|
36
|
+
relative_file_paths: parsed_options[:relative_file_paths],
|
37
|
+
ignore_nested_packages: parsed_options[:ignore_nested_packages],
|
38
|
+
configuration: configuration
|
39
|
+
)
|
40
|
+
end
|
41
|
+
|
42
|
+
sig { returns(T.nilable(OffensesFormatter)) }
|
43
|
+
def offenses_formatter_from_options
|
44
|
+
OffensesFormatter.find(parsed_options[:formatter_name]) if parsed_options[:formatter_name]
|
45
|
+
end
|
46
|
+
|
47
|
+
sig { returns(ParseRun) }
|
48
|
+
def parse_run
|
49
|
+
ParseRun.new(
|
50
|
+
relative_file_set: @files_for_processing.files,
|
51
|
+
parallel: configuration.parallel?,
|
52
|
+
)
|
53
|
+
end
|
54
|
+
|
55
|
+
sig { returns(T::Hash[Symbol, T.untyped]) }
|
56
|
+
def parsed_options
|
57
|
+
return @parsed_options if @parsed_options
|
58
|
+
|
59
|
+
@parsed_options = T.let(nil, T.nilable(T::Hash[Symbol, T.untyped]))
|
60
|
+
|
61
|
+
@parsed_options = {
|
62
|
+
relative_file_paths: T.let([], T::Array[String]),
|
63
|
+
ignore_nested_packages: T.let(false, T::Boolean),
|
64
|
+
formatter_name: T.let(nil, T.nilable(String)),
|
65
|
+
parallel: T.let(configuration.parallel?, T::Boolean),
|
66
|
+
}
|
67
|
+
|
68
|
+
OptionParser.new do |parser|
|
69
|
+
parser.on("--packages=PACKAGESLIST", Array, "package names, comma separated") do |p|
|
70
|
+
@parsed_options[:relative_file_paths] = p
|
71
|
+
@parsed_options[:ignore_nested_packages] = true
|
72
|
+
end
|
73
|
+
|
74
|
+
parser.on("--offenses-formatter=FORMATTER", String,
|
75
|
+
"identifier of offenses formatter to use") do |formatter_name|
|
76
|
+
@parsed_options[:formatter_name] = formatter_name
|
77
|
+
end
|
78
|
+
|
79
|
+
parser.on("--[no-]parallel", TrueClass, "parallel processing") do |parallel|
|
80
|
+
@parsed_options[:parallel] = parallel
|
81
|
+
end
|
82
|
+
end.parse!(args)
|
83
|
+
|
84
|
+
@parsed_options[:relative_file_paths] = args if @parsed_options[:relative_file_paths].empty?
|
85
|
+
|
86
|
+
@parsed_options
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
private_constant :UsesParseRun
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Packwerk
|
5
|
+
module Commands
|
6
|
+
class ValidateCommand < BaseCommand
|
7
|
+
extend T::Sig
|
8
|
+
|
9
|
+
description "verify integrity of packwerk and package configuration"
|
10
|
+
|
11
|
+
sig { override.returns(T::Boolean) }
|
12
|
+
def run
|
13
|
+
validator_result = T.let(nil, T.nilable(Validator::Result))
|
14
|
+
|
15
|
+
progress_formatter.started_validation do
|
16
|
+
validator_result = validator.check_all(package_set, configuration)
|
17
|
+
end
|
18
|
+
|
19
|
+
validator_result = T.must(validator_result)
|
20
|
+
|
21
|
+
if validator_result.ok?
|
22
|
+
out.puts("Validation successful 🎉")
|
23
|
+
else
|
24
|
+
out.puts("Validation failed ❗\n\n#{validator_result.error_value}")
|
25
|
+
end
|
26
|
+
|
27
|
+
validator_result.ok?
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
sig { returns(ApplicationValidator) }
|
33
|
+
def validator
|
34
|
+
ApplicationValidator.new
|
35
|
+
end
|
36
|
+
|
37
|
+
sig { returns(PackageSet) }
|
38
|
+
def package_set
|
39
|
+
PackageSet.load_all_from(
|
40
|
+
configuration.root_path,
|
41
|
+
package_pathspec: configuration.package_paths
|
42
|
+
)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Packwerk
|
5
|
+
module Commands
|
6
|
+
class VersionCommand < BaseCommand
|
7
|
+
extend T::Sig
|
8
|
+
|
9
|
+
description "output packwerk version"
|
10
|
+
|
11
|
+
sig { override.returns(T::Boolean) }
|
12
|
+
def run
|
13
|
+
out.puts(Packwerk::VERSION)
|
14
|
+
true
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Packwerk
|
5
|
+
module Commands
|
6
|
+
extend T::Sig
|
7
|
+
extend ActiveSupport::Autoload
|
8
|
+
|
9
|
+
autoload :BaseCommand
|
10
|
+
autoload :CheckCommand
|
11
|
+
autoload :HelpCommand
|
12
|
+
autoload :InitCommand
|
13
|
+
autoload :LazyLoadedEntry
|
14
|
+
autoload :UpdateTodoCommand
|
15
|
+
autoload :UsesParseRun
|
16
|
+
autoload :ValidateCommand
|
17
|
+
autoload :VersionCommand
|
18
|
+
|
19
|
+
class << self
|
20
|
+
extend T::Sig
|
21
|
+
|
22
|
+
sig { params(name: String, aliases: T::Array[String]).void }
|
23
|
+
def register(name, aliases: [])
|
24
|
+
registry << LazyLoadedEntry.new(name, aliases: aliases)
|
25
|
+
end
|
26
|
+
|
27
|
+
sig { params(name_or_alias: String).returns(T.nilable(T.class_of(BaseCommand))) }
|
28
|
+
def for(name_or_alias)
|
29
|
+
registry
|
30
|
+
.find { |command| command.matches_command?(name_or_alias) }
|
31
|
+
&.command_class
|
32
|
+
end
|
33
|
+
|
34
|
+
sig { returns(T::Array[LazyLoadedEntry]) }
|
35
|
+
def all
|
36
|
+
registry.dup
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
sig { returns(T::Array[LazyLoadedEntry]) }
|
42
|
+
def registry
|
43
|
+
@registry ||= T.let([], T.nilable(T::Array[LazyLoadedEntry]))
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
register("init")
|
48
|
+
register("check")
|
49
|
+
register("update-todo", aliases: ["update"])
|
50
|
+
register("validate")
|
51
|
+
register("version")
|
52
|
+
register("help")
|
53
|
+
end
|
54
|
+
end
|
@@ -60,6 +60,9 @@ module Packwerk
|
|
60
60
|
sig { returns(Pathname) }
|
61
61
|
attr_reader(:cache_directory)
|
62
62
|
|
63
|
+
sig { params(parallel: T::Boolean).returns(T::Boolean) }
|
64
|
+
attr_writer(:parallel)
|
65
|
+
|
63
66
|
sig do
|
64
67
|
params(
|
65
68
|
configs: T::Hash[String, T.untyped],
|
@@ -72,7 +75,7 @@ module Packwerk
|
|
72
75
|
root = config_path ? File.dirname(config_path) : "."
|
73
76
|
@root_path = T.let(File.expand_path(root), String)
|
74
77
|
@package_paths = T.let(configs["package_paths"] || "**/", T.any(String, T::Array[String]))
|
75
|
-
@custom_associations = T.let(configs["custom_associations"] || [], T::Array[Symbol])
|
78
|
+
@custom_associations = T.let((configs["custom_associations"] || []).map(&:to_sym), T::Array[Symbol])
|
76
79
|
@parallel = T.let(configs.key?("parallel") ? configs["parallel"] : true, T::Boolean)
|
77
80
|
@cache_enabled = T.let(configs.key?("cache") ? configs["cache"] : false, T::Boolean)
|
78
81
|
@cache_directory = T.let(Pathname.new(configs["cache_directory"] || "tmp/cache/packwerk"), Pathname)
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
require "ast/node"
|
4
|
+
require "parser/ast/node"
|
5
5
|
|
6
6
|
module Packwerk
|
7
7
|
class FileProcessor
|
@@ -53,6 +53,17 @@ module Packwerk
|
|
53
53
|
ProcessedFile.new(unresolved_references: unresolved_references)
|
54
54
|
rescue Parsers::ParseError => e
|
55
55
|
ProcessedFile.new(offenses: [e.result])
|
56
|
+
rescue StandardError => e
|
57
|
+
message = <<~MSG
|
58
|
+
Packwerk encountered an internal error.
|
59
|
+
For now, you can add this file to `packwerk.yml` `exclude` list.
|
60
|
+
Please file an issue and include this error message and stacktrace:
|
61
|
+
|
62
|
+
#{e.message} #{e.backtrace}"
|
63
|
+
MSG
|
64
|
+
|
65
|
+
offense = Parsers::ParseResult.new(file: relative_file, message: message)
|
66
|
+
ProcessedFile.new(offenses: [offense])
|
56
67
|
end
|
57
68
|
|
58
69
|
private
|
@@ -20,9 +20,9 @@ module Packwerk
|
|
20
20
|
EOS
|
21
21
|
end
|
22
22
|
|
23
|
-
sig { override.params(offense_collection: OffenseCollection,
|
24
|
-
def show_stale_violations(offense_collection,
|
25
|
-
if offense_collection.stale_violations?(
|
23
|
+
sig { override.params(offense_collection: OffenseCollection, file_set: T::Set[String]).returns(String) }
|
24
|
+
def show_stale_violations(offense_collection, file_set)
|
25
|
+
if offense_collection.stale_violations?(file_set)
|
26
26
|
"There were stale violations found, please run `packwerk update-todo`"
|
27
27
|
else
|
28
28
|
"No stale violations detected"
|
@@ -30,6 +30,15 @@ module Packwerk
|
|
30
30
|
finished(execution_time)
|
31
31
|
end
|
32
32
|
|
33
|
+
sig { params(failed: T::Boolean).void }
|
34
|
+
def increment_progress(failed = false)
|
35
|
+
if failed
|
36
|
+
mark_as_failed
|
37
|
+
else
|
38
|
+
mark_as_inspected
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
33
42
|
sig { void }
|
34
43
|
def mark_as_inspected
|
35
44
|
@out.print(".")
|
@@ -44,6 +53,7 @@ module Packwerk
|
|
44
53
|
def interrupted
|
45
54
|
@out.puts
|
46
55
|
@out.puts("Manually interrupted. Violations caught so far are listed below:")
|
56
|
+
@out.puts
|
47
57
|
end
|
48
58
|
|
49
59
|
private
|
@@ -52,6 +62,7 @@ module Packwerk
|
|
52
62
|
def finished(execution_time)
|
53
63
|
@out.puts
|
54
64
|
@out.puts("📦 Finished in #{execution_time.round(2)} seconds")
|
65
|
+
@out.puts
|
55
66
|
end
|
56
67
|
|
57
68
|
sig { void }
|
@@ -1,5 +1,5 @@
|
|
1
1
|
# See: Setting up the configuration file
|
2
|
-
# https://github.com/Shopify/packwerk/blob/main/USAGE.md#
|
2
|
+
# https://github.com/Shopify/packwerk/blob/main/USAGE.md#configuring-packwerk
|
3
3
|
|
4
4
|
# List of patterns for folder paths to include
|
5
5
|
# include:
|
@@ -11,12 +11,12 @@ module Packwerk
|
|
11
11
|
sig do
|
12
12
|
params(
|
13
13
|
root_path: String,
|
14
|
-
|
14
|
+
package_todos: T::Hash[Packwerk::Package, Packwerk::PackageTodo]
|
15
15
|
).void
|
16
16
|
end
|
17
|
-
def initialize(root_path,
|
17
|
+
def initialize(root_path, package_todos = {})
|
18
18
|
@root_path = root_path
|
19
|
-
@
|
19
|
+
@package_todos = T.let(package_todos, T::Hash[Packwerk::Package, Packwerk::PackageTodo])
|
20
20
|
@new_violations = T.let([], T::Array[Packwerk::ReferenceOffense])
|
21
21
|
@strict_mode_violations = T.let([], T::Array[Packwerk::ReferenceOffense])
|
22
22
|
@errors = T.let([], T::Array[Packwerk::Offense])
|
@@ -38,8 +38,12 @@ module Packwerk
|
|
38
38
|
def listed?(offense)
|
39
39
|
return false unless offense.is_a?(ReferenceOffense)
|
40
40
|
|
41
|
-
|
42
|
-
|
41
|
+
already_listed?(offense)
|
42
|
+
end
|
43
|
+
|
44
|
+
sig { params(offenses: T::Array[Offense]).void }
|
45
|
+
def add_offenses(offenses)
|
46
|
+
offenses.each { |offense| add_offense(offense) }
|
43
47
|
end
|
44
48
|
|
45
49
|
sig do
|
@@ -51,16 +55,21 @@ module Packwerk
|
|
51
55
|
return
|
52
56
|
end
|
53
57
|
|
54
|
-
|
55
|
-
|
56
|
-
|
58
|
+
already_listed = already_listed?(offense)
|
59
|
+
|
60
|
+
new_violations << offense unless already_listed
|
61
|
+
|
62
|
+
if strict_mode_violation?(offense)
|
63
|
+
add_to_package_todo(offense) if already_listed
|
57
64
|
strict_mode_violations << offense
|
65
|
+
else
|
66
|
+
add_to_package_todo(offense)
|
58
67
|
end
|
59
68
|
end
|
60
69
|
|
61
70
|
sig { params(for_files: T::Set[String]).returns(T::Boolean) }
|
62
71
|
def stale_violations?(for_files)
|
63
|
-
@
|
72
|
+
@package_todos.values.any? do |package_todo|
|
64
73
|
package_todo.stale_violations?(for_files)
|
65
74
|
end
|
66
75
|
end
|
@@ -76,10 +85,21 @@ module Packwerk
|
|
76
85
|
errors + new_violations
|
77
86
|
end
|
78
87
|
|
88
|
+
sig { returns(T::Array[Packwerk::ReferenceOffense]) }
|
89
|
+
def unlisted_strict_mode_violations
|
90
|
+
strict_mode_violations.reject { |offense| already_listed?(offense) }
|
91
|
+
end
|
92
|
+
|
79
93
|
private
|
80
94
|
|
81
95
|
sig { params(offense: ReferenceOffense).returns(T::Boolean) }
|
82
96
|
def already_listed?(offense)
|
97
|
+
package_todo_for(offense.reference.package).listed?(offense.reference,
|
98
|
+
violation_type: offense.violation_type)
|
99
|
+
end
|
100
|
+
|
101
|
+
sig { params(offense: ReferenceOffense).returns(T::Boolean) }
|
102
|
+
def add_to_package_todo(offense)
|
83
103
|
package_todo_for(offense.reference.package).add_entries(offense.reference,
|
84
104
|
offense.violation_type)
|
85
105
|
end
|
@@ -92,7 +112,7 @@ module Packwerk
|
|
92
112
|
|
93
113
|
sig { params(package_set: Packwerk::PackageSet).void }
|
94
114
|
def cleanup_extra_package_todo_files(package_set)
|
95
|
-
packages_without_todos = (package_set.packages.values - @
|
115
|
+
packages_without_todos = (package_set.packages.values - @package_todos.keys)
|
96
116
|
|
97
117
|
packages_without_todos.each do |package|
|
98
118
|
Packwerk::PackageTodo.new(
|
@@ -104,12 +124,12 @@ module Packwerk
|
|
104
124
|
|
105
125
|
sig { void }
|
106
126
|
def dump_package_todo_files
|
107
|
-
@
|
127
|
+
@package_todos.each_value(&:dump)
|
108
128
|
end
|
109
129
|
|
110
130
|
sig { params(package: Packwerk::Package).returns(Packwerk::PackageTodo) }
|
111
131
|
def package_todo_for(package)
|
112
|
-
@
|
132
|
+
@package_todos[package] ||= Packwerk::PackageTodo.new(
|
113
133
|
package,
|
114
134
|
package_todo_file_for(package),
|
115
135
|
)
|
@@ -22,14 +22,13 @@ module Packwerk
|
|
22
22
|
|
23
23
|
sig { params(base: Class).void }
|
24
24
|
def included(base)
|
25
|
-
|
26
|
-
@offenses_formatters ||= []
|
27
|
-
@offenses_formatters << base
|
25
|
+
offenses_formatters << base
|
28
26
|
end
|
29
27
|
|
30
28
|
sig { returns(T::Array[OffensesFormatter]) }
|
31
29
|
def all
|
32
|
-
|
30
|
+
load_defaults
|
31
|
+
T.cast(offenses_formatters.map(&:new), T::Array[OffensesFormatter])
|
33
32
|
end
|
34
33
|
|
35
34
|
sig { params(identifier: String).returns(OffensesFormatter) }
|
@@ -39,6 +38,16 @@ module Packwerk
|
|
39
38
|
|
40
39
|
private
|
41
40
|
|
41
|
+
sig { void }
|
42
|
+
def load_defaults
|
43
|
+
require("packwerk/formatters/default_offenses_formatter")
|
44
|
+
end
|
45
|
+
|
46
|
+
sig { returns(T::Array[Class]) }
|
47
|
+
def offenses_formatters
|
48
|
+
@offenses_formatters ||= T.let([], T.nilable(T::Array[Class]))
|
49
|
+
end
|
50
|
+
|
42
51
|
sig { params(name: String).returns(OffensesFormatter) }
|
43
52
|
def formatter_by_identifier(name)
|
44
53
|
@formatter_by_identifier ||= T.let(nil, T.nilable(T::Hash[String, T.nilable(OffensesFormatter)]))
|