packwerk 1.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.
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