rubocop-packs 0.0.7

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.
Files changed (53) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +84 -0
  3. data/config/default.yml +16 -0
  4. data/lib/rubocop/cop/packs/class_methods_as_public_apis.rb +71 -0
  5. data/lib/rubocop/cop/packs/namespaced_under_package_name/desired_zeitwerk_api.rb +110 -0
  6. data/lib/rubocop/cop/packs/namespaced_under_package_name.rb +119 -0
  7. data/lib/rubocop/cop/packs/require_documented_public_apis.rb +34 -0
  8. data/lib/rubocop/cop/packs/typed_public_api.rb +39 -0
  9. data/lib/rubocop/packs/inject.rb +24 -0
  10. data/lib/rubocop/packs/private.rb +11 -0
  11. data/lib/rubocop/packs.rb +18 -0
  12. data/lib/rubocop-packs.rb +15 -0
  13. data/sorbet/config +3 -0
  14. data/sorbet/rbi/gems/activesupport@7.0.4.rbi +15914 -0
  15. data/sorbet/rbi/gems/ast@2.4.2.rbi +584 -0
  16. data/sorbet/rbi/gems/coderay@1.1.3.rbi +8 -0
  17. data/sorbet/rbi/gems/concurrent-ruby@1.1.10.rbi +11263 -0
  18. data/sorbet/rbi/gems/diff-lcs@1.5.0.rbi +1079 -0
  19. data/sorbet/rbi/gems/i18n@1.12.0.rbi +2296 -0
  20. data/sorbet/rbi/gems/json@2.6.2.rbi +1547 -0
  21. data/sorbet/rbi/gems/method_source@1.0.0.rbi +8 -0
  22. data/sorbet/rbi/gems/minitest@5.16.3.rbi +1459 -0
  23. data/sorbet/rbi/gems/netrc@0.11.0.rbi +161 -0
  24. data/sorbet/rbi/gems/parallel@1.22.1.rbi +277 -0
  25. data/sorbet/rbi/gems/parse_packwerk@0.12.1.rbi +203 -0
  26. data/sorbet/rbi/gems/parser@3.1.2.1.rbi +8944 -0
  27. data/sorbet/rbi/gems/pry@0.14.1.rbi +8 -0
  28. data/sorbet/rbi/gems/rainbow@3.1.1.rbi +392 -0
  29. data/sorbet/rbi/gems/rake@13.0.6.rbi +2899 -0
  30. data/sorbet/rbi/gems/rbi@0.0.16.rbi +3007 -0
  31. data/sorbet/rbi/gems/regexp_parser@2.6.0.rbi +3498 -0
  32. data/sorbet/rbi/gems/rexml@3.2.5.rbi +4717 -0
  33. data/sorbet/rbi/gems/rspec-core@3.11.0.rbi +10934 -0
  34. data/sorbet/rbi/gems/rspec-expectations@3.11.1.rbi +8090 -0
  35. data/sorbet/rbi/gems/rspec-mocks@3.11.1.rbi +5291 -0
  36. data/sorbet/rbi/gems/rspec-support@3.11.1.rbi +1610 -0
  37. data/sorbet/rbi/gems/rspec@3.11.0.rbi +88 -0
  38. data/sorbet/rbi/gems/rubocop-ast@1.21.0.rbi +6898 -0
  39. data/sorbet/rbi/gems/rubocop-extension-generator@0.5.1.rbi +86 -0
  40. data/sorbet/rbi/gems/rubocop-sorbet@0.6.11.rbi +1002 -0
  41. data/sorbet/rbi/gems/rubocop@1.36.0.rbi +52209 -0
  42. data/sorbet/rbi/gems/ruby-progressbar@1.11.0.rbi +1239 -0
  43. data/sorbet/rbi/gems/spoom@1.1.12.rbi +2369 -0
  44. data/sorbet/rbi/gems/tapioca@0.10.2.rbi +3439 -0
  45. data/sorbet/rbi/gems/thor@1.2.1.rbi +3956 -0
  46. data/sorbet/rbi/gems/tzinfo@2.0.5.rbi +5896 -0
  47. data/sorbet/rbi/gems/unicode-display_width@2.3.0.rbi +48 -0
  48. data/sorbet/rbi/gems/unparser@0.6.5.rbi +4529 -0
  49. data/sorbet/rbi/gems/webrick@1.7.0.rbi +2586 -0
  50. data/sorbet/rbi/gems/yard-sorbet@0.7.0.rbi +389 -0
  51. data/sorbet/rbi/gems/yard@0.9.28.rbi +17775 -0
  52. data/sorbet/rbi/todo.rbi +5 -0
  53. metadata +266 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: bfc93294dd3f42e1dece0b632adba1a4a9ac811d0328c682dd121f2f11063d98
4
+ data.tar.gz: df7a631fa820a30b672461e106882e7221555ddd3f64ba690dd8f7407d172ca4
5
+ SHA512:
6
+ metadata.gz: ff8d5fbd4ab6233223b705ab097bed58aa9e45139e148c084c071a299720a12759fe2173ba6f557d5794b7e097c5dbc0ef87db3744bd47903662721fb4a8831e
7
+ data.tar.gz: cf7e59a3a2aa75057cee35ed0d5938b0c3131eff0d5c9657d9705833f26bff636ebc2d2ecf21fe69d04a49121e4772fa32457fe70225345587b3baaca77e1350
data/README.md ADDED
@@ -0,0 +1,84 @@
1
+ # rubocop-packs
2
+
3
+ A collection of Rubocop rules for modularizing ruby applications that conform to the `packs` standard.
4
+
5
+ ## Installation
6
+
7
+ Just install the `rubocop-packs` gem
8
+
9
+ ```sh
10
+ gem install rubocop-packs
11
+ ```
12
+ or, if you use `Bundler`, add this line your application's `Gemfile`:
13
+
14
+ ```ruby
15
+ gem 'rubocop-packs', require: false
16
+ ```
17
+
18
+ ## Usage
19
+
20
+ You need to tell RuboCop to load the Packs extension. There are three ways to do this:
21
+
22
+ ### RuboCop configuration file
23
+
24
+ Put this into your `.rubocop.yml`:
25
+
26
+ ```yaml
27
+ require: rubocop-packs
28
+ ```
29
+
30
+ Alternatively, use the following array notation when specifying multiple extensions:
31
+
32
+ ```yaml
33
+ require:
34
+ - rubocop-other-extension
35
+ - rubocop-packs
36
+ ```
37
+
38
+ Now you can run `rubocop` and it will automatically load the RuboCop Packs cops together with the standard cops.
39
+
40
+ ### Command line
41
+
42
+ ```sh
43
+ rubocop --require rubocop-packs
44
+ ```
45
+
46
+ ### Rake task
47
+
48
+ ```ruby
49
+ RuboCop::RakeTask.new do |task|
50
+ task.requires << 'rubocop-packs'
51
+ end
52
+ ```
53
+
54
+ ## The Cops
55
+ All cops are located under [`lib/rubocop/cop/packs`](lib/rubocop/cop/packs), and contain examples/documentation.
56
+
57
+ In your `.rubocop.yml`, you may treat the Packs cops just like any other cop. For example:
58
+
59
+ ```yaml
60
+ Packs/NamespacedUnderPackageName:
61
+ Exclude:
62
+ - lib/example.rb
63
+ ```
64
+ ## Contributing
65
+
66
+ Bug reports and pull requests are welcome on GitHub at https://github.com/rubyatscale/rubocop-packs. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Code Of Conduct](CODE_OF_CONDUCT.MD).
67
+
68
+ To contribute a new cop, please use the supplied generator like this:
69
+
70
+ ```sh
71
+ bundle exec rake new_cop[Packs/NewCopName]
72
+ ```
73
+
74
+ which will create a skeleton cop, a skeleton spec, an entry in the default config file and will require the new cop so that it is properly exported from the gem.
75
+
76
+ Don't forget to update the documentation with:
77
+
78
+ ```sh
79
+ VERIFYING_DOCUMENTATION=1 bundle exec rake generate_cops_documentation
80
+ ```
81
+
82
+ ## License
83
+
84
+ The gem is available as open source under the terms of the [MIT License](https://github.com/Shopify/rubocop-packs/blob/main/LICENSE.txt).
@@ -0,0 +1,16 @@
1
+ Packs/ClassMethodsAsPublicApis:
2
+ Enabled: true
3
+ AcceptableParentClasses:
4
+ - T::Enum
5
+ - T::Struct
6
+ - Struct
7
+ - OpenStruct
8
+
9
+ Packs/NamespacedUnderPackageName:
10
+ Enabled: false
11
+
12
+ Packs/TypedPublicApi:
13
+ Enabled: false
14
+
15
+ Packs/RequireDocumentedPublicApis:
16
+ Enabled: false
@@ -0,0 +1,71 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module RuboCop
5
+ module Cop
6
+ module Packs
7
+ # This cop states that public API should live on class methods, which are more easily statically analyzable,
8
+ # searchable, and typically hold less state.
9
+ #
10
+ # @example
11
+ #
12
+ # # bad
13
+ # # packs/foo/app/public/foo.rb
14
+ # module Foo
15
+ # def blah
16
+ # end
17
+ # end
18
+ #
19
+ # # good
20
+ # # packs/foo/app/public/foo.rb
21
+ # module Foo
22
+ # def self.blah
23
+ # end
24
+ # end
25
+ #
26
+ # @example AcceptableParentClasses: [T::Enum, T::Struct, Struct, OpenStruct] (default)
27
+ # You can define `AcceptableParentClasses` which are a list of classes that, if inherited from, non-class methods are permitted.
28
+ # This is useful when value objects are a part of your public API.
29
+ #
30
+ # # good
31
+ # # packs/foo/app/public/foo.rb
32
+ # class Foo < T::Enum
33
+ # const :blah
34
+ # end
35
+ #
36
+ class ClassMethodsAsPublicApis < Base
37
+ extend T::Sig
38
+
39
+ sig { returns(T::Boolean) }
40
+ def support_autocorrect?
41
+ false
42
+ end
43
+
44
+ sig { params(node: T.untyped).void }
45
+ def on_def(node)
46
+ # This cop only applies for ruby files in `app/public`
47
+ return if !processed_source.file_path.include?('app/public')
48
+
49
+ # Looked at https://www.rubydoc.info/gems/rubocop/RuboCop/Cop/Lint/MissingSuper source code as inspiration for htis part.
50
+ class_node = node.each_ancestor(:class).first
51
+ module_node = node.each_ancestor(:module).first
52
+ parent_class = class_node&.parent_class || module_node&.parent
53
+
54
+ acceptable_parent_classes = cop_config['AcceptableParentClasses'] || []
55
+
56
+ # Used this PR as inspiration to check if we're within a `class << self` block
57
+ uses_implicit_static_methods = node.each_ancestor(:sclass).first&.identifier&.source == 'self'
58
+ class_is_allowed_to_have_instance_methods = acceptable_parent_classes.include?(parent_class&.const_name)
59
+ return if uses_implicit_static_methods || class_is_allowed_to_have_instance_methods
60
+
61
+ add_offense(
62
+ node.source_range,
63
+ message: format(
64
+ 'Top-level files in the public/ folder may only define class methods.'
65
+ )
66
+ )
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,110 @@
1
+ # typed: strict
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Packs
6
+ class NamespacedUnderPackageName < Base
7
+ #
8
+ # This is a private class that represents API that we would prefer to be available somehow in Zeitwerk.
9
+ #
10
+ class DesiredZeitwerkApi
11
+ extend T::Sig
12
+
13
+ class NamespaceContext < T::Struct
14
+ const :current_namespace, String
15
+ const :expected_namespace, String
16
+ const :expected_filepath, String
17
+ end
18
+
19
+ #
20
+ # For now, this API includes `package_for_path`
21
+ # If this were truly zeitwerk API, it wouldn't include any mention of packs and it would likely not need the package at all
22
+ # Since it could get the actual namespace without knowing anything about packs.
23
+ # However, we would need to pass to it the desired namespace based on the pack name for it to be able to suggest
24
+ # a desired filepath.
25
+ # Likely this means that our own cop should determine the desired namespace and pass that in
26
+ # and this can determine actual namespace and how to get to expected.
27
+ #
28
+ sig { params(relative_filename: String, package_for_path: ParsePackwerk::Package).returns(T.nilable(NamespaceContext)) }
29
+ def for_file(relative_filename, package_for_path)
30
+ package_name = package_for_path.name
31
+
32
+ # Zeitwerk establishes a standard convention by which namespaces are defined.
33
+ # The package protections namespace checker is coupled to a specific assumption about how auto-loading works.
34
+ #
35
+ # Namely, we expect the following autoload paths: `packs/**/app/**/`
36
+ # Examples:
37
+ # 1) `packs/package_1/app/public/package_1/my_constant.rb` produces constant `Package1::MyConstant`
38
+ # 2) `packs/package_1/app/services/package_1/my_service.rb` produces constant `Package1::MyService`
39
+ # 3) `packs/package_1/app/services/package_1.rb` produces constant `Package1`
40
+ # 4) `packs/package_1/app/public/package_1.rb` produces constant `Package1`
41
+ #
42
+ # Described another way, we expect any part of the directory labeled NAMESPACE to establish a portion of the fully qualified runtime constant:
43
+ # `packs/**/app/**/NAMESPACE1/NAMESPACE2/[etc]`
44
+ #
45
+ # Therefore, for our implementation, we substitute out the non-namespace producing portions of the filename to count the number of namespaces.
46
+ # Note this will *not work* properly in applications that have different assumptions about autoloading.
47
+
48
+ path_without_package_base = relative_filename.gsub(%r{#{package_name}/app/}, '')
49
+ if path_without_package_base.include?('concerns')
50
+ autoload_folder_name = path_without_package_base.split('/').first(2).join('/')
51
+ else
52
+ autoload_folder_name = path_without_package_base.split('/').first
53
+ end
54
+
55
+ remaining_file_path = path_without_package_base.gsub(%r{\A#{autoload_folder_name}/}, '')
56
+ actual_namespace = get_actual_namespace(remaining_file_path, package_name)
57
+
58
+ if relative_filename.include?('app/')
59
+ app_or_lib = 'app'
60
+ elsif relative_filename.include?('lib/')
61
+ app_or_lib = 'lib'
62
+ end
63
+
64
+ absolute_desired_path = root_pathname.join(
65
+ package_name,
66
+ T.must(app_or_lib),
67
+ T.must(autoload_folder_name),
68
+ get_package_last_name(package_for_path),
69
+ remaining_file_path
70
+ )
71
+
72
+ relative_desired_path = absolute_desired_path.relative_path_from(root_pathname)
73
+
74
+ NamespaceContext.new(
75
+ current_namespace: actual_namespace,
76
+ expected_namespace: get_pack_based_namespace(package_for_path),
77
+ expected_filepath: relative_desired_path.to_s
78
+ )
79
+ end
80
+
81
+ sig { params(pack: ParsePackwerk::Package).returns(String) }
82
+ def get_pack_based_namespace(pack)
83
+ get_package_last_name(pack).camelize
84
+ end
85
+
86
+ private
87
+
88
+ sig { returns(Pathname) }
89
+ def root_pathname
90
+ Pathname.pwd
91
+ end
92
+
93
+ sig { params(pack: ParsePackwerk::Package).returns(String) }
94
+ def get_package_last_name(pack)
95
+ T.must(pack.name.split('/').last)
96
+ end
97
+
98
+ sig { params(remaining_file_path: String, package_name: String).returns(String) }
99
+ def get_actual_namespace(remaining_file_path, package_name)
100
+ # If the remaining file path is a ruby file (not a directory), then it establishes a global namespace
101
+ # Otherwise, per Zeitwerk's conventions as listed above, its a directory that establishes another global namespace
102
+ T.must(remaining_file_path.split('/').first).gsub('.rb', '').camelize
103
+ end
104
+ end
105
+
106
+ private_constant :DesiredZeitwerkApi
107
+ end
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,119 @@
1
+ # typed: strict
2
+
3
+ # For String#camelize
4
+ require 'active_support/core_ext/string/inflections'
5
+ require 'rubocop/cop/packs/namespaced_under_package_name/desired_zeitwerk_api'
6
+
7
+ module RuboCop
8
+ module Cop
9
+ module Packs
10
+ # This cop helps ensure that each pack exposes one namespace.
11
+ #
12
+ # @example
13
+ #
14
+ # # bad
15
+ # # packs/foo/app/services/blah/bar.rb
16
+ # class Blah::Bar; end
17
+ #
18
+ # # good
19
+ # # packs/foo/app/services/foo/blah/bar.rb
20
+ # class Foo::Blah::Bar; end
21
+ #
22
+ class NamespacedUnderPackageName < Base
23
+ extend T::Sig
24
+
25
+ include RangeHelp
26
+
27
+ sig { void }
28
+ def on_new_investigation
29
+ absolute_filepath = Pathname.new(processed_source.file_path)
30
+ relative_filepath = absolute_filepath.relative_path_from(Pathname.pwd)
31
+ relative_filename = relative_filepath.to_s
32
+
33
+ # This cop only works for files ruby files in `app`
34
+ return if !relative_filename.include?('app/') || relative_filepath.extname != '.rb'
35
+
36
+ relative_filename = relative_filepath.to_s
37
+ package_for_path = ParsePackwerk.package_from_path(relative_filename)
38
+ return if package_for_path.nil?
39
+
40
+ namespace_context = desired_zeitwerk_api.for_file(relative_filename, package_for_path)
41
+ return if namespace_context.nil?
42
+
43
+ allowed_global_namespaces = Set.new([
44
+ namespace_context.expected_namespace,
45
+ *cop_config['GloballyPermittedNamespaces']
46
+ ])
47
+
48
+ package_name = package_for_path.name
49
+ actual_namespace = namespace_context.current_namespace
50
+
51
+ if allowed_global_namespaces.include?(actual_namespace)
52
+ # No problem!
53
+ else
54
+ package_enforces_namespaces = cop_config['IncludePacks'].include?(package_for_path.name)
55
+ expected_namespace = namespace_context.expected_namespace
56
+ relative_desired_path = namespace_context.expected_filepath
57
+ pack_owning_this_namespace = namespaces_to_packs[actual_namespace]
58
+
59
+ if package_enforces_namespaces
60
+ add_offense(
61
+ source_range(processed_source.buffer, 1, 0),
62
+ message: format(
63
+ '`%<package_name>s` prevents modules/classes that are not submodules of the package namespace. Should be namespaced under `%<expected_namespace>s` with path `%<expected_path>s`. See https://go/packwerk_cheatsheet_namespaces for more info.',
64
+ package_name: package_name,
65
+ expected_namespace: expected_namespace,
66
+ expected_path: relative_desired_path
67
+ )
68
+ )
69
+ elsif pack_owning_this_namespace
70
+ add_offense(
71
+ source_range(processed_source.buffer, 1, 0),
72
+ message: format(
73
+ '`%<pack_owning_this_namespace>s` prevents other packs from sitting in the `%<actual_namespace>s` namespace. This should be namespaced under `%<expected_namespace>s` with path `%<expected_path>s`. See https://go/packwerk_cheatsheet_namespaces for more info.',
74
+ package_name: package_name,
75
+ pack_owning_this_namespace: pack_owning_this_namespace,
76
+ expected_namespace: expected_namespace,
77
+ actual_namespace: actual_namespace,
78
+ expected_path: relative_desired_path
79
+ )
80
+ )
81
+ end
82
+ end
83
+ end
84
+
85
+ # In the future, we'd love this to support auto-correct.
86
+ # Perhaps by automatically renamespacing the file and changing its location?
87
+ sig { returns(T::Boolean) }
88
+ def support_autocorrect?
89
+ false
90
+ end
91
+
92
+ private
93
+
94
+ sig { returns(DesiredZeitwerkApi) }
95
+ def desired_zeitwerk_api
96
+ @desired_zeitwerk_api ||= T.let(nil, T.nilable(DesiredZeitwerkApi))
97
+ @desired_zeitwerk_api ||= DesiredZeitwerkApi.new
98
+ end
99
+
100
+ sig { returns(T::Hash[String, String]) }
101
+ def namespaces_to_packs
102
+ @namespaces_to_packs = T.let(nil, T.nilable(T::Hash[String, String]))
103
+ @namespaces_to_packs ||= begin
104
+ all_packs_enforcing_namespaces = ParsePackwerk.all.select do |p|
105
+ cop_config['IncludePacks'].include?(p.name)
106
+ end
107
+
108
+ namespaces_to_packs = {}
109
+ all_packs_enforcing_namespaces.each do |package|
110
+ namespaces_to_packs[desired_zeitwerk_api.get_pack_based_namespace(package)] = package.name
111
+ end
112
+
113
+ namespaces_to_packs
114
+ end
115
+ end
116
+ end
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,34 @@
1
+ # typed: strict
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Packs
6
+ class RequireDocumentedPublicApis < Style::DocumentationMethod
7
+ extend T::Sig
8
+
9
+ sig { returns(T::Boolean) }
10
+ def support_autocorrect?
11
+ false
12
+ end
13
+
14
+ sig { params(node: T.untyped).void }
15
+ def check(node)
16
+ # This cop only applies for ruby files in `app/public`
17
+ return if !processed_source.file_path.include?('app/public')
18
+ return if non_public?(node) && !require_for_non_public_methods?
19
+
20
+ left_sibling = node.left_sibling
21
+ left_sibling_is_sig = left_sibling && (left_sibling.source.include?('sig do') || left_sibling.source.include?('sig {'))
22
+ # Is there a better way to check if the left sibling is a sorbet signature? Probably!
23
+ if left_sibling_is_sig
24
+ return if documentation_comment?(node.left_sibling)
25
+ elsif documentation_comment?(node)
26
+ return
27
+ end
28
+
29
+ add_offense(node)
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,39 @@
1
+ # typed: strict
2
+
3
+ require 'rubocop-sorbet'
4
+
5
+ module RuboCop
6
+ module Cop
7
+ module Packs
8
+ # This cop helps ensure that each pack's public API is strictly typed, enforcing strong boundaries.
9
+ #
10
+ # @example
11
+ #
12
+ # # bad
13
+ # # packs/foo/app/public/foo.rb
14
+ # # typed: false
15
+ # module Foo; end
16
+ #
17
+ # # good
18
+ # # packs/foo/app/public/foo.rb
19
+ # # typed: strict
20
+ # module Foo; end
21
+ #
22
+ class TypedPublicApi < Sorbet::StrictSigil
23
+ #
24
+ # This inherits from `Sorbet::StrictSigil` and doesn't change any behavior of it.
25
+ # The only reason we do this is so that configuration for this cop can live under a different cop namespace.
26
+ # This prevents this cop's configuration from clashing with other configurations for the same cop.
27
+ # A concrete example of this would be if a user is using this package protection to make sure public APIs are typed,
28
+ # and separately the application as a whole requiring strict typing in certain parts of the application.
29
+ #
30
+ # To prevent problems associated with needing to manage identical configurations for the same cop, we simply call it
31
+ # something else in the context of this protection.
32
+ #
33
+ # We can apply this same pattern if we want to use other cops in the context of package protections and prevent clashing.
34
+ #
35
+ extend T::Sig
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,24 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ # The original code is from https://github.com/rubocop/rubocop-rspec/blob/master/lib/rubocop/rspec/inject.rb
5
+ # See https://github.com/rubocop/rubocop-rspec/blob/master/MIT-LICENSE.md
6
+ module RuboCop
7
+ module Packs
8
+ # Because RuboCop doesn't yet support plugins, we have to monkey patch in a
9
+ # bit of our configuration.
10
+ module Inject
11
+ extend T::Sig
12
+
13
+ sig { void }
14
+ def self.defaults!
15
+ path = CONFIG_DEFAULT.to_s
16
+ hash = ConfigLoader.send(:load_yaml_configuration, path)
17
+ config = Config.new(hash, path).tap(&:make_excludes_absolute)
18
+ puts "configuration from #{path}" if ConfigLoader.debug?
19
+ config = ConfigLoader.merge_with_default(config, path)
20
+ ConfigLoader.instance_variable_set(:@default_configuration, config)
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,11 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module RuboCop
5
+ module Packs
6
+ module Private
7
+ end
8
+
9
+ private_constant :Private
10
+ end
11
+ end
@@ -0,0 +1,18 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ require 'rubocop/packs/private'
5
+
6
+ module RuboCop
7
+ module Packs
8
+ class Error < StandardError; end
9
+ extend T::Sig
10
+
11
+ # Your code goes here...
12
+ PROJECT_ROOT = T.let(Pathname.new(__dir__).parent.parent.expand_path.freeze, Pathname)
13
+ CONFIG_DEFAULT = T.let(PROJECT_ROOT.join('config', 'default.yml').freeze, Pathname)
14
+ CONFIG = T.let(YAML.safe_load(CONFIG_DEFAULT.read).freeze, T.untyped)
15
+
16
+ private_constant(:CONFIG_DEFAULT, :PROJECT_ROOT)
17
+ end
18
+ end
@@ -0,0 +1,15 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ require 'rubocop'
5
+ require 'parse_packwerk'
6
+
7
+ require_relative 'rubocop/packs'
8
+ require_relative 'rubocop/packs/inject'
9
+
10
+ require 'rubocop/cop/packs/namespaced_under_package_name'
11
+ require 'rubocop/cop/packs/typed_public_api'
12
+ require 'rubocop/cop/packs/class_methods_as_public_apis'
13
+ require 'rubocop/cop/packs/require_documented_public_apis'
14
+
15
+ RuboCop::Packs::Inject.defaults!
data/sorbet/config ADDED
@@ -0,0 +1,3 @@
1
+ --dir
2
+ .
3
+ --ignore=/vendor/bundle