packwerk 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (153) hide show
  1. checksums.yaml +7 -0
  2. data/.github/ISSUE_TEMPLATE/bug_report.md +27 -0
  3. data/.github/probots.yml +2 -0
  4. data/.github/pull_request_template.md +27 -0
  5. data/.github/workflows/ci.yml +50 -0
  6. data/.gitignore +12 -0
  7. data/.rubocop.yml +46 -0
  8. data/.ruby-version +1 -0
  9. data/CODEOWNERS +1 -0
  10. data/CODE_OF_CONDUCT.md +76 -0
  11. data/CONTRIBUTING.md +17 -0
  12. data/Gemfile +22 -0
  13. data/Gemfile.lock +236 -0
  14. data/LICENSE.md +7 -0
  15. data/README.md +73 -0
  16. data/Rakefile +13 -0
  17. data/TROUBLESHOOT.md +67 -0
  18. data/USAGE.md +250 -0
  19. data/bin/console +15 -0
  20. data/bin/setup +8 -0
  21. data/dev.yml +32 -0
  22. data/docs/cohesion.png +0 -0
  23. data/exe/packwerk +6 -0
  24. data/lib/packwerk.rb +44 -0
  25. data/lib/packwerk/application_validator.rb +343 -0
  26. data/lib/packwerk/association_inspector.rb +44 -0
  27. data/lib/packwerk/checking_deprecated_references.rb +40 -0
  28. data/lib/packwerk/cli.rb +238 -0
  29. data/lib/packwerk/configuration.rb +82 -0
  30. data/lib/packwerk/const_node_inspector.rb +44 -0
  31. data/lib/packwerk/constant_discovery.rb +60 -0
  32. data/lib/packwerk/constant_name_inspector.rb +22 -0
  33. data/lib/packwerk/dependency_checker.rb +28 -0
  34. data/lib/packwerk/deprecated_references.rb +92 -0
  35. data/lib/packwerk/file_processor.rb +43 -0
  36. data/lib/packwerk/files_for_processing.rb +67 -0
  37. data/lib/packwerk/formatters/progress_formatter.rb +46 -0
  38. data/lib/packwerk/generators/application_validation.rb +62 -0
  39. data/lib/packwerk/generators/configuration_file.rb +69 -0
  40. data/lib/packwerk/generators/inflections_file.rb +43 -0
  41. data/lib/packwerk/generators/root_package.rb +37 -0
  42. data/lib/packwerk/generators/templates/inflections.yml +6 -0
  43. data/lib/packwerk/generators/templates/package.yml +17 -0
  44. data/lib/packwerk/generators/templates/packwerk +23 -0
  45. data/lib/packwerk/generators/templates/packwerk.yml.erb +23 -0
  46. data/lib/packwerk/generators/templates/packwerk_validator_test.rb +11 -0
  47. data/lib/packwerk/graph.rb +74 -0
  48. data/lib/packwerk/inflections/custom.rb +33 -0
  49. data/lib/packwerk/inflections/default.rb +73 -0
  50. data/lib/packwerk/inflector.rb +41 -0
  51. data/lib/packwerk/node.rb +259 -0
  52. data/lib/packwerk/node_processor.rb +49 -0
  53. data/lib/packwerk/node_visitor.rb +22 -0
  54. data/lib/packwerk/offense.rb +44 -0
  55. data/lib/packwerk/output_styles.rb +41 -0
  56. data/lib/packwerk/package.rb +56 -0
  57. data/lib/packwerk/package_set.rb +59 -0
  58. data/lib/packwerk/parsed_constant_definitions.rb +62 -0
  59. data/lib/packwerk/parsers.rb +23 -0
  60. data/lib/packwerk/parsers/erb.rb +66 -0
  61. data/lib/packwerk/parsers/factory.rb +34 -0
  62. data/lib/packwerk/parsers/ruby.rb +42 -0
  63. data/lib/packwerk/privacy_checker.rb +45 -0
  64. data/lib/packwerk/reference.rb +6 -0
  65. data/lib/packwerk/reference_extractor.rb +81 -0
  66. data/lib/packwerk/reference_lister.rb +23 -0
  67. data/lib/packwerk/run_context.rb +103 -0
  68. data/lib/packwerk/sanity_checker.rb +10 -0
  69. data/lib/packwerk/spring_command.rb +28 -0
  70. data/lib/packwerk/updating_deprecated_references.rb +51 -0
  71. data/lib/packwerk/version.rb +6 -0
  72. data/lib/packwerk/violation_type.rb +13 -0
  73. data/library.yml +6 -0
  74. data/packwerk.gemspec +58 -0
  75. data/service.yml +6 -0
  76. data/shipit.rubygems.yml +1 -0
  77. data/sorbet/config +2 -0
  78. data/sorbet/rbi/gems/actioncable@6.1.0.alpha-d80c18a391e33552ae2d943e68af56946f883f65.rbi +840 -0
  79. data/sorbet/rbi/gems/actionmailbox@6.1.0.alpha-d80c18a391e33552ae2d943e68af56946f883f65.rbi +571 -0
  80. data/sorbet/rbi/gems/actionmailer@6.1.0.alpha-d80c18a391e33552ae2d943e68af56946f883f65.rbi +568 -0
  81. data/sorbet/rbi/gems/actionpack@6.1.0.alpha-d80c18a391e33552ae2d943e68af56946f883f65.rbi +5216 -0
  82. data/sorbet/rbi/gems/actiontext@6.1.0.alpha-d80c18a391e33552ae2d943e68af56946f883f65.rbi +663 -0
  83. data/sorbet/rbi/gems/actionview@6.1.0.alpha-d80c18a391e33552ae2d943e68af56946f883f65.rbi +2504 -0
  84. data/sorbet/rbi/gems/activejob@6.1.0.alpha-d80c18a391e33552ae2d943e68af56946f883f65.rbi +635 -0
  85. data/sorbet/rbi/gems/activemodel@6.1.0.alpha-d80c18a391e33552ae2d943e68af56946f883f65.rbi +1201 -0
  86. data/sorbet/rbi/gems/activerecord@6.1.0.alpha-d80c18a391e33552ae2d943e68af56946f883f65.rbi +8011 -0
  87. data/sorbet/rbi/gems/activestorage@6.1.0.alpha-d80c18a391e33552ae2d943e68af56946f883f65.rbi +904 -0
  88. data/sorbet/rbi/gems/activesupport@6.1.0.alpha-d80c18a391e33552ae2d943e68af56946f883f65.rbi +3888 -0
  89. data/sorbet/rbi/gems/ast@2.4.1.rbi +54 -0
  90. data/sorbet/rbi/gems/better_html@1.0.15.rbi +317 -0
  91. data/sorbet/rbi/gems/builder@3.2.4.rbi +8 -0
  92. data/sorbet/rbi/gems/byebug@11.1.3.rbi +8 -0
  93. data/sorbet/rbi/gems/coderay@1.1.3.rbi +8 -0
  94. data/sorbet/rbi/gems/colorize@0.8.1.rbi +40 -0
  95. data/sorbet/rbi/gems/commander@4.5.2.rbi +8 -0
  96. data/sorbet/rbi/gems/concurrent-ruby@1.1.6.rbi +1966 -0
  97. data/sorbet/rbi/gems/constant_resolver@0.1.5.rbi +26 -0
  98. data/sorbet/rbi/gems/crass@1.0.6.rbi +138 -0
  99. data/sorbet/rbi/gems/erubi@1.9.0.rbi +39 -0
  100. data/sorbet/rbi/gems/globalid@0.4.2.rbi +178 -0
  101. data/sorbet/rbi/gems/highline@2.0.3.rbi +8 -0
  102. data/sorbet/rbi/gems/html_tokenizer@0.0.7.rbi +46 -0
  103. data/sorbet/rbi/gems/i18n@1.8.2.rbi +633 -0
  104. data/sorbet/rbi/gems/jaro_winkler@1.5.4.rbi +8 -0
  105. data/sorbet/rbi/gems/loofah@2.5.0.rbi +272 -0
  106. data/sorbet/rbi/gems/m@1.5.1.rbi +108 -0
  107. data/sorbet/rbi/gems/mail@2.7.1.rbi +2490 -0
  108. data/sorbet/rbi/gems/marcel@0.3.3.rbi +30 -0
  109. data/sorbet/rbi/gems/method_source@1.0.0.rbi +76 -0
  110. data/sorbet/rbi/gems/mimemagic@0.3.5.rbi +47 -0
  111. data/sorbet/rbi/gems/mini_mime@1.0.2.rbi +71 -0
  112. data/sorbet/rbi/gems/mini_portile2@2.4.0.rbi +8 -0
  113. data/sorbet/rbi/gems/minitest@5.14.0.rbi +542 -0
  114. data/sorbet/rbi/gems/mocha@1.11.2.rbi +964 -0
  115. data/sorbet/rbi/gems/nio4r@2.5.2.rbi +89 -0
  116. data/sorbet/rbi/gems/nokogiri@1.10.9.rbi +1608 -0
  117. data/sorbet/rbi/gems/parallel@1.19.1.rbi +8 -0
  118. data/sorbet/rbi/gems/parlour@4.0.1.rbi +561 -0
  119. data/sorbet/rbi/gems/parser@2.7.1.4.rbi +1632 -0
  120. data/sorbet/rbi/gems/pry@0.13.1.rbi +8 -0
  121. data/sorbet/rbi/gems/rack-test@1.1.0.rbi +335 -0
  122. data/sorbet/rbi/gems/rack@2.2.2.rbi +1730 -0
  123. data/sorbet/rbi/gems/rails-dom-testing@2.0.3.rbi +123 -0
  124. data/sorbet/rbi/gems/rails-html-sanitizer@1.3.0.rbi +213 -0
  125. data/sorbet/rbi/gems/rails@6.1.0.alpha-d80c18a391e33552ae2d943e68af56946f883f65.rbi +8 -0
  126. data/sorbet/rbi/gems/railties@6.1.0.alpha-d80c18a391e33552ae2d943e68af56946f883f65.rbi +869 -0
  127. data/sorbet/rbi/gems/rainbow@3.0.0.rbi +155 -0
  128. data/sorbet/rbi/gems/rake@13.0.1.rbi +841 -0
  129. data/sorbet/rbi/gems/rexml@3.2.4.rbi +8 -0
  130. data/sorbet/rbi/gems/rubocop-performance@1.5.2.rbi +8 -0
  131. data/sorbet/rbi/gems/rubocop-shopify@1.0.2.rbi +8 -0
  132. data/sorbet/rbi/gems/rubocop-sorbet@0.3.7.rbi +8 -0
  133. data/sorbet/rbi/gems/rubocop@0.82.0.rbi +8 -0
  134. data/sorbet/rbi/gems/ruby-progressbar@1.10.1.rbi +8 -0
  135. data/sorbet/rbi/gems/smart_properties@1.15.0.rbi +168 -0
  136. data/sorbet/rbi/gems/spoom@1.0.4.rbi +418 -0
  137. data/sorbet/rbi/gems/spring@2.1.0.rbi +160 -0
  138. data/sorbet/rbi/gems/sprockets-rails@3.2.1.rbi +431 -0
  139. data/sorbet/rbi/gems/sprockets@4.0.0.rbi +1132 -0
  140. data/sorbet/rbi/gems/tapioca@0.4.5.rbi +518 -0
  141. data/sorbet/rbi/gems/thor@1.0.1.rbi +892 -0
  142. data/sorbet/rbi/gems/tzinfo@2.0.2.rbi +547 -0
  143. data/sorbet/rbi/gems/unicode-display_width@1.7.0.rbi +8 -0
  144. data/sorbet/rbi/gems/websocket-driver@0.7.1.rbi +438 -0
  145. data/sorbet/rbi/gems/websocket-extensions@0.1.4.rbi +71 -0
  146. data/sorbet/rbi/gems/zeitwerk@2.3.0.rbi +8 -0
  147. data/sorbet/tapioca/require.rb +25 -0
  148. data/static/packwerk-check-demo.png +0 -0
  149. data/static/packwerk_check.gif +0 -0
  150. data/static/packwerk_check_violation.gif +0 -0
  151. data/static/packwerk_update.gif +0 -0
  152. data/static/packwerk_validate.gif +0 -0
  153. metadata +341 -0
@@ -0,0 +1,42 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ require "parser"
5
+ require "parser/current"
6
+
7
+ module Packwerk
8
+ module Parsers
9
+ class Ruby
10
+ class RaiseExceptionsParser < Parser::CurrentRuby
11
+ def initialize(builder)
12
+ super(builder)
13
+ super.diagnostics.all_errors_are_fatal = true
14
+ end
15
+ end
16
+
17
+ class TolerateInvalidUtf8Builder < Parser::Builders::Default
18
+ def string_value(token)
19
+ value(token)
20
+ end
21
+ end
22
+
23
+ def initialize(parser_class: RaiseExceptionsParser)
24
+ @builder = TolerateInvalidUtf8Builder.new
25
+ @parser_class = parser_class
26
+ end
27
+
28
+ def call(io:, file_path: "<unknown>")
29
+ buffer = Parser::Source::Buffer.new(file_path)
30
+ buffer.source = io.read
31
+ parser = @parser_class.new(@builder)
32
+ parser.parse(buffer)
33
+ rescue EncodingError => e
34
+ result = ParseResult.new(file: file_path, message: e.message)
35
+ raise Parsers::ParseError, result
36
+ rescue Parser::SyntaxError => e
37
+ result = ParseResult.new(file: file_path, message: "Syntax error: #{e}")
38
+ raise Parsers::ParseError, result
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,45 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ require "packwerk/violation_type"
5
+
6
+ module Packwerk
7
+ class PrivacyChecker
8
+ def violation_type
9
+ ViolationType::Privacy
10
+ end
11
+
12
+ def invalid_reference?(reference, reference_lister)
13
+ return if reference.constant.public?
14
+
15
+ privacy_option = reference.constant.package.enforce_privacy
16
+ return if enforcement_disabled?(privacy_option)
17
+
18
+ return unless privacy_option == true ||
19
+ explicitly_private_constant?(reference.constant, explicitly_private_constants: privacy_option)
20
+
21
+ return if reference_lister.listed?(reference, violation_type: violation_type)
22
+
23
+ true
24
+ end
25
+
26
+ def message_for(reference)
27
+ source_desc = reference.source_package ? "'#{reference.source_package}'" : "here"
28
+ "Privacy violation: '#{reference.constant.name}' is private to '#{reference.constant.package}' but " \
29
+ "referenced from #{source_desc}.\n" \
30
+ "Is there a public entrypoint in '#{reference.constant.package.public_path}' that you can use instead?"
31
+ end
32
+
33
+ private
34
+
35
+ def explicitly_private_constant?(constant, explicitly_private_constants:)
36
+ explicitly_private_constants.include?(constant.name) ||
37
+ # nested constants
38
+ explicitly_private_constants.any? { |epc| constant.name.start_with?(epc + "::") }
39
+ end
40
+
41
+ def enforcement_disabled?(privacy_option)
42
+ [false, nil].include?(privacy_option)
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,6 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ module Packwerk
5
+ Reference = Struct.new(:source_package, :relative_path, :constant)
6
+ end
@@ -0,0 +1,81 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ require "sorbet-runtime"
5
+
6
+ require "packwerk/constant_discovery"
7
+ require "packwerk/constant_name_inspector"
8
+ require "packwerk/node"
9
+ require "packwerk/parsed_constant_definitions"
10
+ require "packwerk/reference"
11
+
12
+ module Packwerk
13
+ # extracts a possible constant reference from a given AST node
14
+ class ReferenceExtractor
15
+ extend T::Sig
16
+
17
+ sig do
18
+ params(
19
+ context_provider: Packwerk::ConstantDiscovery,
20
+ constant_name_inspectors: T::Array[Packwerk::ConstantNameInspector],
21
+ root_node: ::AST::Node,
22
+ root_path: String,
23
+ ).void
24
+ end
25
+ def initialize(
26
+ context_provider:,
27
+ constant_name_inspectors:,
28
+ root_node:,
29
+ root_path:
30
+ )
31
+ @context_provider = context_provider
32
+ @constant_name_inspectors = constant_name_inspectors
33
+ @root_path = root_path
34
+ @local_constant_definitions = ParsedConstantDefinitions.new(root_node: root_node)
35
+ end
36
+
37
+ def reference_from_node(node, ancestors:, file_path:)
38
+ constant_name = T.let(nil, T.nilable(String))
39
+
40
+ @constant_name_inspectors.each do |inspector|
41
+ constant_name = inspector.constant_name_from_node(node, ancestors: ancestors)
42
+ break if constant_name
43
+ end
44
+
45
+ reference_from_constant(constant_name, node: node, ancestors: ancestors, file_path: file_path) if constant_name
46
+ end
47
+
48
+ private
49
+
50
+ def reference_from_constant(constant_name, node:, ancestors:, file_path:)
51
+ namespace_path = Node.enclosing_namespace_path(node, ancestors: ancestors)
52
+ return if local_reference?(constant_name, Node.name_location(node), namespace_path)
53
+
54
+ constant =
55
+ @context_provider.context_for(
56
+ constant_name,
57
+ current_namespace_path: namespace_path
58
+ )
59
+
60
+ return if constant&.package.nil?
61
+
62
+ relative_path =
63
+ Pathname.new(file_path)
64
+ .relative_path_from(@root_path).to_s
65
+
66
+ source_package = @context_provider.package_from_path(relative_path)
67
+
68
+ return if source_package == constant.package
69
+
70
+ Reference.new(source_package, relative_path, constant)
71
+ end
72
+
73
+ def local_reference?(constant_name, name_location, namespace_path)
74
+ @local_constant_definitions.local_reference?(
75
+ constant_name,
76
+ location: name_location,
77
+ namespace_path: namespace_path
78
+ )
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,23 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ require "sorbet-runtime"
5
+
6
+ require "packwerk/reference"
7
+ require "packwerk/violation_type"
8
+
9
+ module Packwerk
10
+ module ReferenceLister
11
+ extend T::Sig
12
+ extend T::Helpers
13
+
14
+ interface!
15
+
16
+ sig do
17
+ params(reference: Packwerk::Reference, violation_type: ViolationType)
18
+ .returns(T::Boolean)
19
+ .abstract
20
+ end
21
+ def listed?(reference, violation_type:); end
22
+ end
23
+ end
@@ -0,0 +1,103 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ require "active_support/inflector"
5
+ require "constant_resolver"
6
+
7
+ require "packwerk/association_inspector"
8
+ require "packwerk/checking_deprecated_references"
9
+ require "packwerk/constant_discovery"
10
+ require "packwerk/const_node_inspector"
11
+ require "packwerk/dependency_checker"
12
+ require "packwerk/file_processor"
13
+ require "packwerk/node_processor"
14
+ require "packwerk/package_set"
15
+ require "packwerk/privacy_checker"
16
+ require "packwerk/reference_extractor"
17
+
18
+ module Packwerk
19
+ class RunContext
20
+ attr_reader(
21
+ :checkers,
22
+ :constant_name_inspectors,
23
+ :context_provider,
24
+ :root_path,
25
+ :file_processor,
26
+ :node_processor_class,
27
+ :reference_lister
28
+ )
29
+
30
+ DEFAULT_CHECKERS = [
31
+ ::Packwerk::DependencyChecker,
32
+ ::Packwerk::PrivacyChecker,
33
+ ]
34
+
35
+ class << self
36
+ def from_configuration(configuration, reference_lister: nil)
37
+ default_reference_lister = reference_lister ||
38
+ ::Packwerk::CheckingDeprecatedReferences.new(configuration.root_path)
39
+ new(
40
+ root_path: configuration.root_path,
41
+ load_paths: configuration.load_paths,
42
+ inflector: ActiveSupport::Inflector,
43
+ custom_associations: configuration.custom_associations,
44
+ reference_lister: default_reference_lister,
45
+ )
46
+ end
47
+ end
48
+
49
+ def initialize(
50
+ root_path:,
51
+ load_paths:,
52
+ package_paths: nil,
53
+ inflector: nil,
54
+ custom_associations: [],
55
+ checker_classes: DEFAULT_CHECKERS,
56
+ node_processor_class: NodeProcessor,
57
+ reference_lister: nil
58
+ )
59
+ @root_path = File.expand_path(root_path)
60
+
61
+ resolver = ConstantResolver.new(
62
+ root_path: @root_path,
63
+ load_paths: load_paths,
64
+ inflector: inflector,
65
+ )
66
+
67
+ package_set = ::Packwerk::PackageSet.load_all_from(@root_path, package_pathspec: package_paths)
68
+
69
+ @context_provider = ::Packwerk::ConstantDiscovery.new(
70
+ constant_resolver: resolver,
71
+ packages: package_set
72
+ )
73
+
74
+ @reference_lister = reference_lister || ::Packwerk::CheckingDeprecatedReferences.new(@root_path)
75
+
76
+ @checkers = checker_classes.map(&:new)
77
+
78
+ @constant_name_inspectors = [
79
+ ::Packwerk::ConstNodeInspector.new,
80
+ ::Packwerk::AssociationInspector.new(inflector: inflector, custom_associations: custom_associations),
81
+ ]
82
+
83
+ @node_processor_class = node_processor_class
84
+ @file_processor = FileProcessor.new(run_context: self)
85
+ end
86
+
87
+ def node_processor_for(filename:, ast_node:)
88
+ reference_extractor = ::Packwerk::ReferenceExtractor.new(
89
+ context_provider: context_provider,
90
+ constant_name_inspectors: constant_name_inspectors,
91
+ root_node: ast_node,
92
+ root_path: root_path,
93
+ )
94
+
95
+ node_processor_class.new(
96
+ reference_extractor: reference_extractor,
97
+ reference_lister: @reference_lister,
98
+ filename: filename,
99
+ checkers: checkers,
100
+ )
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,10 @@
1
+ # typed: ignore
2
+ # frozen_string_literal: true
3
+
4
+ require "packwerk/application_validator"
5
+
6
+ module Packwerk
7
+ # To do: This alias and file should be removed as it is deprecated
8
+ warn("DEPRECATION WARNING: Packwerk::SanityChecker is deprecated, use Packwerk::ApplicationValidator instead.")
9
+ SanityChecker = ApplicationValidator
10
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+ # typed: ignore
3
+
4
+ require "spring/commands"
5
+
6
+ module Packwerk
7
+ class SpringCommand
8
+ def env(*)
9
+ # Packwerk needs to run in a test environment, which has a set of autoload paths that are
10
+ # often a superset of the dev/prod paths (for example, test/support/helpers)
11
+ "test"
12
+ end
13
+
14
+ def exec_name
15
+ "packwerk"
16
+ end
17
+
18
+ def gem_name
19
+ "packwerk"
20
+ end
21
+
22
+ def call
23
+ load(Gem.bin_path(gem_name, exec_name))
24
+ end
25
+ end
26
+
27
+ Spring.register_command("packwerk", SpringCommand.new)
28
+ end
@@ -0,0 +1,51 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ require "sorbet-runtime"
5
+
6
+ require "packwerk/deprecated_references"
7
+ require "packwerk/reference"
8
+ require "packwerk/reference_lister"
9
+ require "packwerk/violation_type"
10
+
11
+ module Packwerk
12
+ class UpdatingDeprecatedReferences
13
+ extend T::Sig
14
+ include ReferenceLister
15
+
16
+ def initialize(root_path, deprecated_references = {})
17
+ @root_path = root_path
18
+ @deprecated_references = deprecated_references
19
+ end
20
+
21
+ sig do
22
+ params(reference: Packwerk::Reference, violation_type: ViolationType)
23
+ .returns(T::Boolean)
24
+ .override
25
+ end
26
+ def listed?(reference, violation_type:)
27
+ deprecated_references = deprecated_references_for(reference.source_package)
28
+ deprecated_references.add_entries(reference, violation_type.serialize)
29
+ true
30
+ end
31
+
32
+ def dump_deprecated_references_files
33
+ @deprecated_references.each do |_, deprecated_references_file|
34
+ deprecated_references_file.dump
35
+ end
36
+ end
37
+
38
+ private
39
+
40
+ def deprecated_references_for(package)
41
+ @deprecated_references[package] ||= Packwerk::DeprecatedReferences.new(
42
+ package,
43
+ deprecated_references_file_for(package),
44
+ )
45
+ end
46
+
47
+ def deprecated_references_file_for(package)
48
+ File.join(@root_path, package.name, "deprecated_references.yml")
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,6 @@
1
+ # typed: strong
2
+ # frozen_string_literal: true
3
+
4
+ module Packwerk
5
+ VERSION = "1.0.0"
6
+ end
@@ -0,0 +1,13 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ require "sorbet-runtime"
5
+
6
+ module Packwerk
7
+ class ViolationType < T::Enum
8
+ enums do
9
+ Privacy = new
10
+ Dependency = new
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,6 @@
1
+ ---
2
+ name: Packwerk
3
+ stewards:
4
+ - "@Shopify/kernel-architecture-patterns"
5
+ slack_channels:
6
+ - architecture-patterns
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/packwerk/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "packwerk"
7
+ spec.version = Packwerk::VERSION
8
+ spec.authors = ["Shopify Inc."]
9
+ spec.email = ["gems@shopify.com"]
10
+
11
+ spec.summary = "Packages for applications based on the zeitwerk autoloader"
12
+
13
+ spec.description = <<~DESCRIPTION
14
+ Sets package level boundaries between a specified set of ruby
15
+ constants to minimize cross-boundary referencing and dependency.
16
+ DESCRIPTION
17
+ spec.homepage = "https://github.com/Shopify/packwerk"
18
+ spec.license = "MIT"
19
+
20
+ if spec.respond_to?(:metadata)
21
+ spec.metadata["homepage_uri"] = spec.homepage
22
+ spec.metadata["source_code_uri"] = "https://github.com/Shopify/packwerk"
23
+ spec.metadata["changelog_uri"] = "https://github.com/Shopify/packwerk/releases"
24
+ end
25
+
26
+ if spec.respond_to?(:metadata)
27
+ spec.metadata["allowed_push_host"] = "https://rubygems.org"
28
+ else
29
+ raise "RubyGems 2.0 or newer is required to protect against " \
30
+ "public gem pushes."
31
+ end
32
+
33
+ spec.bindir = "exe"
34
+ spec.executables << "packwerk"
35
+
36
+ spec.files = Dir.chdir(File.expand_path("..", __FILE__)) do
37
+ %x(git ls-files -z).split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
38
+ end
39
+ spec.require_paths = %w(lib)
40
+
41
+ spec.required_ruby_version = ">= 2.6"
42
+
43
+ spec.add_dependency("activesupport", ">= 5.2")
44
+ spec.add_dependency("constant_resolver")
45
+ spec.add_dependency("sorbet-runtime")
46
+
47
+ spec.add_development_dependency("bundler")
48
+ spec.add_development_dependency("rake")
49
+ spec.add_development_dependency("sorbet")
50
+ spec.add_development_dependency("m")
51
+
52
+ # For Ruby parsing
53
+ spec.add_dependency("ast")
54
+ spec.add_dependency("parser")
55
+
56
+ # For ERB parsing
57
+ spec.add_dependency("better_html")
58
+ end