packwerk-extensions 0.1.7 → 0.1.9
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +120 -1
- data/lib/packwerk/folder_visibility/checker.rb +93 -0
- data/lib/packwerk/folder_visibility/package.rb +23 -0
- data/lib/packwerk/folder_visibility/validator.rb +36 -0
- data/lib/packwerk/privacy/checker.rb +47 -1
- data/lib/packwerk/privacy/package.rb +3 -1
- data/lib/packwerk/privacy/validator.rb +19 -3
- data/lib/packwerk-extensions.rb +1 -0
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 39d34d165ec6218799d3416a91249917b0d5b64272af7a8ba60c66e3473876c5
|
4
|
+
data.tar.gz: 040f5a83a6fef23e26efef5149fce578329c5db46b9d8f3ebe0d380e56250683
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e6f3ac7caadd78432c6b5a45f593c63445e23bbc30784528b13c505175e4358f0efb0de65eeca41b6a049431d24855834d191b2a1b10892dda56016eb777e937
|
7
|
+
data.tar.gz: 7d4fbd68b612ce1cb185e574fca289bf8266133cda8f47a47d95a14adf098c5fa2805d6cdf15ba4c47287b5afcc4674328b7308e255c713cffc24b43c457cd8b
|
data/README.md
CHANGED
@@ -5,7 +5,8 @@
|
|
5
5
|
Currently, it ships the following checkers to help improve the boundaries between packages. These checkers are:
|
6
6
|
- A `privacy` checker that ensures other packages are using your package's public API
|
7
7
|
- A `visibility` checker that allows packages to be private except to an explicit group of other packages.
|
8
|
-
-
|
8
|
+
- A `folder_visibility` checker that allows packages to their sibling packs and parent pack (to be used in an application that uses folder packs)
|
9
|
+
- An `architecture` checker that allows packages to specify their "layer" and requires that each layer only communicate with layers below it.
|
9
10
|
|
10
11
|
## Installation
|
11
12
|
|
@@ -24,6 +25,7 @@ Alternatively, you can require individual checkers:
|
|
24
25
|
require:
|
25
26
|
- packwerk/privacy/checker
|
26
27
|
- packwerk/visibility/checker
|
28
|
+
- packwerk/folder_visibility/checker
|
27
29
|
- packwerk/architecture/checker
|
28
30
|
```
|
29
31
|
|
@@ -54,9 +56,86 @@ Example:
|
|
54
56
|
public_path: my/custom/path/
|
55
57
|
```
|
56
58
|
|
59
|
+
### Defining public constants through sigil
|
60
|
+
|
61
|
+
> [!WARNING]
|
62
|
+
> This way of of defining the public API of a package should be considered WIP. It is not supported by all tooling in the RubyAtScale ecosystem, as @alexevanczuk pointed out in a [comment on the PR](https://github.com/rubyatscale/packwerk-extensions/pull/35#discussion_r1334331797):
|
63
|
+
>
|
64
|
+
> There are a couple of other places that will require changes related to this sigil. Namely, everything that is coupled to the public folder implementation of privacy.
|
65
|
+
>
|
66
|
+
> In the rubyatscale org:
|
67
|
+
>
|
68
|
+
> * pack_stats, example https://github.com/rubyatscale/pack_stats/blob/main/lib/pack_stats/private/metrics/public_usage.rb. (IMO though we can just remove this metric – it has never been useful)
|
69
|
+
> * Other places that mention public_path or app/public.
|
70
|
+
> * Org wide search for app/public link
|
71
|
+
> * Org wide search for public_path link
|
72
|
+
> * packs (the Rust port of packwerk – I could take this one over unless someone is interested in implementing whatever we come up with there
|
73
|
+
|
74
|
+
|
75
|
+
|
76
|
+
You may make individual files public withhin a private package by usage of a comment within the first 5 lines of the `.rb` file containing `pack_public: true`.
|
77
|
+
|
78
|
+
Example:
|
79
|
+
|
80
|
+
```ruby
|
81
|
+
# pack_public: true
|
82
|
+
module Foo
|
83
|
+
class Update
|
84
|
+
end
|
85
|
+
end
|
86
|
+
```
|
87
|
+
Now `Foo::Update` is considered public even though the `foo` package might be set to `enforce_private: (true || :strict)`.
|
88
|
+
|
89
|
+
It's important to note that when combining `public_api: true` with the declaration of `private_constants`,
|
90
|
+
`packwerk validate` will raise an exception if both are used for the same constant. This must be resolved by removing
|
91
|
+
the sigil from the `.rb` file or removing the constant from the list of `private_constants`.
|
92
|
+
|
93
|
+
If you are using rubocop, it may be configured in such a way that there must be an empty line after the magic keywords at the top of the file. Currently, this extension is not modifying rubocop in anyway so it does not recognize `public_pack: true` as a valid magic keyword option. That means placing it at the end of the magic keywords will throw a rubocop exception. However, you can place it first in the list to avoid an exception in rubocop.
|
94
|
+
```
|
95
|
+
-----
|
96
|
+
# typed: ignore
|
97
|
+
# frozen_string_literal: true
|
98
|
+
# pack_public: true
|
99
|
+
|
100
|
+
class Foo
|
101
|
+
...
|
102
|
+
end => Layout/EmptyLineAfterMagicComment: Add an empty line after magic comments.
|
103
|
+
|
104
|
+
------
|
105
|
+
# typed: ignore
|
106
|
+
# frozen_string_literal: true
|
107
|
+
|
108
|
+
# pack_public: true
|
109
|
+
|
110
|
+
class Foo
|
111
|
+
...
|
112
|
+
end => Less than ideal. This won't raise an issue in rubocop, however, only the first 5 lines are scanned for the magic comment of public_pack so there is risk at it being missed. It also is requiring extra empty lines in the group of magic comments.
|
113
|
+
|
114
|
+
-----
|
115
|
+
# pack_public: true
|
116
|
+
# typed: ignore
|
117
|
+
# frozen_string_literal: true
|
118
|
+
|
119
|
+
class Foo
|
120
|
+
...
|
121
|
+
end => Ideal solution. No exceptions from rubocop and very low risk of the magic comment being out of range since
|
122
|
+
```
|
123
|
+
|
57
124
|
### Using specific private constants
|
58
125
|
Sometimes it is desirable to only enforce privacy on a subset of constants in a package. You can do so by defining a `private_constants` list in your package.yml. Note that `enforce_privacy` must be set to `true` or `'strict'` for this to work.
|
59
126
|
|
127
|
+
### Ignore strict mode for violation coming from specific path patterns
|
128
|
+
If you want to activate `'strict'` mode on you package but have a few privacy violations you know you will deal with later,
|
129
|
+
you can set a list of patterns to exclude.
|
130
|
+
|
131
|
+
```yaml
|
132
|
+
enforce_privacy: strict
|
133
|
+
strict_privacy_ignored_patterns:
|
134
|
+
- engines/another_engine/test/**/*
|
135
|
+
```
|
136
|
+
|
137
|
+
In this example, violations on constants of your engine referenced in those files `engines/another_engine/test/**/*` will not fail Packwerk checks.
|
138
|
+
|
60
139
|
### Package Privacy violation
|
61
140
|
Packwerk thinks something is a privacy violation if you're referencing a constant, class, or module defined in the private implementation (i.e. not the public folder) of another package. We care about these because we want to make sure we only use parts of a package that have been exposed as public API.
|
62
141
|
|
@@ -87,6 +166,30 @@ visible_to:
|
|
87
166
|
- components/other_package
|
88
167
|
```
|
89
168
|
|
169
|
+
## Folder-Visibility Checker
|
170
|
+
The folder visibility checker can be used to allow a package to be private to their sibling packs and parent packs and will create todos if used by any other package.
|
171
|
+
|
172
|
+
To enforce visibility for your package, set `enforce_folder_visibility` to `true` on your pack.
|
173
|
+
|
174
|
+
```yaml
|
175
|
+
# components/merchandising/package.yml
|
176
|
+
enforce_folder_visibility: true
|
177
|
+
```
|
178
|
+
|
179
|
+
Here is an example of paths and whether their use of `packs/b/packs/e` is OK or not, assuming that protects itself via `enforce_folder_visibility`
|
180
|
+
|
181
|
+
```
|
182
|
+
. OK (parent of parent)
|
183
|
+
packs/a VIOLATION
|
184
|
+
packs/b OK (parent)
|
185
|
+
packs/b/packs/d OK (sibling)
|
186
|
+
packs/b/packs/e ENFORCE_NESTED_VISIBILITY: TRUE
|
187
|
+
packs/b/packs/e/packs/f VIOLATION
|
188
|
+
packs/b/packs/e/packs/g VIOLATION
|
189
|
+
packs/b/packs/h OK (sibling)
|
190
|
+
packs/c VIOLATION
|
191
|
+
```
|
192
|
+
|
90
193
|
## Architecture Checker
|
91
194
|
The architecture checker can be used to enforce constraints on what can depend on what.
|
92
195
|
|
@@ -105,3 +208,19 @@ layer: utility
|
|
105
208
|
```
|
106
209
|
|
107
210
|
Now this pack can only depend on other utility packages.
|
211
|
+
|
212
|
+
|
213
|
+
## Contributing
|
214
|
+
|
215
|
+
Got another checker you would like to add? Add it to this repo!
|
216
|
+
|
217
|
+
Please ensure these commands pass for you locally:
|
218
|
+
|
219
|
+
```
|
220
|
+
bundle
|
221
|
+
srb tc
|
222
|
+
bin/rubocop
|
223
|
+
bin/rake test
|
224
|
+
```
|
225
|
+
|
226
|
+
Then, submit a PR!
|
@@ -0,0 +1,93 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'packwerk/folder_visibility/package'
|
5
|
+
require 'packwerk/folder_visibility/validator'
|
6
|
+
|
7
|
+
module Packwerk
|
8
|
+
module FolderVisibility
|
9
|
+
class Checker
|
10
|
+
extend T::Sig
|
11
|
+
include Packwerk::Checker
|
12
|
+
|
13
|
+
VIOLATION_TYPE = T.let('folder_visibility', String)
|
14
|
+
|
15
|
+
sig { override.returns(String) }
|
16
|
+
def violation_type
|
17
|
+
VIOLATION_TYPE
|
18
|
+
end
|
19
|
+
|
20
|
+
sig do
|
21
|
+
override
|
22
|
+
.params(reference: Packwerk::Reference)
|
23
|
+
.returns(T::Boolean)
|
24
|
+
end
|
25
|
+
def invalid_reference?(reference)
|
26
|
+
referencing_package = reference.package
|
27
|
+
referenced_package = reference.constant.package
|
28
|
+
|
29
|
+
return false if enforcement_disabled?(Package.from(referenced_package).enforce_folder_visibility)
|
30
|
+
|
31
|
+
# the root pack is parent folder of all packs, so we short-circuit this here
|
32
|
+
referencing_package_is_root_pack = referencing_package.name == '.'
|
33
|
+
return false if referencing_package_is_root_pack
|
34
|
+
|
35
|
+
packages_are_sibling_folders = Pathname.new(referenced_package.name).dirname == Pathname.new(referencing_package.name).dirname
|
36
|
+
return false if packages_are_sibling_folders
|
37
|
+
|
38
|
+
referencing_package_is_parent_folder = Pathname.new(referenced_package.name).to_s.start_with?(referencing_package.name)
|
39
|
+
return false if referencing_package_is_parent_folder
|
40
|
+
|
41
|
+
true
|
42
|
+
end
|
43
|
+
|
44
|
+
sig do
|
45
|
+
override
|
46
|
+
.params(listed_offense: Packwerk::ReferenceOffense)
|
47
|
+
.returns(T::Boolean)
|
48
|
+
end
|
49
|
+
def strict_mode_violation?(listed_offense)
|
50
|
+
publishing_package = listed_offense.reference.constant.package
|
51
|
+
publishing_package.config['enforce_folder_visibility'] == 'strict'
|
52
|
+
end
|
53
|
+
|
54
|
+
sig do
|
55
|
+
override
|
56
|
+
.params(reference: Packwerk::Reference)
|
57
|
+
.returns(String)
|
58
|
+
end
|
59
|
+
def message(reference)
|
60
|
+
source_desc = "'#{reference.package}'"
|
61
|
+
|
62
|
+
message = <<~MESSAGE
|
63
|
+
Folder Visibility violation: '#{reference.constant.name}' belongs to '#{reference.constant.package}', which is not visible to #{source_desc} as it is not a sibling pack or parent pack.
|
64
|
+
Is there a different package to use instead, or should '#{reference.constant.package}' also be visible to #{source_desc}?
|
65
|
+
|
66
|
+
#{standard_help_message(reference)}
|
67
|
+
MESSAGE
|
68
|
+
|
69
|
+
message.chomp
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
sig do
|
75
|
+
params(visibility_option: T.nilable(T.any(T::Boolean, String)))
|
76
|
+
.returns(T::Boolean)
|
77
|
+
end
|
78
|
+
def enforcement_disabled?(visibility_option)
|
79
|
+
[false, nil].include?(visibility_option)
|
80
|
+
end
|
81
|
+
|
82
|
+
sig { params(reference: Reference).returns(String) }
|
83
|
+
def standard_help_message(reference)
|
84
|
+
standard_message = <<~MESSAGE.chomp
|
85
|
+
Inference details: this is a reference to #{reference.constant.name} which seems to be defined in #{reference.constant.location}.
|
86
|
+
To receive help interpreting or resolving this error message, see: https://github.com/Shopify/packwerk/blob/main/TROUBLESHOOT.md#Troubleshooting-violations
|
87
|
+
MESSAGE
|
88
|
+
|
89
|
+
standard_message.chomp
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Packwerk
|
5
|
+
module FolderVisibility
|
6
|
+
class Package < T::Struct
|
7
|
+
extend T::Sig
|
8
|
+
|
9
|
+
const :enforce_folder_visibility, T.nilable(T.any(T::Boolean, String))
|
10
|
+
|
11
|
+
class << self
|
12
|
+
extend T::Sig
|
13
|
+
|
14
|
+
sig { params(package: ::Packwerk::Package).returns(Package) }
|
15
|
+
def from(package)
|
16
|
+
Package.new(
|
17
|
+
enforce_folder_visibility: package.config['enforce_folder_visibility']
|
18
|
+
)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Packwerk
|
5
|
+
module FolderVisibility
|
6
|
+
class Validator
|
7
|
+
extend T::Sig
|
8
|
+
include Packwerk::Validator
|
9
|
+
|
10
|
+
Result = Packwerk::Validator::Result
|
11
|
+
|
12
|
+
sig { override.params(package_set: PackageSet, configuration: Configuration).returns(Result) }
|
13
|
+
def call(package_set, configuration)
|
14
|
+
results = T.let([], T::Array[Result])
|
15
|
+
|
16
|
+
package_manifests_settings_for(configuration, 'enforce_folder_visibility').each do |config, setting|
|
17
|
+
next if setting.nil?
|
18
|
+
|
19
|
+
next if [TrueClass, FalseClass].include?(setting.class) || setting == 'strict'
|
20
|
+
|
21
|
+
results << Result.new(
|
22
|
+
ok: false,
|
23
|
+
error_value: "\tInvalid 'enforce_folder_visibility' option: #{setting.inspect} in #{config.inspect}"
|
24
|
+
)
|
25
|
+
end
|
26
|
+
|
27
|
+
merge_results(results, separator: "\n---\n")
|
28
|
+
end
|
29
|
+
|
30
|
+
sig { override.returns(T::Array[String]) }
|
31
|
+
def permitted_keys
|
32
|
+
%w[enforce_folder_visibility]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -12,6 +12,37 @@ module Packwerk
|
|
12
12
|
include Packwerk::Checker
|
13
13
|
|
14
14
|
VIOLATION_TYPE = T.let('privacy', String)
|
15
|
+
PUBLICIZED_SIGIL = T.let('pack_public: true', String)
|
16
|
+
PUBLICIZED_SIGIL_REGEX = T.let(/#.*pack_public:\s*true/, Regexp)
|
17
|
+
@publicized_locations = T.let({}, T::Hash[String, T::Boolean])
|
18
|
+
|
19
|
+
class << self
|
20
|
+
extend T::Sig
|
21
|
+
|
22
|
+
sig { returns(T::Hash[String, T::Boolean]) }
|
23
|
+
def publicized_locations
|
24
|
+
@publicized_locations
|
25
|
+
end
|
26
|
+
|
27
|
+
sig { params(location: String).returns(T::Boolean) }
|
28
|
+
def publicized_location?(location)
|
29
|
+
unless publicized_locations.key?(location)
|
30
|
+
publicized_locations[location] = check_for_publicized_sigil(location)
|
31
|
+
end
|
32
|
+
|
33
|
+
T.must(publicized_locations[location])
|
34
|
+
end
|
35
|
+
|
36
|
+
sig { params(location: String).returns(T::Boolean) }
|
37
|
+
def check_for_publicized_sigil(location)
|
38
|
+
content_contains_sigil?(File.readlines(location))
|
39
|
+
end
|
40
|
+
|
41
|
+
sig { params(lines: T::Array[String]).returns(T::Boolean) }
|
42
|
+
def content_contains_sigil?(lines)
|
43
|
+
T.must(lines[0..4]).any? { |l| l =~ PUBLICIZED_SIGIL_REGEX }
|
44
|
+
end
|
45
|
+
end
|
15
46
|
|
16
47
|
sig { override.returns(String) }
|
17
48
|
def violation_type
|
@@ -28,6 +59,7 @@ module Packwerk
|
|
28
59
|
privacy_package = Package.from(constant_package)
|
29
60
|
|
30
61
|
return false if privacy_package.public_path?(reference.constant.location)
|
62
|
+
return false if self.class.publicized_location?(reference.constant.location)
|
31
63
|
|
32
64
|
privacy_option = privacy_package.enforce_privacy
|
33
65
|
return false if enforcement_disabled?(privacy_option)
|
@@ -44,7 +76,14 @@ module Packwerk
|
|
44
76
|
end
|
45
77
|
def strict_mode_violation?(listed_offense)
|
46
78
|
publishing_package = listed_offense.reference.constant.package
|
47
|
-
|
79
|
+
|
80
|
+
return false unless publishing_package.config['enforce_privacy'] == 'strict'
|
81
|
+
return false if exclude_from_strict?(
|
82
|
+
publishing_package.config['strict_privacy_ignored_patterns'] || [],
|
83
|
+
Pathname.new(listed_offense.reference.relative_path).cleanpath
|
84
|
+
)
|
85
|
+
|
86
|
+
true
|
48
87
|
end
|
49
88
|
|
50
89
|
sig do
|
@@ -98,6 +137,13 @@ module Packwerk
|
|
98
137
|
|
99
138
|
standard_message.chomp
|
100
139
|
end
|
140
|
+
|
141
|
+
sig { params(globs: T::Array[String], path: Pathname).returns(T::Boolean) }
|
142
|
+
def exclude_from_strict?(globs, path)
|
143
|
+
globs.any? do |glob|
|
144
|
+
path.fnmatch(glob, File::FNM_EXTGLOB)
|
145
|
+
end
|
146
|
+
end
|
101
147
|
end
|
102
148
|
end
|
103
149
|
end
|
@@ -11,6 +11,7 @@ module Packwerk
|
|
11
11
|
const :enforce_privacy, T.nilable(T.any(T::Boolean, String))
|
12
12
|
const :private_constants, T::Array[String]
|
13
13
|
const :ignored_private_constants, T::Array[String]
|
14
|
+
const :strict_privacy_ignored_patterns, T::Array[String]
|
14
15
|
|
15
16
|
sig { params(path: String).returns(T::Boolean) }
|
16
17
|
def public_path?(path)
|
@@ -27,7 +28,8 @@ module Packwerk
|
|
27
28
|
user_defined_public_path: user_defined_public_path(package),
|
28
29
|
enforce_privacy: package.config['enforce_privacy'],
|
29
30
|
private_constants: package.config['private_constants'] || [],
|
30
|
-
ignored_private_constants: package.config['ignored_private_constants'] || []
|
31
|
+
ignored_private_constants: package.config['ignored_private_constants'] || [],
|
32
|
+
strict_privacy_ignored_patterns: package.config['strict_privacy_ignored_patterns'] || []
|
31
33
|
)
|
32
34
|
end
|
33
35
|
|
@@ -31,7 +31,7 @@ module Packwerk
|
|
31
31
|
|
32
32
|
sig { override.returns(T::Array[String]) }
|
33
33
|
def permitted_keys
|
34
|
-
%w[public_path enforce_privacy private_constants ignored_private_constants]
|
34
|
+
%w[public_path enforce_privacy private_constants ignored_private_constants strict_privacy_ignored_patterns]
|
35
35
|
end
|
36
36
|
|
37
37
|
private
|
@@ -110,9 +110,8 @@ module Packwerk
|
|
110
110
|
def check_private_constant_location(configuration, package_set, name, location, config_file_path)
|
111
111
|
declared_package = package_set.package_from_path(relative_path(configuration, config_file_path))
|
112
112
|
constant_package = package_set.package_from_path(location)
|
113
|
-
|
114
113
|
if constant_package == declared_package
|
115
|
-
|
114
|
+
check_for_publicized_constant(location, constant_package, name)
|
116
115
|
else
|
117
116
|
Result.new(
|
118
117
|
ok: false,
|
@@ -122,6 +121,23 @@ module Packwerk
|
|
122
121
|
end
|
123
122
|
end
|
124
123
|
|
124
|
+
sig { params(location: String, constant_package: Packwerk::Package, name: T.untyped).returns(Result) }
|
125
|
+
def check_for_publicized_constant(location, constant_package, name)
|
126
|
+
if Packwerk::Privacy::Checker.publicized_location?(location)
|
127
|
+
sigil = Packwerk::Privacy::Checker::PUBLICIZED_SIGIL
|
128
|
+
Result.new(
|
129
|
+
ok: false,
|
130
|
+
error_value: "'#{name}' is an explicitly publicized constant declared in #{location} through usage of " \
|
131
|
+
"'#{sigil}'. However, the package '#{constant_package}' is also declaring it as a private " \
|
132
|
+
"constant. This conflict must be resolved. Either remove '#{sigil}' from #{location} or " \
|
133
|
+
'remove this constant from the list of private constants in the config for ' \
|
134
|
+
"'#{constant_package}'."
|
135
|
+
)
|
136
|
+
else
|
137
|
+
Result.new(ok: true)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
125
141
|
sig { params(constants: T.untyped, config_file_path: String).returns(T::Array[Result]) }
|
126
142
|
def assert_constants_can_be_loaded(constants, config_file_path)
|
127
143
|
constants.map do |constant|
|
data/lib/packwerk-extensions.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: packwerk-extensions
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.9
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gusto Engineers
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-10-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: packwerk
|
@@ -191,6 +191,9 @@ files:
|
|
191
191
|
- lib/packwerk/architecture/layers.rb
|
192
192
|
- lib/packwerk/architecture/package.rb
|
193
193
|
- lib/packwerk/architecture/validator.rb
|
194
|
+
- lib/packwerk/folder_visibility/checker.rb
|
195
|
+
- lib/packwerk/folder_visibility/package.rb
|
196
|
+
- lib/packwerk/folder_visibility/validator.rb
|
194
197
|
- lib/packwerk/privacy/checker.rb
|
195
198
|
- lib/packwerk/privacy/package.rb
|
196
199
|
- lib/packwerk/privacy/validator.rb
|