packwerk 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.github/ISSUE_TEMPLATE/bug_report.md +27 -0
- data/.github/probots.yml +2 -0
- data/.github/pull_request_template.md +27 -0
- data/.github/workflows/ci.yml +50 -0
- data/.gitignore +12 -0
- data/.rubocop.yml +46 -0
- data/.ruby-version +1 -0
- data/CODEOWNERS +1 -0
- data/CODE_OF_CONDUCT.md +76 -0
- data/CONTRIBUTING.md +17 -0
- data/Gemfile +22 -0
- data/Gemfile.lock +236 -0
- data/LICENSE.md +7 -0
- data/README.md +73 -0
- data/Rakefile +13 -0
- data/TROUBLESHOOT.md +67 -0
- data/USAGE.md +250 -0
- data/bin/console +15 -0
- data/bin/setup +8 -0
- data/dev.yml +32 -0
- data/docs/cohesion.png +0 -0
- data/exe/packwerk +6 -0
- data/lib/packwerk.rb +44 -0
- data/lib/packwerk/application_validator.rb +343 -0
- data/lib/packwerk/association_inspector.rb +44 -0
- data/lib/packwerk/checking_deprecated_references.rb +40 -0
- data/lib/packwerk/cli.rb +238 -0
- data/lib/packwerk/configuration.rb +82 -0
- data/lib/packwerk/const_node_inspector.rb +44 -0
- data/lib/packwerk/constant_discovery.rb +60 -0
- data/lib/packwerk/constant_name_inspector.rb +22 -0
- data/lib/packwerk/dependency_checker.rb +28 -0
- data/lib/packwerk/deprecated_references.rb +92 -0
- data/lib/packwerk/file_processor.rb +43 -0
- data/lib/packwerk/files_for_processing.rb +67 -0
- data/lib/packwerk/formatters/progress_formatter.rb +46 -0
- data/lib/packwerk/generators/application_validation.rb +62 -0
- data/lib/packwerk/generators/configuration_file.rb +69 -0
- data/lib/packwerk/generators/inflections_file.rb +43 -0
- data/lib/packwerk/generators/root_package.rb +37 -0
- data/lib/packwerk/generators/templates/inflections.yml +6 -0
- data/lib/packwerk/generators/templates/package.yml +17 -0
- data/lib/packwerk/generators/templates/packwerk +23 -0
- data/lib/packwerk/generators/templates/packwerk.yml.erb +23 -0
- data/lib/packwerk/generators/templates/packwerk_validator_test.rb +11 -0
- data/lib/packwerk/graph.rb +74 -0
- data/lib/packwerk/inflections/custom.rb +33 -0
- data/lib/packwerk/inflections/default.rb +73 -0
- data/lib/packwerk/inflector.rb +41 -0
- data/lib/packwerk/node.rb +259 -0
- data/lib/packwerk/node_processor.rb +49 -0
- data/lib/packwerk/node_visitor.rb +22 -0
- data/lib/packwerk/offense.rb +44 -0
- data/lib/packwerk/output_styles.rb +41 -0
- data/lib/packwerk/package.rb +56 -0
- data/lib/packwerk/package_set.rb +59 -0
- data/lib/packwerk/parsed_constant_definitions.rb +62 -0
- data/lib/packwerk/parsers.rb +23 -0
- data/lib/packwerk/parsers/erb.rb +66 -0
- data/lib/packwerk/parsers/factory.rb +34 -0
- data/lib/packwerk/parsers/ruby.rb +42 -0
- data/lib/packwerk/privacy_checker.rb +45 -0
- data/lib/packwerk/reference.rb +6 -0
- data/lib/packwerk/reference_extractor.rb +81 -0
- data/lib/packwerk/reference_lister.rb +23 -0
- data/lib/packwerk/run_context.rb +103 -0
- data/lib/packwerk/sanity_checker.rb +10 -0
- data/lib/packwerk/spring_command.rb +28 -0
- data/lib/packwerk/updating_deprecated_references.rb +51 -0
- data/lib/packwerk/version.rb +6 -0
- data/lib/packwerk/violation_type.rb +13 -0
- data/library.yml +6 -0
- data/packwerk.gemspec +58 -0
- data/service.yml +6 -0
- data/shipit.rubygems.yml +1 -0
- data/sorbet/config +2 -0
- data/sorbet/rbi/gems/actioncable@6.1.0.alpha-d80c18a391e33552ae2d943e68af56946f883f65.rbi +840 -0
- data/sorbet/rbi/gems/actionmailbox@6.1.0.alpha-d80c18a391e33552ae2d943e68af56946f883f65.rbi +571 -0
- data/sorbet/rbi/gems/actionmailer@6.1.0.alpha-d80c18a391e33552ae2d943e68af56946f883f65.rbi +568 -0
- data/sorbet/rbi/gems/actionpack@6.1.0.alpha-d80c18a391e33552ae2d943e68af56946f883f65.rbi +5216 -0
- data/sorbet/rbi/gems/actiontext@6.1.0.alpha-d80c18a391e33552ae2d943e68af56946f883f65.rbi +663 -0
- data/sorbet/rbi/gems/actionview@6.1.0.alpha-d80c18a391e33552ae2d943e68af56946f883f65.rbi +2504 -0
- data/sorbet/rbi/gems/activejob@6.1.0.alpha-d80c18a391e33552ae2d943e68af56946f883f65.rbi +635 -0
- data/sorbet/rbi/gems/activemodel@6.1.0.alpha-d80c18a391e33552ae2d943e68af56946f883f65.rbi +1201 -0
- data/sorbet/rbi/gems/activerecord@6.1.0.alpha-d80c18a391e33552ae2d943e68af56946f883f65.rbi +8011 -0
- data/sorbet/rbi/gems/activestorage@6.1.0.alpha-d80c18a391e33552ae2d943e68af56946f883f65.rbi +904 -0
- data/sorbet/rbi/gems/activesupport@6.1.0.alpha-d80c18a391e33552ae2d943e68af56946f883f65.rbi +3888 -0
- data/sorbet/rbi/gems/ast@2.4.1.rbi +54 -0
- data/sorbet/rbi/gems/better_html@1.0.15.rbi +317 -0
- data/sorbet/rbi/gems/builder@3.2.4.rbi +8 -0
- data/sorbet/rbi/gems/byebug@11.1.3.rbi +8 -0
- data/sorbet/rbi/gems/coderay@1.1.3.rbi +8 -0
- data/sorbet/rbi/gems/colorize@0.8.1.rbi +40 -0
- data/sorbet/rbi/gems/commander@4.5.2.rbi +8 -0
- data/sorbet/rbi/gems/concurrent-ruby@1.1.6.rbi +1966 -0
- data/sorbet/rbi/gems/constant_resolver@0.1.5.rbi +26 -0
- data/sorbet/rbi/gems/crass@1.0.6.rbi +138 -0
- data/sorbet/rbi/gems/erubi@1.9.0.rbi +39 -0
- data/sorbet/rbi/gems/globalid@0.4.2.rbi +178 -0
- data/sorbet/rbi/gems/highline@2.0.3.rbi +8 -0
- data/sorbet/rbi/gems/html_tokenizer@0.0.7.rbi +46 -0
- data/sorbet/rbi/gems/i18n@1.8.2.rbi +633 -0
- data/sorbet/rbi/gems/jaro_winkler@1.5.4.rbi +8 -0
- data/sorbet/rbi/gems/loofah@2.5.0.rbi +272 -0
- data/sorbet/rbi/gems/m@1.5.1.rbi +108 -0
- data/sorbet/rbi/gems/mail@2.7.1.rbi +2490 -0
- data/sorbet/rbi/gems/marcel@0.3.3.rbi +30 -0
- data/sorbet/rbi/gems/method_source@1.0.0.rbi +76 -0
- data/sorbet/rbi/gems/mimemagic@0.3.5.rbi +47 -0
- data/sorbet/rbi/gems/mini_mime@1.0.2.rbi +71 -0
- data/sorbet/rbi/gems/mini_portile2@2.4.0.rbi +8 -0
- data/sorbet/rbi/gems/minitest@5.14.0.rbi +542 -0
- data/sorbet/rbi/gems/mocha@1.11.2.rbi +964 -0
- data/sorbet/rbi/gems/nio4r@2.5.2.rbi +89 -0
- data/sorbet/rbi/gems/nokogiri@1.10.9.rbi +1608 -0
- data/sorbet/rbi/gems/parallel@1.19.1.rbi +8 -0
- data/sorbet/rbi/gems/parlour@4.0.1.rbi +561 -0
- data/sorbet/rbi/gems/parser@2.7.1.4.rbi +1632 -0
- data/sorbet/rbi/gems/pry@0.13.1.rbi +8 -0
- data/sorbet/rbi/gems/rack-test@1.1.0.rbi +335 -0
- data/sorbet/rbi/gems/rack@2.2.2.rbi +1730 -0
- data/sorbet/rbi/gems/rails-dom-testing@2.0.3.rbi +123 -0
- data/sorbet/rbi/gems/rails-html-sanitizer@1.3.0.rbi +213 -0
- data/sorbet/rbi/gems/rails@6.1.0.alpha-d80c18a391e33552ae2d943e68af56946f883f65.rbi +8 -0
- data/sorbet/rbi/gems/railties@6.1.0.alpha-d80c18a391e33552ae2d943e68af56946f883f65.rbi +869 -0
- data/sorbet/rbi/gems/rainbow@3.0.0.rbi +155 -0
- data/sorbet/rbi/gems/rake@13.0.1.rbi +841 -0
- data/sorbet/rbi/gems/rexml@3.2.4.rbi +8 -0
- data/sorbet/rbi/gems/rubocop-performance@1.5.2.rbi +8 -0
- data/sorbet/rbi/gems/rubocop-shopify@1.0.2.rbi +8 -0
- data/sorbet/rbi/gems/rubocop-sorbet@0.3.7.rbi +8 -0
- data/sorbet/rbi/gems/rubocop@0.82.0.rbi +8 -0
- data/sorbet/rbi/gems/ruby-progressbar@1.10.1.rbi +8 -0
- data/sorbet/rbi/gems/smart_properties@1.15.0.rbi +168 -0
- data/sorbet/rbi/gems/spoom@1.0.4.rbi +418 -0
- data/sorbet/rbi/gems/spring@2.1.0.rbi +160 -0
- data/sorbet/rbi/gems/sprockets-rails@3.2.1.rbi +431 -0
- data/sorbet/rbi/gems/sprockets@4.0.0.rbi +1132 -0
- data/sorbet/rbi/gems/tapioca@0.4.5.rbi +518 -0
- data/sorbet/rbi/gems/thor@1.0.1.rbi +892 -0
- data/sorbet/rbi/gems/tzinfo@2.0.2.rbi +547 -0
- data/sorbet/rbi/gems/unicode-display_width@1.7.0.rbi +8 -0
- data/sorbet/rbi/gems/websocket-driver@0.7.1.rbi +438 -0
- data/sorbet/rbi/gems/websocket-extensions@0.1.4.rbi +71 -0
- data/sorbet/rbi/gems/zeitwerk@2.3.0.rbi +8 -0
- data/sorbet/tapioca/require.rb +25 -0
- data/static/packwerk-check-demo.png +0 -0
- data/static/packwerk_check.gif +0 -0
- data/static/packwerk_check_violation.gif +0 -0
- data/static/packwerk_update.gif +0 -0
- data/static/packwerk_validate.gif +0 -0
- metadata +341 -0
@@ -0,0 +1,28 @@
|
|
1
|
+
# typed: true
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "packwerk/violation_type"
|
5
|
+
|
6
|
+
module Packwerk
|
7
|
+
class DependencyChecker
|
8
|
+
def violation_type
|
9
|
+
ViolationType::Dependency
|
10
|
+
end
|
11
|
+
|
12
|
+
def invalid_reference?(reference, reference_lister)
|
13
|
+
return unless reference.source_package
|
14
|
+
return unless reference.source_package.enforce_dependencies?
|
15
|
+
return if reference.source_package.dependency?(reference.constant.package)
|
16
|
+
return if reference_lister.listed?(reference, violation_type: violation_type)
|
17
|
+
true
|
18
|
+
end
|
19
|
+
|
20
|
+
def message_for(reference)
|
21
|
+
"Dependency violation: #{reference.constant.name} belongs to '#{reference.constant.package}', but " \
|
22
|
+
"'#{reference.source_package}' does not specify a dependency on " \
|
23
|
+
"'#{reference.constant.package}'.\n" \
|
24
|
+
"Are we missing an abstraction?\n" \
|
25
|
+
"Is the code making the reference, and the referenced constant, in the right packages?\n"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
# typed: true
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "sorbet-runtime"
|
5
|
+
require "yaml"
|
6
|
+
|
7
|
+
require "packwerk/reference"
|
8
|
+
require "packwerk/reference_lister"
|
9
|
+
require "packwerk/violation_type"
|
10
|
+
|
11
|
+
module Packwerk
|
12
|
+
class DeprecatedReferences
|
13
|
+
extend T::Sig
|
14
|
+
include ReferenceLister
|
15
|
+
|
16
|
+
def initialize(package, filepath)
|
17
|
+
@package = package
|
18
|
+
@filepath = filepath
|
19
|
+
@new_entries = {}
|
20
|
+
end
|
21
|
+
|
22
|
+
sig do
|
23
|
+
params(reference: Packwerk::Reference, violation_type: ViolationType)
|
24
|
+
.returns(T::Boolean)
|
25
|
+
.override
|
26
|
+
end
|
27
|
+
def listed?(reference, violation_type:)
|
28
|
+
violated_constants_found = deprecated_references.dig(reference.constant.package.name, reference.constant.name)
|
29
|
+
return false unless violated_constants_found
|
30
|
+
|
31
|
+
violated_constant_in_file = violated_constants_found.fetch("files", []).include?(reference.relative_path)
|
32
|
+
return false unless violated_constant_in_file
|
33
|
+
|
34
|
+
violated_constants_found.fetch("violations", []).include?(violation_type.serialize)
|
35
|
+
end
|
36
|
+
|
37
|
+
def add_entries(reference, violation_type)
|
38
|
+
package_violations = @new_entries.fetch(reference.constant.package.name, {})
|
39
|
+
entries_for_file = package_violations[reference.constant.name] ||= {}
|
40
|
+
|
41
|
+
entries_for_file["violations"] ||= []
|
42
|
+
entries_for_file["violations"] << violation_type
|
43
|
+
|
44
|
+
entries_for_file["files"] ||= []
|
45
|
+
entries_for_file["files"] << reference.relative_path.to_s
|
46
|
+
|
47
|
+
@new_entries[reference.constant.package.name] = package_violations
|
48
|
+
end
|
49
|
+
|
50
|
+
def dump
|
51
|
+
if @new_entries.empty?
|
52
|
+
File.delete(@filepath) if File.exist?(@filepath)
|
53
|
+
else
|
54
|
+
prepare_entries_for_dump
|
55
|
+
message = <<~MESSAGE
|
56
|
+
# This file contains a list of dependencies that are not part of the long term plan for #{@package.name}.
|
57
|
+
# We should generally work to reduce this list, but not at the expense of actually getting work done.
|
58
|
+
#
|
59
|
+
# You can regenerate this file using the following command:
|
60
|
+
#
|
61
|
+
# bundle exec packwerk update #{@package.name}
|
62
|
+
MESSAGE
|
63
|
+
File.open(@filepath, "w") do |f|
|
64
|
+
f.write(message)
|
65
|
+
f.write(@new_entries.to_yaml)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
def prepare_entries_for_dump
|
73
|
+
@new_entries.each do |package_name, package_violations|
|
74
|
+
package_violations.each do |_, entries_for_file|
|
75
|
+
entries_for_file["violations"].sort!.uniq!
|
76
|
+
entries_for_file["files"].sort!.uniq!
|
77
|
+
end
|
78
|
+
@new_entries[package_name] = package_violations.sort.to_h
|
79
|
+
end
|
80
|
+
|
81
|
+
@new_entries = @new_entries.sort.to_h
|
82
|
+
end
|
83
|
+
|
84
|
+
def deprecated_references
|
85
|
+
@deprecated_references ||= if File.exist?(@filepath)
|
86
|
+
YAML.load_file(@filepath) || {}
|
87
|
+
else
|
88
|
+
{}
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# typed: false
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "ast/node"
|
5
|
+
|
6
|
+
require "packwerk/node"
|
7
|
+
require "packwerk/offense"
|
8
|
+
require "packwerk/parsers"
|
9
|
+
|
10
|
+
module Packwerk
|
11
|
+
class FileProcessor
|
12
|
+
class UnknownFileTypeResult < Offense
|
13
|
+
def initialize(file:)
|
14
|
+
super(file: file, message: "unknown file type")
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def initialize(run_context:, parser_factory: nil)
|
19
|
+
@run_context = run_context
|
20
|
+
@parser_factory = parser_factory || Packwerk::Parsers::Factory.instance
|
21
|
+
end
|
22
|
+
|
23
|
+
def call(file_path)
|
24
|
+
parser = @parser_factory.for_path(file_path)
|
25
|
+
return [UnknownFileTypeResult.new(file: file_path)] if parser.nil?
|
26
|
+
|
27
|
+
node = File.open(file_path, "r", external_encoding: Encoding::UTF_8) do |file|
|
28
|
+
parser.call(io: file, file_path: file_path)
|
29
|
+
rescue Parsers::ParseError => e
|
30
|
+
return [e.result]
|
31
|
+
end
|
32
|
+
|
33
|
+
result = []
|
34
|
+
if node
|
35
|
+
@node_processor = @run_context.node_processor_for(filename: file_path, ast_node: node)
|
36
|
+
node_visitor = Packwerk::NodeVisitor.new(node_processor: @node_processor)
|
37
|
+
|
38
|
+
node_visitor.visit(node, ancestors: [], result: result)
|
39
|
+
end
|
40
|
+
result
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# typed: true
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Packwerk
|
5
|
+
class FilesForProcessing
|
6
|
+
class << self
|
7
|
+
def fetch(paths:, configuration:)
|
8
|
+
new(paths, configuration).files
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(paths, configuration)
|
13
|
+
@paths = paths
|
14
|
+
@configuration = configuration
|
15
|
+
end
|
16
|
+
|
17
|
+
def files
|
18
|
+
include_files = if custom_files.empty?
|
19
|
+
configured_included_files
|
20
|
+
else
|
21
|
+
custom_files
|
22
|
+
end
|
23
|
+
|
24
|
+
include_files - configured_excluded_files
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def custom_files
|
30
|
+
@custom_files ||= @paths.flat_map do |path|
|
31
|
+
path = File.expand_path(path, @configuration.root_path)
|
32
|
+
if File.file?(path)
|
33
|
+
path
|
34
|
+
else
|
35
|
+
custom_included_files(path)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def custom_included_files(path)
|
41
|
+
# Note, assuming include globs are always relative paths
|
42
|
+
absolute_includes = @configuration.include.map do |glob|
|
43
|
+
File.expand_path(glob, @configuration.root_path)
|
44
|
+
end
|
45
|
+
|
46
|
+
Dir.glob([File.join(path, "**", "*")]).select do |file_path|
|
47
|
+
absolute_includes.any? do |pattern|
|
48
|
+
File.fnmatch?(pattern, file_path, File::FNM_EXTGLOB)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def configured_included_files
|
54
|
+
files_for_globs(@configuration.include)
|
55
|
+
end
|
56
|
+
|
57
|
+
def configured_excluded_files
|
58
|
+
files_for_globs(@configuration.exclude)
|
59
|
+
end
|
60
|
+
|
61
|
+
def files_for_globs(globs)
|
62
|
+
globs
|
63
|
+
.flat_map { |glob| Dir[File.expand_path(glob, @configuration.root_path)] }
|
64
|
+
.uniq
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# typed: true
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "benchmark"
|
5
|
+
|
6
|
+
require "packwerk/inflector"
|
7
|
+
require "packwerk/output_styles"
|
8
|
+
|
9
|
+
module Packwerk
|
10
|
+
module Formatters
|
11
|
+
class ProgressFormatter
|
12
|
+
def initialize(out, style: OutputStyles::Plain)
|
13
|
+
@out = out
|
14
|
+
@style = style
|
15
|
+
end
|
16
|
+
|
17
|
+
def started(target_files)
|
18
|
+
files_size = target_files.size
|
19
|
+
files_string = Inflector.default.pluralize("file", files_size)
|
20
|
+
@out.puts("📦 Packwerk is inspecting #{files_size} #{files_string}")
|
21
|
+
end
|
22
|
+
|
23
|
+
def started_validation
|
24
|
+
@out.puts("📦 Packwerk is running validation...")
|
25
|
+
|
26
|
+
execution_time = Benchmark.realtime { yield }
|
27
|
+
finished(execution_time)
|
28
|
+
|
29
|
+
@out.puts("✅ Packages are valid. Use `packwerk check` to run static checks.")
|
30
|
+
end
|
31
|
+
|
32
|
+
def mark_as_inspected
|
33
|
+
@out.print(".")
|
34
|
+
end
|
35
|
+
|
36
|
+
def mark_as_failed
|
37
|
+
@out.print("#{@style.error}E#{@style.reset}")
|
38
|
+
end
|
39
|
+
|
40
|
+
def finished(execution_time)
|
41
|
+
@out.puts
|
42
|
+
@out.puts("📦 Finished in #{execution_time.round(2)} seconds")
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# typed: true
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Packwerk
|
5
|
+
module Generators
|
6
|
+
class ApplicationValidation
|
7
|
+
class << self
|
8
|
+
def generate(for_rails_app: false, root: ".", out: $stdout)
|
9
|
+
new(root, out: out).generate(for_rails_app: for_rails_app)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(root, out: $stdout)
|
14
|
+
@root = root
|
15
|
+
@out = out
|
16
|
+
end
|
17
|
+
|
18
|
+
def generate(for_rails_app:)
|
19
|
+
@out.puts("📦 Generating application validator...")
|
20
|
+
if for_rails_app
|
21
|
+
generate_packwerk_validate_script
|
22
|
+
else
|
23
|
+
generate_validation_test
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def generate_packwerk_validate_script
|
30
|
+
destination_file_path = File.join(@root, "bin")
|
31
|
+
FileUtils.mkdir_p(destination_file_path)
|
32
|
+
|
33
|
+
if File.exist?(File.join(destination_file_path, "packwerk"))
|
34
|
+
@out.puts("⚠️ Packwerk application validation bin script already exists.")
|
35
|
+
return true
|
36
|
+
end
|
37
|
+
|
38
|
+
source_file_path = File.expand_path("../templates/packwerk", __FILE__)
|
39
|
+
FileUtils.cp(source_file_path, destination_file_path)
|
40
|
+
|
41
|
+
@out.puts("✅ Packwerk application validation bin script generated in #{destination_file_path}")
|
42
|
+
true
|
43
|
+
end
|
44
|
+
|
45
|
+
def generate_validation_test
|
46
|
+
destination_file_path = File.join(@root, "test")
|
47
|
+
FileUtils.mkdir_p(destination_file_path)
|
48
|
+
|
49
|
+
if File.exist?(File.join(destination_file_path, "packwerk_validator_test.rb"))
|
50
|
+
@out.puts("⚠️ Packwerk application validation test already exists.")
|
51
|
+
return true
|
52
|
+
end
|
53
|
+
|
54
|
+
source_file_path = File.expand_path("../templates/packwerk_validator_test.rb", __FILE__)
|
55
|
+
FileUtils.cp(source_file_path, destination_file_path)
|
56
|
+
|
57
|
+
@out.puts("✅ Packwerk application validation test generated in #{destination_file_path}")
|
58
|
+
true
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# typed: true
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "packwerk/configuration"
|
5
|
+
require "erb"
|
6
|
+
|
7
|
+
module Packwerk
|
8
|
+
module Generators
|
9
|
+
class ConfigurationFile
|
10
|
+
extend T::Sig
|
11
|
+
|
12
|
+
CONFIGURATION_TEMPLATE_FILE_PATH = "templates/packwerk.yml.erb"
|
13
|
+
|
14
|
+
class << self
|
15
|
+
def generate(load_paths:, root:, out:)
|
16
|
+
new(load_paths: load_paths, root: root, out: out).generate
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
sig { params(load_paths: T::Array[String], root: String, out: T.any(StringIO, IO)).void }
|
21
|
+
def initialize(load_paths:, root:, out: $stdout)
|
22
|
+
@load_paths = load_paths
|
23
|
+
@root = root
|
24
|
+
@out = out
|
25
|
+
|
26
|
+
set_template_variables
|
27
|
+
end
|
28
|
+
|
29
|
+
sig { returns(T::Boolean) }
|
30
|
+
def generate
|
31
|
+
@out.puts("📦 Generating Packwerk configuration file...")
|
32
|
+
default_config_path = File.join(@root, ::Packwerk::Configuration::DEFAULT_CONFIG_PATH)
|
33
|
+
|
34
|
+
if File.exist?(default_config_path)
|
35
|
+
@out.puts("⚠️ Packwerk configuration file already exists.")
|
36
|
+
return true
|
37
|
+
end
|
38
|
+
|
39
|
+
File.write(default_config_path, render)
|
40
|
+
|
41
|
+
@out.puts("✅ Packwerk configuration file generated in #{default_config_path}")
|
42
|
+
true
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def set_template_variables
|
48
|
+
@load_paths_formatted = if @load_paths.empty?
|
49
|
+
"# load_paths:\n# - 'app/models'\n"
|
50
|
+
else
|
51
|
+
@load_paths.map { |path| "- #{path}\n" }.join
|
52
|
+
end
|
53
|
+
|
54
|
+
@load_paths_comment = unless @load_paths.empty?
|
55
|
+
"# These load paths were auto generated by Packwerk.\nload_paths:\n"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def render
|
60
|
+
ERB.new(template, trim_mode: "-").result(binding)
|
61
|
+
end
|
62
|
+
|
63
|
+
def template
|
64
|
+
template_file_path = File.join(__dir__, CONFIGURATION_TEMPLATE_FILE_PATH)
|
65
|
+
File.read(template_file_path)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# typed: true
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Packwerk
|
5
|
+
module Generators
|
6
|
+
class InflectionsFile
|
7
|
+
extend T::Sig
|
8
|
+
|
9
|
+
class << self
|
10
|
+
def generate(root:, out:)
|
11
|
+
new(root, out: out).generate
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize(root, out: $stdout)
|
16
|
+
@root = root
|
17
|
+
@out = out
|
18
|
+
end
|
19
|
+
|
20
|
+
sig { returns(T::Boolean) }
|
21
|
+
def generate
|
22
|
+
ruby_inflection_file_exist = Dir.glob("#{@root}/**/inflections.rb").any?
|
23
|
+
yaml_inflection_file_exist = Dir.glob("#{@root}/**/inflections.yml").any?
|
24
|
+
|
25
|
+
if !ruby_inflection_file_exist || yaml_inflection_file_exist
|
26
|
+
return true
|
27
|
+
end
|
28
|
+
|
29
|
+
@out.puts("📦 Generating `inflections.yml` file...")
|
30
|
+
|
31
|
+
destination_file_path = File.join(@root, "config")
|
32
|
+
FileUtils.mkdir_p(destination_file_path)
|
33
|
+
|
34
|
+
source_file_path = File.join(__dir__, "/templates/inflections.yml")
|
35
|
+
FileUtils.cp(source_file_path, destination_file_path)
|
36
|
+
|
37
|
+
@out.puts("✅ `inflections.yml` generated in #{destination_file_path}")
|
38
|
+
|
39
|
+
true
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|