packwerk 2.0.0 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +26 -22
  3. data/README.md +13 -1
  4. data/USAGE.md +7 -0
  5. data/lib/packwerk/application_load_paths.rb +12 -18
  6. data/lib/packwerk/application_validator.rb +88 -40
  7. data/lib/packwerk/cache.rb +169 -0
  8. data/lib/packwerk/cli.rb +29 -13
  9. data/lib/packwerk/configuration.rb +17 -12
  10. data/lib/packwerk/constant_discovery.rb +20 -4
  11. data/lib/packwerk/constant_name_inspector.rb +1 -1
  12. data/lib/packwerk/deprecated_references.rb +1 -1
  13. data/lib/packwerk/file_processor.rb +43 -22
  14. data/lib/packwerk/files_for_processing.rb +55 -26
  15. data/lib/packwerk/generators/templates/packwerk.yml.erb +6 -0
  16. data/lib/packwerk/node.rb +2 -1
  17. data/lib/packwerk/node_processor.rb +6 -6
  18. data/lib/packwerk/node_processor_factory.rb +3 -4
  19. data/lib/packwerk/node_visitor.rb +3 -0
  20. data/lib/packwerk/offense.rb +10 -2
  21. data/lib/packwerk/package.rb +1 -1
  22. data/lib/packwerk/package_set.rb +4 -3
  23. data/lib/packwerk/parse_run.rb +37 -17
  24. data/lib/packwerk/parsed_constant_definitions.rb +4 -4
  25. data/lib/packwerk/parsers/erb.rb +2 -0
  26. data/lib/packwerk/parsers/factory.rb +2 -0
  27. data/lib/packwerk/parsers/parser_interface.rb +19 -0
  28. data/lib/packwerk/parsers/ruby.rb +2 -0
  29. data/lib/packwerk/parsers.rb +1 -0
  30. data/lib/packwerk/reference_checking/checkers/checker.rb +1 -1
  31. data/lib/packwerk/reference_checking/reference_checker.rb +3 -4
  32. data/lib/packwerk/reference_extractor.rb +72 -20
  33. data/lib/packwerk/reference_offense.rb +8 -3
  34. data/lib/packwerk/result.rb +2 -2
  35. data/lib/packwerk/run_context.rb +62 -36
  36. data/lib/packwerk/spring_command.rb +1 -1
  37. data/lib/packwerk/unresolved_reference.rb +10 -0
  38. data/lib/packwerk/version.rb +1 -1
  39. data/lib/packwerk.rb +2 -0
  40. data/packwerk.gemspec +4 -2
  41. data/sorbet/config +1 -0
  42. data/sorbet/rbi/gems/tapioca@0.4.19.rbi +1 -1
  43. data/sorbet/tapioca/require.rb +1 -1
  44. metadata +36 -5
@@ -1,4 +1,4 @@
1
- # typed: true
1
+ # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "constant_resolver"
@@ -8,76 +8,107 @@ module Packwerk
8
8
  class RunContext
9
9
  extend T::Sig
10
10
 
11
- attr_reader(
12
- :root_path,
13
- :load_paths,
14
- :package_paths,
15
- :inflector,
16
- :custom_associations,
17
- :checker_classes,
18
- )
19
-
20
- DEFAULT_CHECKERS = [
21
- ::Packwerk::ReferenceChecking::Checkers::DependencyChecker,
22
- ::Packwerk::ReferenceChecking::Checkers::PrivacyChecker,
23
- ]
11
+ DEFAULT_CHECKERS = T.let([
12
+ ::Packwerk::ReferenceChecking::Checkers::DependencyChecker.new,
13
+ ::Packwerk::ReferenceChecking::Checkers::PrivacyChecker.new,
14
+ ], T::Array[ReferenceChecking::Checkers::Checker])
24
15
 
25
16
  class << self
17
+ extend T::Sig
18
+
19
+ sig do
20
+ params(configuration: Configuration).returns(RunContext)
21
+ end
26
22
  def from_configuration(configuration)
27
23
  inflector = ActiveSupport::Inflector
24
+
28
25
  new(
29
26
  root_path: configuration.root_path,
30
27
  load_paths: configuration.load_paths,
31
28
  package_paths: configuration.package_paths,
32
29
  inflector: inflector,
33
- custom_associations: configuration.custom_associations
30
+ custom_associations: configuration.custom_associations,
31
+ cache_enabled: configuration.cache_enabled?,
32
+ cache_directory: configuration.cache_directory,
33
+ config_path: configuration.config_path,
34
34
  )
35
35
  end
36
36
  end
37
37
 
38
+ sig do
39
+ params(
40
+ root_path: String,
41
+ load_paths: T::Hash[String, Module],
42
+ inflector: T.class_of(ActiveSupport::Inflector),
43
+ cache_directory: Pathname,
44
+ config_path: T.nilable(String),
45
+ package_paths: T.nilable(T.any(T::Array[String], String)),
46
+ custom_associations: AssociationInspector::CustomAssociations,
47
+ checkers: T::Array[ReferenceChecking::Checkers::Checker],
48
+ cache_enabled: T::Boolean,
49
+ ).void
50
+ end
38
51
  def initialize(
39
52
  root_path:,
40
53
  load_paths:,
54
+ inflector:,
55
+ cache_directory:,
56
+ config_path: nil,
41
57
  package_paths: nil,
42
- inflector: nil,
43
58
  custom_associations: [],
44
- checker_classes: DEFAULT_CHECKERS
59
+ checkers: DEFAULT_CHECKERS,
60
+ cache_enabled: false
45
61
  )
46
62
  @root_path = root_path
47
63
  @load_paths = load_paths
48
64
  @package_paths = package_paths
49
65
  @inflector = inflector
50
66
  @custom_associations = custom_associations
51
- @checker_classes = checker_classes
67
+ @checkers = checkers
68
+ @cache_enabled = cache_enabled
69
+ @cache_directory = cache_directory
70
+ @config_path = config_path
71
+
72
+ @file_processor = T.let(nil, T.nilable(FileProcessor))
73
+ @context_provider = T.let(nil, T.nilable(ConstantDiscovery))
74
+ # We need to initialize this before we fork the process, see https://github.com/Shopify/packwerk/issues/182
75
+ @cache = T.let(
76
+ Cache.new(enable_cache: @cache_enabled, cache_directory: @cache_directory, config_path: @config_path), Cache
77
+ )
52
78
  end
53
79
 
54
- sig { params(file: String).returns(T::Array[Packwerk::Offense]) }
55
- def process_file(file:)
56
- references = file_processor.call(file)
80
+ sig { params(absolute_file: String).returns(T::Array[Packwerk::Offense]) }
81
+ def process_file(absolute_file:)
82
+ processed_file = file_processor.call(absolute_file)
83
+
84
+ references = ReferenceExtractor.get_fully_qualified_references_from(
85
+ processed_file.unresolved_references,
86
+ context_provider
87
+ )
88
+ reference_checker = ReferenceChecking::ReferenceChecker.new(@checkers)
57
89
 
58
- reference_checker = ReferenceChecking::ReferenceChecker.new(checkers)
59
- references.flat_map { |reference| reference_checker.call(reference) }
90
+ processed_file.offenses + references.flat_map { |reference| reference_checker.call(reference) }
60
91
  end
61
92
 
62
93
  private
63
94
 
64
95
  sig { returns(FileProcessor) }
65
96
  def file_processor
66
- @file_processor ||= FileProcessor.new(node_processor_factory: node_processor_factory)
97
+ @file_processor ||= FileProcessor.new(node_processor_factory: node_processor_factory, cache: @cache)
67
98
  end
68
99
 
69
100
  sig { returns(NodeProcessorFactory) }
70
101
  def node_processor_factory
71
102
  NodeProcessorFactory.new(
72
103
  context_provider: context_provider,
73
- root_path: root_path,
104
+ root_path: @root_path,
74
105
  constant_name_inspectors: constant_name_inspectors
75
106
  )
76
107
  end
77
108
 
78
109
  sig { returns(ConstantDiscovery) }
79
110
  def context_provider
80
- ::Packwerk::ConstantDiscovery.new(
111
+ @context_provider ||= ::Packwerk::ConstantDiscovery.new(
81
112
  constant_resolver: resolver,
82
113
  packages: package_set
83
114
  )
@@ -86,27 +117,22 @@ module Packwerk
86
117
  sig { returns(ConstantResolver) }
87
118
  def resolver
88
119
  ConstantResolver.new(
89
- root_path: root_path,
90
- load_paths: load_paths,
91
- inflector: inflector,
120
+ root_path: @root_path,
121
+ load_paths: @load_paths,
122
+ inflector: @inflector,
92
123
  )
93
124
  end
94
125
 
95
126
  sig { returns(PackageSet) }
96
127
  def package_set
97
- ::Packwerk::PackageSet.load_all_from(root_path, package_pathspec: package_paths)
98
- end
99
-
100
- sig { returns(T::Array[ReferenceChecking::Checkers::Checker]) }
101
- def checkers
102
- checker_classes.map(&:new)
128
+ ::Packwerk::PackageSet.load_all_from(@root_path, package_pathspec: @package_paths)
103
129
  end
104
130
 
105
131
  sig { returns(T::Array[ConstantNameInspector]) }
106
132
  def constant_name_inspectors
107
133
  [
108
134
  ::Packwerk::ConstNodeInspector.new,
109
- ::Packwerk::AssociationInspector.new(inflector: inflector, custom_associations: custom_associations),
135
+ ::Packwerk::AssociationInspector.new(inflector: @inflector, custom_associations: @custom_associations),
110
136
  ]
111
137
  end
112
138
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
- # typed: false
2
+ # typed: true
3
3
 
4
4
  require "spring/commands"
5
5
 
@@ -0,0 +1,10 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ module Packwerk
5
+ # An unresolved reference from a file in one package to a constant that may be defined in a different package.
6
+ # Unresolved means that we know how it's referred to in the file,
7
+ # and we have enough context on that reference to figure out the fully qualified reference such that we
8
+ # can produce a Reference in a separate pass. However, we have not yet resolved it to its fully qualified version.
9
+ UnresolvedReference = Struct.new(:constant_name, :namespace_path, :relative_path, :source_location)
10
+ end
@@ -2,5 +2,5 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module Packwerk
5
- VERSION = "2.0.0"
5
+ VERSION = "2.2.0"
6
6
  end
data/lib/packwerk.rb CHANGED
@@ -15,6 +15,7 @@ module Packwerk
15
15
  autoload :ApplicationValidator
16
16
  autoload :AssociationInspector
17
17
  autoload :OffenseCollection
18
+ autoload :Cache
18
19
  autoload :Cli
19
20
  autoload :Configuration
20
21
  autoload :ConstantDiscovery
@@ -36,6 +37,7 @@ module Packwerk
36
37
  autoload :ParsedConstantDefinitions
37
38
  autoload :Parsers
38
39
  autoload :ParseRun
40
+ autoload :UnresolvedReference
39
41
  autoload :Reference
40
42
  autoload :ReferenceExtractor
41
43
  autoload :ReferenceOffense
data/packwerk.gemspec CHANGED
@@ -41,16 +41,18 @@ Gem::Specification.new do |spec|
41
41
  spec.required_ruby_version = ">= 2.6"
42
42
 
43
43
  spec.add_dependency("activesupport", ">= 5.2")
44
- spec.add_dependency("constant_resolver")
44
+ spec.add_dependency("constant_resolver", ">=0.2.0")
45
45
  spec.add_dependency("parallel")
46
- spec.add_dependency("sorbet-runtime")
46
+ spec.add_dependency("sorbet-runtime", ">=0.5.9914")
47
47
  spec.add_dependency("bundler")
48
+ spec.add_dependency("digest")
48
49
 
49
50
  spec.add_development_dependency("rake")
50
51
  spec.add_development_dependency("sorbet")
51
52
  spec.add_development_dependency("m")
52
53
  # https://github.com/ruby/psych/pull/487
53
54
  spec.add_development_dependency("psych", "~> 3")
55
+ spec.add_development_dependency("zeitwerk")
54
56
 
55
57
  # For Ruby parsing
56
58
  spec.add_dependency("ast")
data/sorbet/config CHANGED
@@ -1,2 +1,3 @@
1
1
  --dir
2
2
  .
3
+ --enable-experimental-requires-ancestor
@@ -562,7 +562,7 @@ module Tapioca::GenericTypeRegistry
562
562
  def name_of(constant); end
563
563
  sig { params(object: BasicObject).returns(Integer) }
564
564
  def object_id_of(object); end
565
- sig { params(constant: T.untyped, type_variable_type: T.enum([:type_member, :type_template]), type_variable: T::Types::TypeVariable, fixed: T.untyped, lower: T.untyped, upper: T.untyped).void }
565
+ sig { params(constant: T.untyped, type_variable_type: T.deprecated_enum([:type_member, :type_template]), type_variable: T::Types::TypeVariable, fixed: T.untyped, lower: T.untyped, upper: T.untyped).void }
566
566
  def register_type_variable(constant, type_variable_type, type_variable, fixed, lower, upper); end
567
567
  sig { params(type_variable_type: Symbol, variance: Symbol, fixed: T.untyped, lower: T.untyped, upper: T.untyped).returns(String) }
568
568
  def serialize_type_variable(type_variable_type, variance, fixed, lower, upper); end
@@ -3,7 +3,7 @@
3
3
  # This is an autogenerated file for explicit gem requires.
4
4
  # Please instead update this file by running `tapioca require`.
5
5
 
6
- # typed: false
6
+ # typed: strict
7
7
 
8
8
  require "ast"
9
9
  require "ast/node"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: packwerk
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0
4
+ version: 2.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shopify Inc.
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-12-08 00:00:00.000000000 Z
11
+ date: 2022-04-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -30,14 +30,14 @@ dependencies:
30
30
  requirements:
31
31
  - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: '0'
33
+ version: 0.2.0
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
- version: '0'
40
+ version: 0.2.0
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: parallel
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -54,6 +54,20 @@ dependencies:
54
54
  version: '0'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: sorbet-runtime
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: 0.5.9914
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: 0.5.9914
69
+ - !ruby/object:Gem::Dependency
70
+ name: bundler
57
71
  requirement: !ruby/object:Gem::Requirement
58
72
  requirements:
59
73
  - - ">="
@@ -67,7 +81,7 @@ dependencies:
67
81
  - !ruby/object:Gem::Version
68
82
  version: '0'
69
83
  - !ruby/object:Gem::Dependency
70
- name: bundler
84
+ name: digest
71
85
  requirement: !ruby/object:Gem::Requirement
72
86
  requirements:
73
87
  - - ">="
@@ -136,6 +150,20 @@ dependencies:
136
150
  - - "~>"
137
151
  - !ruby/object:Gem::Version
138
152
  version: '3'
153
+ - !ruby/object:Gem::Dependency
154
+ name: zeitwerk
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - ">="
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
139
167
  - !ruby/object:Gem::Dependency
140
168
  name: ast
141
169
  requirement: !ruby/object:Gem::Requirement
@@ -222,6 +250,7 @@ files:
222
250
  - lib/packwerk/application_load_paths.rb
223
251
  - lib/packwerk/application_validator.rb
224
252
  - lib/packwerk/association_inspector.rb
253
+ - lib/packwerk/cache.rb
225
254
  - lib/packwerk/cli.rb
226
255
  - lib/packwerk/configuration.rb
227
256
  - lib/packwerk/const_node_inspector.rb
@@ -254,6 +283,7 @@ files:
254
283
  - lib/packwerk/parsers.rb
255
284
  - lib/packwerk/parsers/erb.rb
256
285
  - lib/packwerk/parsers/factory.rb
286
+ - lib/packwerk/parsers/parser_interface.rb
257
287
  - lib/packwerk/parsers/ruby.rb
258
288
  - lib/packwerk/reference.rb
259
289
  - lib/packwerk/reference_checking/checkers/checker.rb
@@ -266,6 +296,7 @@ files:
266
296
  - lib/packwerk/run_context.rb
267
297
  - lib/packwerk/sanity_checker.rb
268
298
  - lib/packwerk/spring_command.rb
299
+ - lib/packwerk/unresolved_reference.rb
269
300
  - lib/packwerk/version.rb
270
301
  - lib/packwerk/violation_type.rb
271
302
  - library.yml