nextgen 0.1.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 (102) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +21 -0
  3. data/README.md +143 -0
  4. data/config/generators.yml +146 -0
  5. data/exe/nextgen +13 -0
  6. data/lib/nextgen/actions/bundler.rb +62 -0
  7. data/lib/nextgen/actions/git.rb +56 -0
  8. data/lib/nextgen/actions/yarn.rb +32 -0
  9. data/lib/nextgen/actions.rb +155 -0
  10. data/lib/nextgen/cli.rb +19 -0
  11. data/lib/nextgen/commands/create.rb +312 -0
  12. data/lib/nextgen/ext/prompt/list.rb +13 -0
  13. data/lib/nextgen/ext/prompt/multilist.rb +16 -0
  14. data/lib/nextgen/generators/action_mailer.rb +26 -0
  15. data/lib/nextgen/generators/annotate.rb +2 -0
  16. data/lib/nextgen/generators/base.rb +60 -0
  17. data/lib/nextgen/generators/basic_auth.rb +5 -0
  18. data/lib/nextgen/generators/brakeman.rb +2 -0
  19. data/lib/nextgen/generators/bundler_audit.rb +2 -0
  20. data/lib/nextgen/generators/capybara_lockstep.rb +4 -0
  21. data/lib/nextgen/generators/clean_gemfile.rb +1 -0
  22. data/lib/nextgen/generators/dotenv.rb +3 -0
  23. data/lib/nextgen/generators/erb_lint.rb +11 -0
  24. data/lib/nextgen/generators/eslint.rb +24 -0
  25. data/lib/nextgen/generators/factory_bot_rails.rb +16 -0
  26. data/lib/nextgen/generators/git_safe.rb +4 -0
  27. data/lib/nextgen/generators/github_actions.rb +2 -0
  28. data/lib/nextgen/generators/good_migrations.rb +1 -0
  29. data/lib/nextgen/generators/home_controller.rb +3 -0
  30. data/lib/nextgen/generators/initial_git_commit.rb +5 -0
  31. data/lib/nextgen/generators/initial_migrations.rb +11 -0
  32. data/lib/nextgen/generators/letter_opener.rb +8 -0
  33. data/lib/nextgen/generators/node.rb +5 -0
  34. data/lib/nextgen/generators/open_browser_on_start.rb +12 -0
  35. data/lib/nextgen/generators/overcommit.rb +1 -0
  36. data/lib/nextgen/generators/pgcli_rails.rb +1 -0
  37. data/lib/nextgen/generators/rack_canonical_host.rb +8 -0
  38. data/lib/nextgen/generators/rack_mini_profiler.rb +2 -0
  39. data/lib/nextgen/generators/rspec_rails.rb +19 -0
  40. data/lib/nextgen/generators/rspec_system_testing.rb +5 -0
  41. data/lib/nextgen/generators/rubocop.rb +32 -0
  42. data/lib/nextgen/generators/shoulda.rb +6 -0
  43. data/lib/nextgen/generators/sidekiq.rb +30 -0
  44. data/lib/nextgen/generators/stylelint.rb +24 -0
  45. data/lib/nextgen/generators/thor.rb +2 -0
  46. data/lib/nextgen/generators/tomo.rb +10 -0
  47. data/lib/nextgen/generators/vite.rb +103 -0
  48. data/lib/nextgen/generators.rb +87 -0
  49. data/lib/nextgen/rails.rb +39 -0
  50. data/lib/nextgen/rails_options.rb +191 -0
  51. data/lib/nextgen/thor_extensions.rb +48 -0
  52. data/lib/nextgen/tidy_gemfile.rb +71 -0
  53. data/lib/nextgen/version.rb +3 -0
  54. data/lib/nextgen.rb +17 -0
  55. data/template/.editorconfig +14 -0
  56. data/template/.env.sample +2 -0
  57. data/template/.erb-lint.yml.tt +25 -0
  58. data/template/.eslintrc.cjs +26 -0
  59. data/template/.github/workflows/ci.yml.tt +142 -0
  60. data/template/.overcommit.yml.tt +86 -0
  61. data/template/.prettierrc.cjs +6 -0
  62. data/template/.rubocop.yml.tt +269 -0
  63. data/template/.stylelintrc.cjs +52 -0
  64. data/template/DEPLOYMENT.md +10 -0
  65. data/template/Procfile.tt +4 -0
  66. data/template/README.md.tt +52 -0
  67. data/template/Thorfile +7 -0
  68. data/template/app/controllers/concerns/basic_auth.rb +20 -0
  69. data/template/app/controllers/home_controller.rb +4 -0
  70. data/template/app/frontend/controllers/index.js +5 -0
  71. data/template/app/frontend/images/example.svg +3 -0
  72. data/template/app/frontend/stylesheets/base.css +8 -0
  73. data/template/app/frontend/stylesheets/index.css +3 -0
  74. data/template/app/frontend/stylesheets/reset.css +36 -0
  75. data/template/app/helpers/inline_svg_helper.rb +9 -0
  76. data/template/app/views/home/index.html.erb.tt +5 -0
  77. data/template/bin/setup +107 -0
  78. data/template/config/initializers/generators.rb +5 -0
  79. data/template/config/initializers/rack_mini_profiler.rb +4 -0
  80. data/template/config/initializers/sidekiq.rb +32 -0
  81. data/template/config/sidekiq.yml +5 -0
  82. data/template/lib/puma/plugin/open.rb +14 -0
  83. data/template/lib/tasks/auto_annotate_models.rake +52 -0
  84. data/template/lib/tasks/erblint.rake +11 -0
  85. data/template/lib/tasks/eslint.rake +11 -0
  86. data/template/lib/tasks/rubocop.rake +4 -0
  87. data/template/lib/tasks/stylelint.rake +11 -0
  88. data/template/lib/templates/rspec/system/system_spec.rb +10 -0
  89. data/template/lib/vite_inline_svg_file_loader.rb +25 -0
  90. data/template/package.json +6 -0
  91. data/template/postcss.config.js +3 -0
  92. data/template/spec/support/factory_bot.rb +3 -0
  93. data/template/spec/support/mailer.rb +5 -0
  94. data/template/spec/support/shoulda.rb +6 -0
  95. data/template/spec/support/system.rb +17 -0
  96. data/template/test/application_system_test_case.rb +18 -0
  97. data/template/test/helpers/inline_svg_helper_test.rb +23 -0
  98. data/template/test/support/factory_bot.rb +3 -0
  99. data/template/test/support/mailer.rb +3 -0
  100. data/template/test/support/shoulda.rb +6 -0
  101. data/template/test/support/vite.rb +5 -0
  102. metadata +220 -0
@@ -0,0 +1,8 @@
1
+ /*
2
+ This file is for base element styles, like:
3
+
4
+ - Any @font-face declarations needed custom web fonts.
5
+ - Default font-family on the body element.
6
+ - Default foreground, background, and link colors.
7
+ - Global CSS variables (declared on :root), such as color palette.
8
+ */
@@ -0,0 +1,3 @@
1
+ @import "modern-normalize";
2
+ @import "reset";
3
+ @import "base";
@@ -0,0 +1,36 @@
1
+ :root {
2
+ line-height: 1.5;
3
+ -webkit-font-smoothing: antialiased;
4
+ }
5
+
6
+ h1,
7
+ h2,
8
+ h3,
9
+ h4,
10
+ h5,
11
+ figure,
12
+ p,
13
+ ol,
14
+ ul {
15
+ margin: 0;
16
+ }
17
+
18
+ ol,
19
+ ul {
20
+ list-style: none;
21
+ padding-inline: 0;
22
+ }
23
+
24
+ h1,
25
+ h2,
26
+ h3,
27
+ h4,
28
+ h5 {
29
+ font-size: inherit;
30
+ font-weight: inherit;
31
+ }
32
+
33
+ img {
34
+ display: block;
35
+ max-inline-size: 100%;
36
+ }
@@ -0,0 +1,9 @@
1
+ module InlineSvgHelper
2
+ def inline_svg_tag(filename, title: nil)
3
+ svg = ViteInlineSvgFileLoader.named(filename)
4
+ svg = svg.sub(/\A<svg/, '<svg role="img"')
5
+ svg = svg.sub(/\A<svg.*?>/, safe_join(['\0', "\n", tag.title(title)])) if title.present?
6
+
7
+ svg.strip.html_safe # rubocop:disable Rails/OutputSafety
8
+ end
9
+ end
@@ -0,0 +1,5 @@
1
+ <%% provide(:title, "Home") %>
2
+ <p>Find me in app/views/home/index.html.erb</p>
3
+ <% if File.exist?("package.json") && File.read("package.json").match?(%r{@hotwired/stimulus}) -%>
4
+ <p>Stimulus says, <i data-controller=hello></i></p>
5
+ <% end -%>
@@ -0,0 +1,107 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # This script is a way to set up or update your development environment automatically.
4
+ # This script is idempotent, so that you can run it at any time and get an expectable outcome.
5
+ # Add necessary setup steps to this method.
6
+ def setup!
7
+ run "bundle install" if bundle_needed?
8
+ run "overcommit --install" if overcommit_installable?
9
+ run "bin/rails db:prepare" if database_present?
10
+ run "yarn install" if yarn_needed?
11
+ run "bin/rails tmp:create" if tmp_missing?
12
+ env ".env", from: ".env.sample"
13
+ run "bin/rails restart"
14
+
15
+ if git_safe_needed?
16
+ say_status :notice,
17
+ "Remember to run #{colorize("mkdir -p .git/safe", :yellow)} to trust the binstubs in this project",
18
+ :magenta
19
+ end
20
+
21
+ say_status :Ready!,
22
+ "Use #{colorize("bin/rails s", :yellow)} to start the app, " \
23
+ "or #{colorize("bin/rake", :yellow)} to run tests"
24
+ end
25
+
26
+ def run(command, echo: true, silent: false, exception: true)
27
+ say_status(:run, command, :blue) if echo
28
+ with_original_bundler_env do
29
+ options = silent ? {out: File::NULL, err: File::NULL} : {}
30
+ system(command, exception: exception, **options)
31
+ end
32
+ end
33
+
34
+ def run?(command)
35
+ run command, silent: true, echo: false, exception: false
36
+ end
37
+
38
+ def bundle_needed?
39
+ !run("bundle check", silent: true, exception: false)
40
+ end
41
+
42
+ def overcommit_installable?
43
+ File.exist?(".overcommit.yml") && !File.exist?(".git/hooks/overcommit-hook") && run?("overcommit -v")
44
+ end
45
+
46
+ def database_present?
47
+ File.exist?("config/database.yml")
48
+ end
49
+
50
+ def yarn_needed?
51
+ File.exist?("yarn.lock") && !run("yarn check --check-files", silent: true, exception: false)
52
+ end
53
+
54
+ def tmp_missing?
55
+ !Dir.exist?("tmp/pids")
56
+ end
57
+
58
+ def git_safe_needed?
59
+ ENV["PATH"].include?(".git/safe/../../bin") && !Dir.exist?(".git/safe")
60
+ end
61
+
62
+ def with_original_bundler_env(&block)
63
+ return yield unless defined?(Bundler)
64
+
65
+ Bundler.with_original_env(&block)
66
+ end
67
+
68
+ def env(env_file, from:)
69
+ return unless File.exist?(from)
70
+
71
+ unless File.exist?(env_file)
72
+ say_status(:copy, "#{from} → #{env_file}", :magenta)
73
+ require "fileutils"
74
+ FileUtils.cp(from, env_file)
75
+ end
76
+
77
+ keys = ->(f) { File.readlines(f).filter_map { |l| l[/^([^#\s][^=\s]*)/, 1] } }
78
+
79
+ missing = keys[from] - keys[env_file]
80
+ return if missing.empty?
81
+
82
+ say_status(:WARNING, "Your #{env_file} file is missing #{missing.join(", ")}. Refer to #{from} for details.", :red)
83
+ end
84
+
85
+ def say_status(label, message, color = :green)
86
+ label = label.to_s.rjust(12)
87
+ puts [colorize(label, color), message].join(" ")
88
+ end
89
+
90
+ def colorize(str, color)
91
+ return str unless color_supported?
92
+
93
+ code = {red: 31, green: 32, yellow: 33, blue: 34, magenta: 35}.fetch(color)
94
+ "\e[0;#{code};49m#{str}\e[0m"
95
+ end
96
+
97
+ def color_supported?
98
+ if ENV["TERM"] == "dumb" || !ENV["NO_COLOR"].to_s.empty?
99
+ false
100
+ else
101
+ [$stdout, $stderr].all? { |io| io.respond_to?(:tty?) && io.tty? }
102
+ end
103
+ end
104
+
105
+ Dir.chdir(File.expand_path("..", __dir__)) do
106
+ setup!
107
+ end
@@ -0,0 +1,5 @@
1
+ Rails.application.config.generators do |g|
2
+ # Disable generators we don't need.
3
+ g.javascripts false
4
+ g.stylesheets false
5
+ end
@@ -0,0 +1,4 @@
1
+ return unless defined?(Rack::MiniProfiler)
2
+
3
+ # https://github.com/MiniProfiler/rack-mini-profiler#configuration-options
4
+ Rack::MiniProfiler.config.enable_hotwire_turbo_drive_support = true
@@ -0,0 +1,32 @@
1
+ return unless defined?(Sidekiq)
2
+
3
+ # Disable SSL certificate verification if using Heroku Redis
4
+ # redis_opts = {
5
+ # ssl_params: {
6
+ # verify_mode: OpenSSL::SSL::VERIFY_NONE
7
+ # }
8
+ # }
9
+ # Sidekiq.configure_server do |config|
10
+ # config.redis = redis_opts
11
+ # end
12
+ # Sidekiq.configure_client do |config|
13
+ # config.redis = redis_opts
14
+ # end
15
+
16
+ # Enable Rails CurrentAttributes to flow transparently through to Sidekiq jobs
17
+ # require "sidekiq/middleware/current_attributes"
18
+ # Sidekiq::CurrentAttributes.persist(Myapp::Current)
19
+
20
+ require "sidekiq/web"
21
+
22
+ Sidekiq::Web.app_url = "/"
23
+
24
+ sidekiq_username = ENV.fetch("SIDEKIQ_WEB_USERNAME", nil)
25
+ sidekiq_password = ENV.fetch("SIDEKIQ_WEB_PASSWORD", nil)
26
+
27
+ Sidekiq::Web.use(Rack::Auth::Basic, "Sidekiq") do |username, password|
28
+ if sidekiq_username.present? && sidekiq_password.present?
29
+ ActiveSupport::SecurityUtils.secure_compare(username, sidekiq_username) &
30
+ ActiveSupport::SecurityUtils.secure_compare(password, sidekiq_password)
31
+ end
32
+ end
@@ -0,0 +1,5 @@
1
+ ---
2
+ :queues:
3
+ - default
4
+
5
+ :concurrency: <%= ENV["SIDEKIQ_CONCURRENCY"] || ENV["RAILS_MAX_THREADS"] || 5 %>
@@ -0,0 +1,14 @@
1
+ require "puma/plugin"
2
+
3
+ Puma::Plugin.create do
4
+ def start(launcher)
5
+ return unless defined?(Rails) && defined?(Launchy)
6
+ return unless Rails.env.development?
7
+
8
+ binding = launcher.options[:binds].grep(/^tcp|ssl/).first
9
+ return if binding.nil?
10
+
11
+ url = binding.sub(/^tcp/, "http").sub(/^ssl/, "https").sub("0.0.0.0", "localhost")
12
+ Launchy.open(url)
13
+ end
14
+ end
@@ -0,0 +1,52 @@
1
+ return unless Rails.env.development?
2
+
3
+ require "annotate"
4
+ task :set_annotation_options do # rubocop:disable Rake/Desc, Metrics/BlockLength
5
+ # You can override any of these by setting an environment variable of the same name.
6
+ Annotate.set_defaults(
7
+ "routes" => "false",
8
+ "models" => "true",
9
+ "position_in_routes" => "before",
10
+ "position_in_class" => "before",
11
+ "position_in_test" => "before",
12
+ "position_in_fixture" => "before",
13
+ "position_in_factory" => "before",
14
+ "position_in_serializer" => "before",
15
+ "show_foreign_keys" => "true",
16
+ "show_complete_foreign_keys" => "false",
17
+ "show_indexes" => "true",
18
+ "simple_indexes" => "false",
19
+ "model_dir" => "app/models",
20
+ "root_dir" => "",
21
+ "include_version" => "false",
22
+ "require" => "",
23
+ "exclude_tests" => "true",
24
+ "exclude_fixtures" => "false",
25
+ "exclude_factories" => "false",
26
+ "exclude_serializers" => "false",
27
+ "exclude_scaffolds" => "true",
28
+ "exclude_controllers" => "true",
29
+ "exclude_helpers" => "true",
30
+ "exclude_sti_subclasses" => "false",
31
+ "ignore_model_sub_dir" => "false",
32
+ "ignore_columns" => nil,
33
+ "ignore_routes" => nil,
34
+ "ignore_unknown_models" => "false",
35
+ "hide_limit_column_types" => "integer,bigint,boolean",
36
+ "hide_default_column_types" => "json,jsonb,hstore",
37
+ "skip_on_db_migrate" => "false",
38
+ "format_bare" => "true",
39
+ "format_rdoc" => "false",
40
+ "format_markdown" => "false",
41
+ "sort" => "false",
42
+ "force" => "false",
43
+ "frozen" => "false",
44
+ "classified_sort" => "true",
45
+ "trace" => "false",
46
+ "wrapper_open" => nil,
47
+ "wrapper_close" => nil,
48
+ "with_comment" => "true"
49
+ )
50
+ end
51
+
52
+ Annotate.load_tasks
@@ -0,0 +1,11 @@
1
+ desc "Run erblint"
2
+ task :erblint do
3
+ sh "bin/erblint --lint-all"
4
+ end
5
+
6
+ namespace :erblint do
7
+ desc "Autocorrect erblint offenses"
8
+ task :autocorrect do
9
+ sh "bin/erblint --lint-all -a"
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ desc "Run ESLint"
2
+ task :eslint do
3
+ sh "yarn lint:js"
4
+ end
5
+
6
+ namespace :eslint do
7
+ desc "Autocorrect ESLint offenses"
8
+ task :autocorrect do
9
+ sh "yarn fix:js"
10
+ end
11
+ end
@@ -0,0 +1,4 @@
1
+ return unless Gem.loaded_specs.key?("rubocop")
2
+
3
+ require "rubocop/rake_task"
4
+ RuboCop::RakeTask.new
@@ -0,0 +1,11 @@
1
+ desc "Run Stylelint"
2
+ task :stylelint do
3
+ sh "yarn lint:css"
4
+ end
5
+
6
+ namespace :stylelint do
7
+ desc "Autocorrect Stylelint offenses"
8
+ task :autocorrect do
9
+ sh "yarn fix:css"
10
+ end
11
+ end
@@ -0,0 +1,10 @@
1
+ require "rails_helper"
2
+
3
+ RSpec.describe "<%= class_name %>" do
4
+ pending "add some scenarios (or delete) #{__FILE__}"
5
+
6
+ # it "renders a message on the home page" do
7
+ # visit "/"
8
+ # expect(page).to have_content("Hello, world!")
9
+ # end
10
+ end
@@ -0,0 +1,25 @@
1
+ module ViteInlineSvgFileLoader
2
+ class << self
3
+ def named(filename)
4
+ vite = ViteRuby.instance
5
+ vite_asset_path = vite.manifest.path_for(filename)
6
+
7
+ if vite.dev_server_running?
8
+ fetch_from_dev_server(vite_asset_path)
9
+ else
10
+ Rails.public_path.join(vite_asset_path.sub(%r{^/}, "")).read
11
+ end
12
+ end
13
+
14
+ private
15
+
16
+ def fetch_from_dev_server(path)
17
+ config = ViteRuby.config
18
+ dev_server_uri = URI("#{config.protocol}://#{config.host_with_port}#{path}")
19
+ response = Net::HTTP.get_response(dev_server_uri)
20
+ raise "Failed to load inline SVG from #{dev_server_uri}" unless response.is_a?(Net::HTTPSuccess)
21
+
22
+ response.body
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,6 @@
1
+ {
2
+ "private": true,
3
+ "engines": {
4
+ "node": ">=18.0.0"
5
+ }
6
+ }
@@ -0,0 +1,3 @@
1
+ module.exports = {
2
+ plugins: [require("autoprefixer")],
3
+ };
@@ -0,0 +1,3 @@
1
+ RSpec.configure do |config|
2
+ config.include FactoryBot::Syntax::Methods
3
+ end
@@ -0,0 +1,5 @@
1
+ RSpec.configure do |config|
2
+ config.before(:each) do
3
+ ActionMailer::Base.deliveries.clear
4
+ end
5
+ end
@@ -0,0 +1,6 @@
1
+ Shoulda::Matchers.configure do |config|
2
+ config.integrate do |with|
3
+ with.test_framework :rspec
4
+ with.library :rails
5
+ end
6
+ end
@@ -0,0 +1,17 @@
1
+ require "capybara/rails"
2
+ require "capybara/rspec"
3
+
4
+ Capybara.default_max_wait_time = 2
5
+ Capybara.disable_animation = true
6
+
7
+ RSpec.configure do |config|
8
+ config.before(:each, type: :system) do
9
+ driven_by :selenium,
10
+ using: (ENV["SHOW_BROWSER"] ? :chrome : :headless_chrome),
11
+ screen_size: [1400, 1400] do |options|
12
+ # Allows running in Docker
13
+ options.add_argument("--disable-dev-shm-usage")
14
+ options.add_argument("--no-sandbox")
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,18 @@
1
+ require "test_helper"
2
+ require "capybara/rails"
3
+
4
+ class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
5
+ driven_by :selenium,
6
+ using: (ENV["SHOW_BROWSER"] ? :chrome : :headless_chrome),
7
+ screen_size: [1400, 1400] do |options|
8
+ # Allows running in Docker
9
+ options.add_argument("--disable-dev-shm-usage")
10
+ options.add_argument("--no-sandbox")
11
+ end
12
+
13
+ setup do
14
+ Capybara.default_max_wait_time = 2
15
+ Capybara.disable_animation = true
16
+ Capybara.server = :puma, {Silent: true}
17
+ end
18
+ end
@@ -0,0 +1,23 @@
1
+ require "test_helper"
2
+
3
+ class InlineSvgHelperTest < ActionView::TestCase
4
+ test "inline_svg_tag returns contents of svg file as html_safe string" do
5
+ svg_result = inline_svg_tag("images/example.svg")
6
+ assert_predicate(svg_result, :html_safe?)
7
+ assert_equal(<<~EXPECTED_SVG.strip, svg_result)
8
+ <svg role="img" width="180" height="180" viewBox="0 0 180 180" fill="none" xmlns="http://www.w3.org/2000/svg">
9
+ <rect x="2" y="2" width="176" height="176" rx="6" fill="white" stroke="#CCCCCC" stroke-width="4" stroke-miterlimit="0" stroke-linecap="round"/>
10
+ </svg>
11
+ EXPECTED_SVG
12
+ end
13
+
14
+ test "inline_svg_tag adds a <title> to the svg if specified" do
15
+ svg_result = inline_svg_tag("images/example.svg", title: "rounded box")
16
+ assert_equal(<<~EXPECTED_SVG.strip, svg_result)
17
+ <svg role="img" width="180" height="180" viewBox="0 0 180 180" fill="none" xmlns="http://www.w3.org/2000/svg">
18
+ <title>rounded box</title>
19
+ <rect x="2" y="2" width="176" height="176" rx="6" fill="white" stroke="#CCCCCC" stroke-width="4" stroke-miterlimit="0" stroke-linecap="round"/>
20
+ </svg>
21
+ EXPECTED_SVG
22
+ end
23
+ end
@@ -0,0 +1,3 @@
1
+ ActiveSupport.on_load(:active_support_test_case) do
2
+ include FactoryBot::Syntax::Methods
3
+ end
@@ -0,0 +1,3 @@
1
+ ActiveSupport.on_load(:active_support_test_case) do
2
+ setup { ActionMailer::Base.deliveries.clear }
3
+ end
@@ -0,0 +1,6 @@
1
+ Shoulda::Matchers.configure do |config|
2
+ config.integrate do |with|
3
+ with.test_framework :minitest
4
+ with.library :rails
5
+ end
6
+ end
@@ -0,0 +1,5 @@
1
+ return if ViteRuby.config.auto_build
2
+
3
+ # Compile assets once at the start of testing
4
+ millis = Benchmark.ms { ViteRuby.commands.build }
5
+ puts format("Built Vite assets (%.1fms)", millis)