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