packwerk 1.3.2 → 1.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +9 -7
- data/README.md +2 -1
- data/TROUBLESHOOT.md +3 -3
- data/USAGE.md +27 -11
- data/exe/packwerk +7 -1
- data/lib/packwerk/application_load_paths.rb +1 -0
- data/lib/packwerk/application_validator.rb +3 -1
- data/lib/packwerk/cli.rb +27 -5
- data/lib/packwerk/const_node_inspector.rb +3 -2
- data/lib/packwerk/constant_name_inspector.rb +1 -1
- data/lib/packwerk/deprecated_references.rb +11 -6
- data/lib/packwerk/file_processor.rb +39 -14
- data/lib/packwerk/files_for_processing.rb +15 -4
- data/lib/packwerk/generators/templates/package.yml +1 -1
- data/lib/packwerk/graph.rb +2 -0
- data/lib/packwerk/inflector.rb +1 -0
- data/lib/packwerk/node.rb +1 -0
- data/lib/packwerk/node_processor.rb +10 -22
- data/lib/packwerk/node_processor_factory.rb +0 -2
- data/lib/packwerk/node_visitor.rb +4 -2
- data/lib/packwerk/package.rb +25 -4
- data/lib/packwerk/package_set.rb +43 -8
- data/lib/packwerk/parsed_constant_definitions.rb +1 -0
- data/lib/packwerk/reference.rb +2 -1
- data/lib/packwerk/reference_checking/checkers/checker.rb +21 -0
- data/lib/packwerk/reference_checking/checkers/dependency_checker.rb +31 -0
- data/lib/packwerk/reference_checking/checkers/privacy_checker.rb +58 -0
- data/lib/packwerk/reference_checking/reference_checker.rb +33 -0
- data/lib/packwerk/reference_extractor.rb +2 -2
- data/lib/packwerk/reference_offense.rb +1 -0
- data/lib/packwerk/run_context.rb +9 -6
- data/lib/packwerk/sanity_checker.rb +1 -1
- data/lib/packwerk/spring_command.rb +28 -0
- data/lib/packwerk/version.rb +1 -1
- data/lib/packwerk/violation_type.rb +1 -1
- data/lib/packwerk.rb +14 -3
- data/packwerk.gemspec +3 -1
- data/service.yml +0 -2
- data/sorbet/rbi/gems/psych@3.3.2.rbi +24 -0
- metadata +23 -6
- data/lib/packwerk/checker.rb +0 -17
- data/lib/packwerk/dependency_checker.rb +0 -26
- data/lib/packwerk/privacy_checker.rb +0 -53
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5d150d89ede62e601246e2ff0290ecda4aab883a7c8b8be02feb86760ad26b72
|
4
|
+
data.tar.gz: 33b088b3387b8fdfe694967f20529fb8795dec8572dc9e036cb4979402f19c5b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 102154447670b85dc4e0825dcdf1ec11cd0fd291ffc1c7230b98da2208b0a39c38d2c8726a33226aeea68082e16a24917db7ad3380c871b9b02b0d76c8ac69fc
|
7
|
+
data.tar.gz: 666e3745e9167028e5d3901d74400848c87c1b8a64a7487d3e4bfe13916e2902990633db7c448057b8106fd4b6b5c5de08c697c88151a305c92d34259266448f
|
data/Gemfile.lock
CHANGED
@@ -87,10 +87,11 @@ GIT
|
|
87
87
|
PATH
|
88
88
|
remote: .
|
89
89
|
specs:
|
90
|
-
packwerk (1.
|
90
|
+
packwerk (1.4.0)
|
91
91
|
activesupport (>= 5.2)
|
92
92
|
ast
|
93
93
|
better_html
|
94
|
+
bundler
|
94
95
|
constant_resolver
|
95
96
|
parallel
|
96
97
|
parser
|
@@ -135,16 +136,16 @@ GEM
|
|
135
136
|
marcel (1.0.0)
|
136
137
|
method_source (1.0.0)
|
137
138
|
mini_mime (1.0.3)
|
138
|
-
mini_portile2 (2.
|
139
|
+
mini_portile2 (2.6.1)
|
139
140
|
minitest (5.14.4)
|
140
141
|
minitest-focus (1.2.1)
|
141
142
|
minitest (>= 4, < 6)
|
142
143
|
mocha (1.12.0)
|
143
144
|
nio4r (2.5.7)
|
144
|
-
nokogiri (1.
|
145
|
-
mini_portile2 (~> 2.
|
145
|
+
nokogiri (1.12.5)
|
146
|
+
mini_portile2 (~> 2.6.1)
|
146
147
|
racc (~> 1.4)
|
147
|
-
nokogiri (1.
|
148
|
+
nokogiri (1.12.5-x86_64-darwin)
|
148
149
|
racc (~> 1.4)
|
149
150
|
parallel (1.20.1)
|
150
151
|
parlour (6.0.0)
|
@@ -157,6 +158,7 @@ GEM
|
|
157
158
|
pry (0.14.0)
|
158
159
|
coderay (~> 1.1)
|
159
160
|
method_source (~> 1.0)
|
161
|
+
psych (3.3.2)
|
160
162
|
racc (1.5.2)
|
161
163
|
rack (2.2.3)
|
162
164
|
rack-test (1.1.0)
|
@@ -189,7 +191,7 @@ GEM
|
|
189
191
|
rubocop-sorbet (0.6.1)
|
190
192
|
rubocop
|
191
193
|
ruby-progressbar (1.11.0)
|
192
|
-
smart_properties (1.
|
194
|
+
smart_properties (1.16.3)
|
193
195
|
sorbet (0.5.6360)
|
194
196
|
sorbet-static (= 0.5.6360)
|
195
197
|
sorbet-runtime (0.5.6360)
|
@@ -237,13 +239,13 @@ PLATFORMS
|
|
237
239
|
x86_64-darwin-20
|
238
240
|
|
239
241
|
DEPENDENCIES
|
240
|
-
bundler
|
241
242
|
byebug
|
242
243
|
constant_resolver
|
243
244
|
m
|
244
245
|
minitest-focus
|
245
246
|
mocha
|
246
247
|
packwerk!
|
248
|
+
psych (~> 3)
|
247
249
|
rails!
|
248
250
|
rake
|
249
251
|
rubocop-performance
|
data/README.md
CHANGED
@@ -47,7 +47,8 @@ Or install it yourself as:
|
|
47
47
|
|
48
48
|
$ gem install packwerk
|
49
49
|
|
50
|
-
|
50
|
+
2. Run `bundle binstub packwerk` to generate the binstub
|
51
|
+
3. Run `bin/packwerk init` to generate the configuration files
|
51
52
|
|
52
53
|
## Usage
|
53
54
|
|
data/TROUBLESHOOT.md
CHANGED
@@ -14,11 +14,11 @@ Packwerk can give feedback via continuous integration (CI) if you have it set up
|
|
14
14
|
|
15
15
|
You can specify folders or packages in Packwerk commands for a shorter run time:
|
16
16
|
|
17
|
-
packwerk check components/your_package
|
17
|
+
bin/packwerk check components/your_package
|
18
18
|
|
19
|
-
packwerk update-deprecations components/your_package
|
19
|
+
bin/packwerk update-deprecations components/your_package
|
20
20
|
|
21
|
-
_Note: You cannot specify folders or packages for `packwerk validate` because the command runs for the entire application._
|
21
|
+
_Note: You cannot specify folders or packages for `bin/packwerk validate` because the command runs for the entire application._
|
22
22
|
|
23
23
|
![](static/packwerk_check_violation.gif)
|
24
24
|
|
data/USAGE.md
CHANGED
@@ -6,7 +6,9 @@
|
|
6
6
|
* [What is a package?](#what-is-a-package)
|
7
7
|
* [Package principles](#package-principles)
|
8
8
|
* [Getting started](#getting-started)
|
9
|
-
* [Setting up
|
9
|
+
* [Setting up Spring](#setting-up-spring)
|
10
|
+
* [Configuring Packwerk](#configuring-packwerk)
|
11
|
+
* [Using a custom ERB parser](#using-a-custom-erb-parser)
|
10
12
|
* [Inflections](#inflections)
|
11
13
|
* [Validating the package system](#validating-the-package-system)
|
12
14
|
* [Defining packages](#defining-packages)
|
@@ -42,9 +44,12 @@ The [package principles](https://en.wikipedia.org/wiki/Package_principles) page
|
|
42
44
|
|
43
45
|
## Getting started
|
44
46
|
|
45
|
-
After including Packwerk in the Gemfile, you
|
47
|
+
After including Packwerk in the Gemfile, you will first want to generate a binstub:
|
48
|
+
You can do this by running `bundle binstub packwerk`, which will generate a [binstub](https://bundler.io/man/bundle-binstubs.1.html#DESCRIPTION) at `bin/packwerk`.
|
46
49
|
|
47
|
-
|
50
|
+
Then, you can generate the necessary files to get Packwerk running by executing:
|
51
|
+
|
52
|
+
bin/packwerk init
|
48
53
|
|
49
54
|
Here is a list of files generated:
|
50
55
|
|
@@ -56,7 +61,14 @@ Here is a list of files generated:
|
|
56
61
|
|
57
62
|
After that, you may begin creating packages for your application. See [Defining packages](#Defining-packages)
|
58
63
|
|
59
|
-
|
64
|
+
### Setting up Spring
|
65
|
+
|
66
|
+
[Spring](https://github.com/rails/spring) is a preloader for Rails. Because `packwerk` loads `Rails`, it can be sped up dramatically by enabling spring. Packwerk supports the usage of Spring.
|
67
|
+
Firstly, spring needs to know about the packwerk spring command when spring is loading. To do that, add `require 'packwerk/spring_command'` to `config/spring.rb` in your application.
|
68
|
+
Secondly, to enable Spring, first run `bin/spring binstub packwerk` which will "springify" the generated binstub.
|
69
|
+
|
70
|
+
|
71
|
+
## Configuring Packwerk
|
60
72
|
|
61
73
|
Packwerk reads from the `packwerk.yml` configuration file in the root directory. Packwerk will run with the default configuration if any of these settings are not specified.
|
62
74
|
|
@@ -134,7 +146,7 @@ Any new inflectors should be added to `config/inflections.yml`.
|
|
134
146
|
|
135
147
|
There are some criteria that an application must meet in order to have a valid package system. These criteria include having a valid autoload path cache, package definition files, and application folder structure. The dependency graph within the package system also has to be acyclic.
|
136
148
|
|
137
|
-
We recommend setting up the package system validation for your Rails application in a CI step (or through a test suite for Ruby projects) separate from `packwerk check`.
|
149
|
+
We recommend setting up the package system validation for your Rails application in a CI step (or through a test suite for Ruby projects) separate from `bin/packwerk check`.
|
138
150
|
|
139
151
|
Use the following command to validate the application:
|
140
152
|
|
@@ -231,13 +243,17 @@ After enforcing the boundary checks for a package, you may execute:
|
|
231
243
|
|
232
244
|
Packwerk will check the entire codebase for any new or stale violations.
|
233
245
|
|
234
|
-
You can also specify folders for a shorter run time
|
246
|
+
You can also specify folders for a shorter run time. When checking against folders all subfolders will be analyzed, irrespective of nested package boundaries.
|
235
247
|
|
236
248
|
packwerk check components/your_package
|
237
249
|
|
250
|
+
You can also specify packages for a shorter run time. When checking against packages any packages nested underneath the specified packages will not be checked. This can be helpful to test packages like the root package, which can have many nested packages.
|
251
|
+
|
252
|
+
packwerk check --packages=components/your_package,components/your_other_package
|
253
|
+
|
238
254
|
![](static/packwerk_check.gif)
|
239
255
|
|
240
|
-
In order to keep the package system valid at each version of the application, we recommend running `packwerk check` in your CI pipeline.
|
256
|
+
In order to keep the package system valid at each version of the application, we recommend running `bin/packwerk check` in your CI pipeline.
|
241
257
|
|
242
258
|
See: [TROUBLESHOOT.md - Sample violations](TROUBLESHOOT.md#Sample-violations)
|
243
259
|
|
@@ -247,17 +263,17 @@ For existing codebases, packages are likely to have existing boundary violations
|
|
247
263
|
|
248
264
|
If so, you will want to stop the bleeding and prevent more violations from occuring. The existing violations in the codebase can be recorded in a [deprecated references list](#Understanding_the_list_of_deprecated_references) by executing:
|
249
265
|
|
250
|
-
packwerk update-deprecations
|
266
|
+
bin/packwerk update-deprecations
|
251
267
|
|
252
|
-
Similar to `packwerk check`, you may also run `packwerk update-deprecations` on folders or packages:
|
268
|
+
Similar to `bin/packwerk check`, you may also run `bin/packwerk update-deprecations` on folders or packages:
|
253
269
|
|
254
|
-
packwerk update-deprecations components/your_package
|
270
|
+
bin/packwerk update-deprecations components/your_package
|
255
271
|
|
256
272
|
![](static/packwerk_update.gif)
|
257
273
|
|
258
274
|
_Note: Changing dependencies or enabling dependencies will not require a full update of the codebase, only the package that changed. On the other hand, changing or enabling privacy will require a full update of the codebase._
|
259
275
|
|
260
|
-
`packwerk update-deprecations` should only be run to record existing violations and to remove deprecated references that have been worked off. Running `packwerk update-deprecations` to resolve a violation should be the very last resort.
|
276
|
+
`bin/packwerk update-deprecations` should only be run to record existing violations and to remove deprecated references that have been worked off. Running `bin/packwerk update-deprecations` to resolve a violation should be the very last resort.
|
261
277
|
|
262
278
|
See: [TROUBLESHOOT.md - Troubleshooting violations](TROUBLESHOOT.md#Troubleshooting_violations)
|
263
279
|
|
data/exe/packwerk
CHANGED
@@ -3,4 +3,10 @@
|
|
3
3
|
|
4
4
|
require "packwerk"
|
5
5
|
|
6
|
-
|
6
|
+
# Needs to be run in test environment in order to have test helper paths available in the autoload paths
|
7
|
+
ENV["RAILS_ENV"] = "test"
|
8
|
+
|
9
|
+
# Command line arguments needs to be duplicated because spring modifies it
|
10
|
+
packwerk_argv = ARGV.dup
|
11
|
+
cli = Packwerk::Cli.new(style: Packwerk::OutputStyles::Coloured.new)
|
12
|
+
cli.run(packwerk_argv)
|
@@ -7,6 +7,8 @@ require "pathname"
|
|
7
7
|
require "yaml"
|
8
8
|
|
9
9
|
module Packwerk
|
10
|
+
# Checks the structure of the application and its packwerk configuration to make sure we can run a check and deliver
|
11
|
+
# correct results.
|
10
12
|
class ApplicationValidator
|
11
13
|
def initialize(config_file_path:, configuration:, environment:)
|
12
14
|
@config_file_path = config_file_path
|
@@ -296,7 +298,7 @@ module Packwerk
|
|
296
298
|
end
|
297
299
|
|
298
300
|
def package_manifests(glob_pattern = package_glob)
|
299
|
-
PackageSet.package_paths(@configuration.root_path, glob_pattern)
|
301
|
+
PackageSet.package_paths(@configuration.root_path, glob_pattern, @configuration.exclude)
|
300
302
|
.map { |f| File.realpath(f) }
|
301
303
|
end
|
302
304
|
|
data/lib/packwerk/cli.rb
CHANGED
@@ -1,7 +1,10 @@
|
|
1
1
|
# typed: true
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
+
require "optparse"
|
5
|
+
|
4
6
|
module Packwerk
|
7
|
+
# A command-line interface to Packwerk.
|
5
8
|
class Cli
|
6
9
|
extend T::Sig
|
7
10
|
|
@@ -97,7 +100,7 @@ module Packwerk
|
|
97
100
|
result = if success
|
98
101
|
<<~EOS
|
99
102
|
|
100
|
-
🎉 Packwerk is ready to be used. You can start defining packages and run `packwerk check`.
|
103
|
+
🎉 Packwerk is ready to be used. You can start defining packages and run `bin/packwerk check`.
|
101
104
|
For more information on how to use Packwerk, see: https://github.com/Shopify/packwerk/blob/main/USAGE.md
|
102
105
|
EOS
|
103
106
|
else
|
@@ -123,8 +126,12 @@ module Packwerk
|
|
123
126
|
result.status
|
124
127
|
end
|
125
128
|
|
126
|
-
def fetch_files_to_process(paths)
|
127
|
-
files = FilesForProcessing.fetch(
|
129
|
+
def fetch_files_to_process(paths, ignore_nested_packages)
|
130
|
+
files = FilesForProcessing.fetch(
|
131
|
+
paths: paths,
|
132
|
+
ignore_nested_packages: ignore_nested_packages,
|
133
|
+
configuration: @configuration
|
134
|
+
)
|
128
135
|
abort("No files found or given. "\
|
129
136
|
"Specify files or check the include and exclude glob in the config file.") if files.empty?
|
130
137
|
files
|
@@ -155,9 +162,24 @@ module Packwerk
|
|
155
162
|
end
|
156
163
|
end
|
157
164
|
|
158
|
-
def parse_run(
|
165
|
+
def parse_run(params)
|
166
|
+
paths = T.let([], T::Array[String])
|
167
|
+
ignore_nested_packages = nil
|
168
|
+
|
169
|
+
if params.any? { |p| p.include?("--packages") }
|
170
|
+
OptionParser.new do |parser|
|
171
|
+
parser.on("--packages=PACKAGESLIST", Array, "package names, comma separated") do |p|
|
172
|
+
paths = p
|
173
|
+
end
|
174
|
+
end.parse!(params)
|
175
|
+
ignore_nested_packages = true
|
176
|
+
else
|
177
|
+
paths = params
|
178
|
+
ignore_nested_packages = false
|
179
|
+
end
|
180
|
+
|
159
181
|
ParseRun.new(
|
160
|
-
files: fetch_files_to_process(paths),
|
182
|
+
files: fetch_files_to_process(paths, ignore_nested_packages),
|
161
183
|
configuration: @configuration,
|
162
184
|
progress_formatter: @progress_formatter,
|
163
185
|
offenses_formatter: @offenses_formatter
|
@@ -15,6 +15,9 @@ module Packwerk
|
|
15
15
|
def constant_name_from_node(node, ancestors:)
|
16
16
|
return nil unless Node.constant?(node)
|
17
17
|
parent = ancestors.first
|
18
|
+
|
19
|
+
# Only process the root `const` node for namespaced constant references. For example, in the
|
20
|
+
# reference `Spam::Eggs::Thing`, we only process the const node associated with `Spam`.
|
18
21
|
return nil unless root_constant?(parent)
|
19
22
|
|
20
23
|
if parent && constant_in_module_or_class_definition?(node, parent: parent)
|
@@ -30,8 +33,6 @@ module Packwerk
|
|
30
33
|
|
31
34
|
private
|
32
35
|
|
33
|
-
# Only process the root `const` node for namespaced constant references. For example, in the
|
34
|
-
# reference `Spam::Eggs::Thing`, we only process the const node associated with `Spam`.
|
35
36
|
sig { params(parent: T.nilable(AST::Node)).returns(T::Boolean) }
|
36
37
|
def root_constant?(parent)
|
37
38
|
!(parent && Node.constant?(parent))
|
@@ -4,7 +4,7 @@
|
|
4
4
|
require "ast"
|
5
5
|
|
6
6
|
module Packwerk
|
7
|
-
# An interface describing
|
7
|
+
# An interface describing an object that can extract a constant name from an AST node.
|
8
8
|
module ConstantNameInspector
|
9
9
|
extend T::Sig
|
10
10
|
extend T::Helpers
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require "yaml"
|
@@ -7,11 +7,15 @@ module Packwerk
|
|
7
7
|
class DeprecatedReferences
|
8
8
|
extend T::Sig
|
9
9
|
|
10
|
+
ENTRIES_TYPE = T.type_alias do
|
11
|
+
T::Hash[String, T.untyped]
|
12
|
+
end
|
13
|
+
|
10
14
|
sig { params(package: Packwerk::Package, filepath: String).void }
|
11
15
|
def initialize(package, filepath)
|
12
16
|
@package = package
|
13
17
|
@filepath = filepath
|
14
|
-
@new_entries = {}
|
18
|
+
@new_entries = T.let({}, ENTRIES_TYPE)
|
15
19
|
end
|
16
20
|
|
17
21
|
sig do
|
@@ -73,7 +77,7 @@ module Packwerk
|
|
73
77
|
#
|
74
78
|
# You can regenerate this file using the following command:
|
75
79
|
#
|
76
|
-
# packwerk update-deprecations #{@package.name}
|
80
|
+
# bin/packwerk update-deprecations #{@package.name}
|
77
81
|
MESSAGE
|
78
82
|
File.open(@filepath, "w") do |f|
|
79
83
|
f.write(message)
|
@@ -84,7 +88,7 @@ module Packwerk
|
|
84
88
|
|
85
89
|
private
|
86
90
|
|
87
|
-
sig { returns(
|
91
|
+
sig { returns(ENTRIES_TYPE) }
|
88
92
|
def prepare_entries_for_dump
|
89
93
|
@new_entries.each do |package_name, package_violations|
|
90
94
|
package_violations.each do |_, entries_for_file|
|
@@ -97,8 +101,9 @@ module Packwerk
|
|
97
101
|
@new_entries = @new_entries.sort.to_h
|
98
102
|
end
|
99
103
|
|
100
|
-
sig { returns(
|
104
|
+
sig { returns(ENTRIES_TYPE) }
|
101
105
|
def deprecated_references
|
106
|
+
@deprecated_references ||= T.let(@deprecated_references, T.nilable(ENTRIES_TYPE))
|
102
107
|
@deprecated_references ||= if File.exist?(@filepath)
|
103
108
|
load_yaml(@filepath)
|
104
109
|
else
|
@@ -106,7 +111,7 @@ module Packwerk
|
|
106
111
|
end
|
107
112
|
end
|
108
113
|
|
109
|
-
sig { params(filepath: String).returns(
|
114
|
+
sig { params(filepath: String).returns(ENTRIES_TYPE) }
|
110
115
|
def load_yaml(filepath)
|
111
116
|
YAML.load_file(filepath) || {}
|
112
117
|
rescue Psych::Exception
|
@@ -1,10 +1,12 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: true
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require "ast/node"
|
5
5
|
|
6
6
|
module Packwerk
|
7
7
|
class FileProcessor
|
8
|
+
extend T::Sig
|
9
|
+
|
8
10
|
class UnknownFileTypeResult < Offense
|
9
11
|
def initialize(file:)
|
10
12
|
super(file: file, message: "unknown file type")
|
@@ -16,24 +18,47 @@ module Packwerk
|
|
16
18
|
@parser_factory = parser_factory || Packwerk::Parsers::Factory.instance
|
17
19
|
end
|
18
20
|
|
21
|
+
sig do
|
22
|
+
params(file_path: String).returns(
|
23
|
+
T::Array[
|
24
|
+
T.any(
|
25
|
+
Packwerk::Reference,
|
26
|
+
Packwerk::Offense,
|
27
|
+
)
|
28
|
+
]
|
29
|
+
)
|
30
|
+
end
|
19
31
|
def call(file_path)
|
20
|
-
|
21
|
-
return [UnknownFileTypeResult.new(file: file_path)] if parser.nil?
|
32
|
+
return [UnknownFileTypeResult.new(file: file_path)] if parser_for(file_path).nil?
|
22
33
|
|
23
|
-
node =
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
34
|
+
node = parse_into_ast(file_path)
|
35
|
+
return [] unless node
|
36
|
+
|
37
|
+
references_from_ast(node, file_path)
|
38
|
+
rescue Parsers::ParseError => e
|
39
|
+
[e.result]
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
28
43
|
|
29
|
-
|
30
|
-
|
31
|
-
node_processor = @node_processor_factory.for(filename: file_path, node: node)
|
32
|
-
node_visitor = Packwerk::NodeVisitor.new(node_processor: node_processor)
|
44
|
+
def references_from_ast(node, file_path)
|
45
|
+
references = []
|
33
46
|
|
34
|
-
|
47
|
+
node_processor = @node_processor_factory.for(filename: file_path, node: node)
|
48
|
+
node_visitor = Packwerk::NodeVisitor.new(node_processor: node_processor)
|
49
|
+
node_visitor.visit(node, ancestors: [], result: references)
|
50
|
+
|
51
|
+
references
|
52
|
+
end
|
53
|
+
|
54
|
+
def parse_into_ast(file_path)
|
55
|
+
File.open(file_path, "r", nil, external_encoding: Encoding::UTF_8) do |file|
|
56
|
+
parser_for(file_path).call(io: file, file_path: file_path)
|
35
57
|
end
|
36
|
-
|
58
|
+
end
|
59
|
+
|
60
|
+
def parser_for(file_path)
|
61
|
+
@parser_factory.for_path(file_path)
|
37
62
|
end
|
38
63
|
end
|
39
64
|
end
|
@@ -4,14 +4,15 @@
|
|
4
4
|
module Packwerk
|
5
5
|
class FilesForProcessing
|
6
6
|
class << self
|
7
|
-
def fetch(paths:, configuration:)
|
8
|
-
new(paths, configuration).files
|
7
|
+
def fetch(paths:, configuration:, ignore_nested_packages: false)
|
8
|
+
new(paths, configuration, ignore_nested_packages).files
|
9
9
|
end
|
10
10
|
end
|
11
11
|
|
12
|
-
def initialize(paths, configuration)
|
12
|
+
def initialize(paths, configuration, ignore_nested_packages)
|
13
13
|
@paths = paths
|
14
14
|
@configuration = configuration
|
15
|
+
@ignore_nested_packages = ignore_nested_packages
|
15
16
|
end
|
16
17
|
|
17
18
|
def files
|
@@ -43,11 +44,21 @@ module Packwerk
|
|
43
44
|
File.expand_path(glob, @configuration.root_path)
|
44
45
|
end
|
45
46
|
|
46
|
-
Dir.glob([File.join(path, "**", "*")]).select do |file_path|
|
47
|
+
files = Dir.glob([File.join(path, "**", "*")]).select do |file_path|
|
47
48
|
absolute_includes.any? do |pattern|
|
48
49
|
File.fnmatch?(pattern, file_path, File::FNM_EXTGLOB)
|
49
50
|
end
|
50
51
|
end
|
52
|
+
|
53
|
+
if @ignore_nested_packages
|
54
|
+
nested_packages_paths = Dir.glob(File.join(path, "*", "**", "package.yml"))
|
55
|
+
nested_packages_globs = nested_packages_paths.map { |npp| npp.gsub("package.yml", "**/*") }
|
56
|
+
nested_packages_globs.each do |glob|
|
57
|
+
files -= Dir.glob(glob)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
files
|
51
62
|
end
|
52
63
|
|
53
64
|
def configured_included_files
|
@@ -1,5 +1,5 @@
|
|
1
1
|
# This file represents the root package of the application
|
2
|
-
# Please validate the configuration using `
|
2
|
+
# Please validate the configuration using `packwerk validate` (for Rails applications) or running the auto generated
|
3
3
|
# test case (for non-Rails projects). You can then use `packwerk check` to check your code.
|
4
4
|
|
5
5
|
# Turn on dependency checks for this package
|
data/lib/packwerk/graph.rb
CHANGED
@@ -2,7 +2,9 @@
|
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
module Packwerk
|
5
|
+
# A general implementation of a graph data structure with the ability to check for - and list - cycles.
|
5
6
|
class Graph
|
7
|
+
# @param [Array<Array>] edges The edges of the graph; An edge being represented as an Array of two nodes.
|
6
8
|
def initialize(*edges)
|
7
9
|
@edges = edges.uniq
|
8
10
|
@cycles = Set.new
|
data/lib/packwerk/inflector.rb
CHANGED
data/lib/packwerk/node.rb
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
module Packwerk
|
5
|
+
# Processes a single node in an abstract syntax tree (AST) using the provided checkers.
|
5
6
|
class NodeProcessor
|
6
7
|
extend T::Sig
|
7
8
|
|
@@ -9,35 +10,22 @@ module Packwerk
|
|
9
10
|
params(
|
10
11
|
reference_extractor: ReferenceExtractor,
|
11
12
|
filename: String,
|
12
|
-
checkers: T::Array[Checker]
|
13
13
|
).void
|
14
14
|
end
|
15
|
-
def initialize(reference_extractor:, filename
|
15
|
+
def initialize(reference_extractor:, filename:)
|
16
16
|
@reference_extractor = reference_extractor
|
17
17
|
@filename = filename
|
18
|
-
@checkers = checkers
|
19
18
|
end
|
20
19
|
|
21
|
-
sig
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
20
|
+
sig do
|
21
|
+
params(
|
22
|
+
node: Parser::AST::Node,
|
23
|
+
ancestors: T::Array[Parser::AST::Node]
|
24
|
+
).returns(T.nilable(Packwerk::Reference))
|
26
25
|
end
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
def check_reference(reference, node)
|
31
|
-
return [] unless reference
|
32
|
-
@checkers.each_with_object([]) do |checker, violations|
|
33
|
-
next unless checker.invalid_reference?(reference)
|
34
|
-
offense = Packwerk::ReferenceOffense.new(
|
35
|
-
location: Node.location(node),
|
36
|
-
reference: reference,
|
37
|
-
violation_type: checker.violation_type
|
38
|
-
)
|
39
|
-
violations << offense
|
40
|
-
end
|
26
|
+
def call(node, ancestors)
|
27
|
+
return unless Node.method_call?(node) || Node.constant?(node)
|
28
|
+
@reference_extractor.reference_from_node(node, ancestors: ancestors, file_path: @filename)
|
41
29
|
end
|
42
30
|
end
|
43
31
|
end
|
@@ -8,14 +8,12 @@ module Packwerk
|
|
8
8
|
const :root_path, String
|
9
9
|
const :context_provider, Packwerk::ConstantDiscovery
|
10
10
|
const :constant_name_inspectors, T::Array[ConstantNameInspector]
|
11
|
-
const :checkers, T::Array[Checker]
|
12
11
|
|
13
12
|
sig { params(filename: String, node: AST::Node).returns(NodeProcessor) }
|
14
13
|
def for(filename:, node:)
|
15
14
|
::Packwerk::NodeProcessor.new(
|
16
15
|
reference_extractor: reference_extractor(node: node),
|
17
16
|
filename: filename,
|
18
|
-
checkers: checkers,
|
19
17
|
)
|
20
18
|
end
|
21
19
|
|
@@ -1,14 +1,16 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: true
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
module Packwerk
|
5
|
+
# Visits all nodes of an AST, processing them using a given node processor.
|
5
6
|
class NodeVisitor
|
6
7
|
def initialize(node_processor:)
|
7
8
|
@node_processor = node_processor
|
8
9
|
end
|
9
10
|
|
10
11
|
def visit(node, ancestors:, result:)
|
11
|
-
|
12
|
+
reference = @node_processor.call(node, ancestors)
|
13
|
+
result << reference if reference
|
12
14
|
|
13
15
|
child_ancestors = [node] + ancestors
|
14
16
|
Node.each_child(node) do |child|
|