vite_rails 1.0.8 → 2.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.
Files changed (71) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +27 -1
  3. data/CONTRIBUTING.md +0 -1
  4. data/README.md +57 -32
  5. data/lib/tasks/vite.rake +17 -0
  6. data/lib/vite_rails.rb +5 -93
  7. data/lib/vite_rails/config.rb +11 -100
  8. data/lib/vite_rails/engine.rb +7 -11
  9. data/lib/vite_rails/installation.rb +47 -0
  10. data/lib/vite_rails/tag_helpers.rb +61 -0
  11. data/lib/vite_rails/version.rb +2 -2
  12. data/{lib/install/config/vite.json → templates/config/rails-vite.json} +1 -0
  13. data/{lib/install/javascript → templates}/entrypoints/application.js +0 -0
  14. metadata +25 -129
  15. data/lib/install/bin/vite +0 -17
  16. data/lib/install/binstubs.rb +0 -6
  17. data/lib/install/config/vite.config.ts +0 -11
  18. data/lib/install/template.rb +0 -40
  19. data/lib/tasks/vite/binstubs.rake +0 -12
  20. data/lib/tasks/vite/build.rake +0 -33
  21. data/lib/tasks/vite/clean.rake +0 -25
  22. data/lib/tasks/vite/clobber.rake +0 -20
  23. data/lib/tasks/vite/info.rake +0 -20
  24. data/lib/tasks/vite/install.rake +0 -12
  25. data/lib/tasks/vite/install_dependencies.rake +0 -20
  26. data/lib/tasks/vite/verify_install.rake +0 -23
  27. data/lib/vite_rails/builder.rb +0 -113
  28. data/lib/vite_rails/commands.rb +0 -68
  29. data/lib/vite_rails/dev_server.rb +0 -23
  30. data/lib/vite_rails/dev_server_proxy.rb +0 -49
  31. data/lib/vite_rails/helper.rb +0 -67
  32. data/lib/vite_rails/manifest.rb +0 -138
  33. data/lib/vite_rails/runner.rb +0 -56
  34. data/package.json +0 -16
  35. data/package/default.vite.json +0 -15
  36. data/test/builder_test.rb +0 -72
  37. data/test/command_test.rb +0 -35
  38. data/test/configuration_test.rb +0 -80
  39. data/test/dev_server_runner_test.rb +0 -83
  40. data/test/dev_server_test.rb +0 -39
  41. data/test/engine_rake_tasks_test.rb +0 -42
  42. data/test/helper_test.rb +0 -138
  43. data/test/manifest_test.rb +0 -75
  44. data/test/mode_test.rb +0 -21
  45. data/test/mounted_app/Rakefile +0 -6
  46. data/test/mounted_app/test/dummy/Rakefile +0 -5
  47. data/test/mounted_app/test/dummy/bin/rails +0 -5
  48. data/test/mounted_app/test/dummy/bin/rake +0 -5
  49. data/test/mounted_app/test/dummy/config.ru +0 -7
  50. data/test/mounted_app/test/dummy/config/application.rb +0 -12
  51. data/test/mounted_app/test/dummy/config/environment.rb +0 -5
  52. data/test/mounted_app/test/dummy/config/vite.json +0 -20
  53. data/test/mounted_app/test/dummy/package.json +0 -7
  54. data/test/rake_tasks_test.rb +0 -74
  55. data/test/test_app/Rakefile +0 -5
  56. data/test/test_app/app/javascript/entrypoints/application.js +0 -2
  57. data/test/test_app/app/javascript/entrypoints/multi_entry.css +0 -4
  58. data/test/test_app/app/javascript/entrypoints/multi_entry.js +0 -4
  59. data/test/test_app/bin/vite +0 -17
  60. data/test/test_app/config.ru +0 -7
  61. data/test/test_app/config/application.rb +0 -13
  62. data/test/test_app/config/environment.rb +0 -6
  63. data/test/test_app/config/vite.json +0 -20
  64. data/test/test_app/config/vite_public_root.yml +0 -20
  65. data/test/test_app/package.json +0 -13
  66. data/test/test_app/public/vite/manifest.json +0 -36
  67. data/test/test_app/some.config.js +0 -0
  68. data/test/test_app/yarn.lock +0 -11
  69. data/test/test_helper.rb +0 -34
  70. data/test/vite_runner_test.rb +0 -59
  71. data/test/webpacker_test.rb +0 -15
@@ -1,12 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- binstubs_template_path = File.expand_path('../../install/binstubs.rb', __dir__).freeze
4
- bin_path = ENV['BUNDLE_BIN'] || Rails.root.join('bin')
5
-
6
- namespace :vite do
7
- desc 'Installs Vite binstubs in this application'
8
- task :binstubs do |task|
9
- prefix = task.name.split(/#|vite:binstubs/).first
10
- exec "#{ RbConfig.ruby } #{ bin_path }/rails #{ prefix }app:template LOCATION=#{ binstubs_template_path }"
11
- end
12
- end
@@ -1,33 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- $stdout.sync = true
4
-
5
- def enhance_assets_precompile
6
- Rake::Task['assets:precompile'].enhance do |task|
7
- prefix = task.name.split(/#|assets:precompile/).first
8
-
9
- Rake::Task["#{ prefix }vite:build"].invoke
10
- end
11
- end
12
-
13
- namespace :vite do
14
- desc 'Compile JavaScript packs using vite for production with digests'
15
- task build: [:'vite:verify_install', :environment] do
16
- ViteRails.with_node_env(ENV.fetch('NODE_ENV', 'production')) do
17
- ViteRails.ensure_log_goes_to_stdout do
18
- ViteRails.build || exit!
19
- end
20
- end
21
- end
22
- end
23
-
24
- # Compile packs after we've compiled all other assets during precompilation
25
- skip_vite_precompile = %w[no false n f].include?(ENV['VITE_RUBY_PRECOMPILE'])
26
-
27
- unless skip_vite_precompile
28
- if Rake::Task.task_defined?('assets:precompile')
29
- enhance_assets_precompile
30
- else
31
- Rake::Task.define_task('assets:precompile' => [:'vite:install_dependencies', :'vite:build'])
32
- end
33
- end
@@ -1,25 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- $stdout.sync = true
4
-
5
- namespace :vite do
6
- desc 'Remove old compiled vites'
7
- task :clean, [:keep, :age] => [:'vite:verify_install', :environment] do |_, args|
8
- ViteRails.ensure_log_goes_to_stdout do
9
- ViteRails.clean(keep_up_to: Integer(args.keep || 2), age_in_seconds: Integer(args.age || 3600))
10
- end
11
- end
12
- end
13
-
14
- skip_vite_clean = %w[no false n f].include?(ENV['VITE_RUBY_PRECOMPILE'])
15
-
16
- unless skip_vite_clean
17
- # Run clean if the assets:clean is run
18
- if Rake::Task.task_defined?('assets:clean')
19
- Rake::Task['assets:clean'].enhance do
20
- Rake::Task['vite:clean'].invoke
21
- end
22
- else
23
- Rake::Task.define_task('assets:clean' => 'vite:clean')
24
- end
25
- end
@@ -1,20 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- namespace :vite do
4
- desc 'Remove the vite build output directory'
5
- task clobber: [:'vite:verify_install', :environment] do
6
- ViteRails.clobber
7
- $stdout.puts "Removed vite build output directory #{ ViteRails.config.build_output_dir }"
8
- end
9
- end
10
-
11
- skip_vite_clobber = %w[no false n f].include?(ENV['VITE_RUBY_PRECOMPILE'])
12
-
13
- unless skip_vite_clobber
14
- # Run clobber if the assets:clobber is run
15
- if Rake::Task.task_defined?('assets:clobber')
16
- Rake::Task['assets:clobber'].enhance do
17
- Rake::Task['vite:clobber'].invoke
18
- end
19
- end
20
- end
@@ -1,20 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- namespace :vite do
4
- desc "Provide information on ViteRails's environment"
5
- task :info do
6
- Dir.chdir(Rails.root) do
7
- $stdout.puts "Ruby: #{ `ruby --version` }"
8
- $stdout.puts "Rails: #{ Rails.version }"
9
- $stdout.puts "ViteRails: #{ ViteRails::VERSION }"
10
- $stdout.puts "Node: #{ `node --version` }"
11
- $stdout.puts "Yarn: #{ `yarn --version` }"
12
-
13
- $stdout.puts "\n"
14
- $stdout.puts "vite-plugin-ruby: \n#{ `npm list vite-plugin-ruby version` }"
15
-
16
- $stdout.puts "Is bin/vite present?: #{ File.exist? 'bin/vite' }"
17
- $stdout.puts "Is bin/yarn present?: #{ File.exist? 'bin/yarn' }"
18
- end
19
- end
20
- end
@@ -1,12 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- install_template_path = File.expand_path('../../install/template.rb', __dir__).freeze
4
- bin_path = ENV['BUNDLE_BIN'] || Rails.root.join('bin')
5
-
6
- namespace :vite do
7
- desc 'Install ViteRails in this application'
8
- task :install do |task|
9
- prefix = task.name.split(/#|vite:install/).first
10
- exec "#{ RbConfig.ruby } #{ bin_path }/rails #{ prefix }app:template LOCATION=#{ install_template_path }"
11
- end
12
- end
@@ -1,20 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- namespace :vite do
4
- desc 'Support for older Rails versions. Install all JavaScript dependencies as specified via Yarn'
5
- task :install_dependencies do
6
- valid_node_envs = %w[test development production]
7
- node_env = ENV.fetch('NODE_ENV') { valid_node_envs.include?(Rails.env) ? Rails.env : 'production' }
8
- Dir.chdir(Rails.root) do
9
- install_command = if Rails.root.join('yarn.lock').exist?
10
- v1 = `yarn --version`.start_with?('1')
11
- "yarn install #{ v1 ? '--no-progress --frozen-lockfile' : '--immutable' }"
12
- elsif Rails.root.join('pnpm-lock.yaml').exist?
13
- 'pnpm install'
14
- else
15
- 'npm ci'
16
- end
17
- system({ 'NODE_ENV' => node_env }, install_command)
18
- end
19
- end
20
- end
@@ -1,23 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- namespace :vite do
4
- desc 'Verifies if Vite Rails is installed'
5
- task :verify_install do
6
- unless File.exist?(Rails.root.join('bin/vite'))
7
- warn <<~WARN
8
- vite binstub not found.
9
- Have you run rails vite:install?
10
- Make sure the bin directory and bin/vite are not included in .gitignore
11
- WARN
12
- exit!
13
- end
14
- unless ViteRails.config.config_path.exist?
15
- path = ViteRails.config.config_path.relative_path_from(Pathname.new(pwd)).to_s
16
- warn <<~WARN
17
- Configuration #{ path } file for vite-plugin-ruby not found.
18
- Make sure vite:install has run successfully before running dependent tasks.
19
- WARN
20
- exit!
21
- end
22
- end
23
- end
@@ -1,113 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'open3'
4
- require 'digest/sha1'
5
-
6
- # Public: Keeps track of watched files and triggers builds as needed.
7
- class ViteRails::Builder
8
- def initialize(vite_rails)
9
- @vite_rails = vite_rails
10
- end
11
-
12
- # Public: Checks if the watched files have changed since the last compilation,
13
- # and triggers a Vite build if any files have changed.
14
- def build
15
- if stale?
16
- build_with_vite.tap { record_files_digest }
17
- else
18
- logger.debug 'Skipping build. Vite assets are already up-to-date ⚡️'
19
- true
20
- end
21
- end
22
-
23
- # Public: Returns true if all the assets built by Vite are up to date.
24
- def fresh?
25
- previous_files_digest&.== watched_files_digest
26
- end
27
-
28
- # Public: Returns true if any of the assets built by Vite is out of date.
29
- def stale?
30
- !fresh?
31
- end
32
-
33
- private
34
-
35
- delegate :config, :logger, to: :@vite_rails
36
-
37
- # Internal: Writes a digest of the watched files to disk for future checks.
38
- def record_files_digest
39
- config.build_cache_dir.mkpath
40
- files_digest_path.write(watched_files_digest)
41
- end
42
-
43
- # Internal: The path of where a digest of the watched files is stored.
44
- def files_digest_path
45
- config.build_cache_dir.join("last-compilation-digest-#{ config.mode }")
46
- end
47
-
48
- # Internal: Reads a digest of watched files from disk.
49
- def previous_files_digest
50
- files_digest_path.read if files_digest_path.exist? && config.manifest_path.exist?
51
- rescue Errno::ENOENT, Errno::ENOTDIR
52
- end
53
-
54
- # Internal: Returns a digest of all the watched files, allowing to detect
55
- # changes, and skip Vite builds if no files have changed.
56
- def watched_files_digest
57
- Dir.chdir File.expand_path(config.root) do
58
- files = Dir[*watched_paths].reject { |f| File.directory?(f) }
59
- file_ids = files.sort.map { |f| "#{ File.basename(f) }/#{ Digest::SHA1.file(f).hexdigest }" }
60
- Digest::SHA1.hexdigest(file_ids.join('/'))
61
- end
62
- end
63
-
64
- # Public: Initiates a Vite build command to generate assets.
65
- #
66
- # Returns true if the build is successful, or false if it failed.
67
- def build_with_vite
68
- logger.info 'Building with Vite ⚡️'
69
-
70
- stdout, stderr, status = Open3.capture3(vite_env,
71
- "#{ which_ruby } ./bin/vite build --mode #{ config.mode }", chdir: File.expand_path(config.root))
72
-
73
- if status.success?
74
- logger.info "Build with Vite complete: #{ config.build_output_dir }"
75
- logger.error(stderr.to_s) unless stderr.empty?
76
- logger.info(stdout) unless config.hide_build_console_output
77
- else
78
- non_empty_streams = [stdout, stderr].delete_if(&:empty?)
79
- logger.error "Build with Vite failed:\n#{ non_empty_streams.join("\n\n") }"
80
- end
81
-
82
- status.success?
83
- end
84
-
85
- # Internal: Used to prefix the bin/vite executable file.
86
- def which_ruby
87
- bin_vite_path = config.root.join('bin/vite')
88
- first_line = File.readlines(bin_vite_path).first.chomp
89
- /ruby/.match?(first_line) ? RbConfig.ruby : ''
90
- end
91
-
92
- # Internal: Files and directories that should be watched for changes.
93
- #
94
- # NOTE: You can specify additional ones in vite.json using "watchAdditionalPaths": [...]
95
- def watched_paths
96
- [
97
- *config.watch_additional_paths,
98
- "#{ config.source_code_dir }/**/*",
99
- 'yarn.lock',
100
- 'package.json',
101
- config.config_path,
102
- ].freeze
103
- end
104
-
105
- # Internal: Sets additional environment variables for vite-plugin-ruby.
106
- def vite_env
107
- ViteRails.env.merge(
108
- "#{ ViteRails::ENV_PREFIX }_CONFIG_PATH" => config.config_path,
109
- "#{ ViteRails::ENV_PREFIX }_MODE" => config.mode,
110
- "#{ ViteRails::ENV_PREFIX }_ROOT" => config.root,
111
- ).transform_values(&:to_s)
112
- end
113
- end
@@ -1,68 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Public: Encapsulates common tasks, available both programatically and in Rake.
4
- class ViteRails::Commands
5
- def initialize(vite_rails)
6
- @vite_rails = vite_rails
7
- end
8
-
9
- # Public: Loads the manifest with all the entries compiled by Vite.
10
- def bootstrap
11
- manifest.refresh
12
- end
13
-
14
- # Public: Builds all assets that are managed by Vite, from the entrypoints.
15
- def build
16
- builder.build.tap { manifest.refresh }
17
- end
18
-
19
- # Public: Removes all build cache and previously compiled assets.
20
- def clobber
21
- config.build_output_dir.rmtree if config.build_output_dir.exist?
22
- config.build_cache_dir.rmtree if config.build_cache_dir.exist?
23
- end
24
-
25
- # Public: Cleanup old assets in the output directory.
26
- #
27
- # keep_up_to - Max amount of backups to preserve.
28
- # age_in_seconds - Amount of time to look back in order to preserve them.
29
- #
30
- # NOTE: By default keeps the last version, or 2 if created in the past hour.
31
- #
32
- # Examples:
33
- # To force only 1 backup to be kept: clean(1, 0)
34
- # To only keep files created within the last 10 minutes: clean(0, 600)
35
- def clean(keep_up_to: 2, age_in_seconds: 3600)
36
- return false unless config.build_output_dir.exist? && config.manifest_path.exist?
37
-
38
- versions.sort.reverse
39
- .each_with_index
40
- .drop_while { |(mtime, _), index|
41
- max_age = [0, Time.now - Time.at(mtime)].max
42
- max_age < age_in_seconds || index < keep_up_to
43
- }
44
- .each do |(_, files), _index|
45
- files.each do |file|
46
- next unless File.file?(file)
47
-
48
- File.delete(file)
49
- logger.info("Removed #{ file }")
50
- end
51
- end
52
- true
53
- end
54
-
55
- private
56
-
57
- delegate :config, :builder, :manifest, :logger, to: :@vite_rails
58
-
59
- def versions
60
- all_files = Dir.glob("#{ config.build_output_dir }/**/*")
61
- entries = all_files - [config.manifest_path] - current_version_files
62
- entries.reject { |file| File.directory?(file) }.group_by { |file| File.mtime(file).utc.to_i }
63
- end
64
-
65
- def current_version_files
66
- Dir.glob(manifest.refresh.values.map { |value| config.build_output_dir.join("#{ value['file'] }*") })
67
- end
68
- end
@@ -1,23 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Public: Allows to verify if a Vite development server is already running.
4
- class ViteRails::DevServer
5
- # Public: Configure dev server connection timeout (in seconds).
6
- # Example:
7
- # ViteRails.dev_server.connect_timeout = 1
8
- cattr_accessor(:connect_timeout) { 0.01 }
9
-
10
- def initialize(config)
11
- @config = config
12
- end
13
-
14
- # Public: Returns true if the Vite development server is reachable.
15
- def running?
16
- Socket.tcp(host, port, connect_timeout: connect_timeout).close
17
- true
18
- rescue StandardError
19
- false
20
- end
21
-
22
- delegate :host, :port, to: :@config
23
- end
@@ -1,49 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'rack/proxy'
4
-
5
- # Public: Allows to relay asset requests to the Vite development server.
6
- class ViteRails::DevServerProxy < Rack::Proxy
7
- VITE_DEPENDENCY_PREFIX = '/@'
8
-
9
- def initialize(app = nil, options = {})
10
- @vite_rails = options.delete(:vite_rails) || ViteRails.instance
11
- options[:streaming] = false if Rails.env.test? && !options.key?(:streaming)
12
- super
13
- end
14
-
15
- # Rack: Intercept asset requests and send them to the Vite server.
16
- def perform_request(env)
17
- if vite_should_handle?(env['REQUEST_URI'], env['HTTP_REFERER']) && dev_server.running?
18
- env['REQUEST_URI'] = env['REQUEST_URI']
19
- .sub(vite_asset_url_prefix, '/')
20
- .sub('.ts.js', '.ts') # Patch: Rails helpers always append the extension.
21
- env['PATH_INFO'], env['QUERY_STRING'] = env['REQUEST_URI'].split('?')
22
-
23
- env['HTTP_HOST'] = env['HTTP_X_FORWARDED_HOST'] = config.host
24
- env['HTTP_X_FORWARDED_SERVER'] = config.host_with_port
25
- env['HTTP_PORT'] = env['HTTP_X_FORWARDED_PORT'] = config.port.to_s
26
- env['HTTP_X_FORWARDED_PROTO'] = env['HTTP_X_FORWARDED_SCHEME'] = config.protocol
27
- env['HTTPS'] = env['HTTP_X_FORWARDED_SSL'] = 'off' unless config.https
28
- env['SCRIPT_NAME'] = ''
29
- super(env)
30
- else
31
- @app.call(env)
32
- end
33
- end
34
-
35
- private
36
-
37
- delegate :config, :dev_server, to: :@vite_rails
38
-
39
- def vite_should_handle?(url, referer)
40
- return true if url.start_with?(vite_asset_url_prefix) # Vite Asset
41
- return true if url.start_with?(VITE_DEPENDENCY_PREFIX) # Vite Package Asset
42
- return true if url.include?('?t=') # Hot Reload
43
- return true if referer && URI.parse(referer).path.start_with?(vite_asset_url_prefix) # Entry Imported from another Entry.
44
- end
45
-
46
- def vite_asset_url_prefix
47
- @vite_asset_url_prefix ||= "/#{ config.public_output_dir }/"
48
- end
49
- end
@@ -1,67 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Public: Allows to render HTML tags for scripts and styles processed by Vite.
4
- module ViteRails::Helper
5
- DEFAULT_VITE_SKIP_PRELOAD_TAGS = Rails::VERSION::MAJOR <= 5 && Rails::VERSION::MINOR < 2
6
-
7
- # Public: Returns the current Vite Rails instance.
8
- def current_vite_instance
9
- ViteRails.instance
10
- end
11
-
12
- # Public: Renders a script tag for vite/client to enable HMR in development.
13
- def vite_client_tag
14
- content_tag('script', '', src: '/@vite/client', type: 'module') if ViteRails.dev_server_running?
15
- end
16
-
17
- # Public: Computes the relative path for the specified given Vite asset.
18
- #
19
- # Example:
20
- # <%= vite_asset_path 'calendar.css' %> # => "/vite/assets/calendar-1016838bab065ae1e122.css"
21
- def vite_asset_path(name, **options)
22
- current_vite_instance.manifest.lookup!(name, **options).fetch('file')
23
- end
24
-
25
- # Public: Renders a <script> tag for the specified Vite entrypoints.
26
- def vite_javascript_tag(*names,
27
- type: 'module',
28
- asset_type: :javascript,
29
- skip_preload_tags: DEFAULT_VITE_SKIP_PRELOAD_TAGS,
30
- skip_style_tags: false,
31
- crossorigin: 'anonymous',
32
- **options)
33
- js_entries = names.map { |name| current_vite_instance.manifest.lookup!(name, type: asset_type) }
34
- js_tags = javascript_include_tag(*js_entries.map { |entry| entry['file'] }, type: type, crossorigin: crossorigin, **options)
35
-
36
- unless skip_preload_tags || ViteRails.dev_server_running?
37
- preload_paths = js_entries.flat_map { |entry| entry['imports'] }.compact.uniq
38
- preload_tags = preload_paths.map { |path| preload_link_tag(path, crossorigin: crossorigin) }
39
- end
40
-
41
- unless skip_style_tags || ViteRails.dev_server_running?
42
- style_paths = names.map { |name| current_vite_instance.manifest.lookup(name, type: :stylesheet)&.fetch('file') }.compact
43
- style_tags = stylesheet_link_tag(*style_paths)
44
- end
45
-
46
- safe_join [js_tags, preload_tags, style_tags]
47
- end
48
-
49
- # Public: Renders a <script> tag for the specified Vite entrypoints.
50
- #
51
- # NOTE: Because TypeScript is not a valid target in browsers, we only specify
52
- # the ts file when running the Vite development server.
53
- def vite_typescript_tag(*names, **options)
54
- vite_javascript_tag(*names, asset_type: :typescript, **options)
55
- end
56
-
57
- # Public: Renders a <link> tag for the specified Vite entrypoints.
58
- def vite_stylesheet_tag(*names, **options)
59
- stylesheet_link_tag(*sources_from_vite_manifest(names, type: :stylesheet), **options)
60
- end
61
-
62
- private
63
-
64
- def sources_from_vite_manifest(names, type:)
65
- names.map { |name| vite_asset_path(name, type: type) }
66
- end
67
- end