vite_rails 1.0.11 → 2.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +23 -0
  3. data/README.md +52 -34
  4. data/lib/tasks/vite.rake +17 -0
  5. data/lib/vite_rails.rb +5 -82
  6. data/lib/vite_rails/config.rb +11 -130
  7. data/lib/vite_rails/engine.rb +7 -11
  8. data/lib/vite_rails/installation.rb +52 -0
  9. data/lib/vite_rails/tag_helpers.rb +61 -0
  10. data/lib/vite_rails/version.rb +2 -2
  11. data/{lib/install/config/vite.json → templates/config/rails-vite.json} +1 -0
  12. data/{lib/install/javascript → templates}/entrypoints/application.js +0 -0
  13. metadata +27 -166
  14. data/CONTRIBUTING.md +0 -33
  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 -39
  21. data/lib/tasks/vite/clean.rake +0 -23
  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 -14
  26. data/lib/tasks/vite/verify_install.rake +0 -23
  27. data/lib/vite_rails/builder.rb +0 -111
  28. data/lib/vite_rails/commands.rb +0 -109
  29. data/lib/vite_rails/dev_server.rb +0 -23
  30. data/lib/vite_rails/dev_server_proxy.rb +0 -57
  31. data/lib/vite_rails/helper.rb +0 -71
  32. data/lib/vite_rails/manifest.rb +0 -140
  33. data/lib/vite_rails/runner.rb +0 -53
  34. data/package.json +0 -19
  35. data/package/default.vite.json +0 -16
  36. data/test/builder_test.rb +0 -77
  37. data/test/commands_test.rb +0 -67
  38. data/test/config_test.rb +0 -133
  39. data/test/dev_server_proxy_test.rb +0 -102
  40. data/test/dev_server_test.rb +0 -9
  41. data/test/engine_rake_tasks_test.rb +0 -81
  42. data/test/helper_test.rb +0 -70
  43. data/test/manifest_test.rb +0 -79
  44. data/test/mode_test.rb +0 -16
  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 -14
  53. data/test/mounted_app/test/dummy/package.json +0 -8
  54. data/test/mounted_app/test/dummy/yarn.lock +0 -208
  55. data/test/rake_tasks_test.rb +0 -60
  56. data/test/runner_test.rb +0 -31
  57. data/test/test_app/Rakefile +0 -5
  58. data/test/test_app/app/frontend/entrypoints/application.js +0 -2
  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 -18
  64. data/test/test_app/config/vite_additional_paths.json +0 -5
  65. data/test/test_app/config/vite_public_dir.json +0 -5
  66. data/test/test_app/package.json +0 -13
  67. data/test/test_app/public/vite-production/manifest.json +0 -22
  68. data/test/test_app/some.config.js +0 -0
  69. data/test/test_app/yarn.lock +0 -11
  70. data/test/test_helper.rb +0 -68
@@ -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,57 +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) && dev_server_running?
18
- forward_to_vite_dev_server(env)
19
- super(env)
20
- else
21
- @app.call(env)
22
- end
23
- end
24
-
25
- private
26
-
27
- delegate :config, :dev_server_running?, to: :@vite_rails
28
-
29
- def rewrite_uri_for_vite(env)
30
- uri = env.fetch('REQUEST_URI') { [env['PATH_INFO'], env['QUERY_STRING']].reject(&:blank?).join('?') }
31
- .sub(vite_asset_url_prefix, '/')
32
- env['PATH_INFO'], env['QUERY_STRING'] = (env['REQUEST_URI'] = uri).split('?')
33
- end
34
-
35
- def forward_to_vite_dev_server(env)
36
- rewrite_uri_for_vite(env)
37
- env['HTTP_HOST'] = env['HTTP_X_FORWARDED_HOST'] = config.host
38
- env['HTTP_X_FORWARDED_SERVER'] = config.host_with_port
39
- env['HTTP_PORT'] = env['HTTP_X_FORWARDED_PORT'] = config.port.to_s
40
- env['HTTP_X_FORWARDED_PROTO'] = env['HTTP_X_FORWARDED_SCHEME'] = config.protocol
41
- env['HTTPS'] = env['HTTP_X_FORWARDED_SSL'] = 'off' unless config.https
42
- env['SCRIPT_NAME'] = ''
43
- end
44
-
45
- def vite_should_handle?(env)
46
- path, query, referer = env['PATH_INFO'], env['QUERY_STRING'], env['HTTP_REFERER']
47
- return true if path.start_with?(vite_asset_url_prefix) # Vite asset
48
- return true if path.start_with?(VITE_DEPENDENCY_PREFIX) # Packages and imports
49
- return true if query&.start_with?('t=') # Hot Reload for a stylesheet
50
- return true if query&.start_with?('import&') # Hot Reload for an imported entrypoint
51
- return true if referer && URI.parse(referer).path.start_with?(vite_asset_url_prefix) # Entry imported from another entry.
52
- end
53
-
54
- def vite_asset_url_prefix
55
- @vite_asset_url_prefix ||= "/#{ config.public_output_dir }/"
56
- end
57
- end
@@ -1,71 +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.gem_version < Gem::Version.new('5.2.0')
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
- return unless current_vite_instance.dev_server_running?
15
-
16
- content_tag('script', '', src: current_vite_instance.manifest.prefix_vite_asset('@vite/client'), type: 'module')
17
- end
18
-
19
- # Public: Resolves the path for the specified Vite asset.
20
- #
21
- # Example:
22
- # <%= vite_asset_path 'calendar.css' %> # => "/vite/assets/calendar-1016838bab065ae1e122.css"
23
- def vite_asset_path(name, **options)
24
- path_to_asset current_vite_instance.manifest.lookup!(name, **options).fetch('file')
25
- end
26
-
27
- # Public: Renders a <script> tag for the specified Vite entrypoints.
28
- def vite_javascript_tag(*names,
29
- type: 'module',
30
- asset_type: :javascript,
31
- skip_preload_tags: DEFAULT_VITE_SKIP_PRELOAD_TAGS,
32
- skip_style_tags: false,
33
- crossorigin: 'anonymous',
34
- **options)
35
- js_entries = names.map { |name| current_vite_instance.manifest.lookup!(name, type: asset_type) }
36
- js_tags = javascript_include_tag(*js_entries.map { |entry| entry['file'] }, crossorigin: crossorigin, type: type, **options)
37
-
38
- unless skip_preload_tags || current_vite_instance.dev_server_running?
39
- preload_paths = js_entries.flat_map { |entry| entry['imports'] }.compact.uniq
40
- preload_tags = preload_paths.map { |path| preload_link_tag(path, crossorigin: crossorigin) }
41
- end
42
-
43
- unless skip_style_tags || current_vite_instance.dev_server_running?
44
- style_paths = names.map { |name|
45
- current_vite_instance.manifest.lookup(name.delete_suffix('.js'), type: :stylesheet)&.fetch('file')
46
- }.compact
47
- style_tags = stylesheet_link_tag(*style_paths)
48
- end
49
-
50
- safe_join [js_tags, preload_tags, style_tags]
51
- end
52
-
53
- # Public: Renders a <script> tag for the specified Vite entrypoints.
54
- #
55
- # NOTE: Because TypeScript is not a valid target in browsers, we only specify
56
- # the ts file when running the Vite development server.
57
- def vite_typescript_tag(*names, **options)
58
- vite_javascript_tag(*names, asset_type: :typescript, extname: false, **options)
59
- end
60
-
61
- # Public: Renders a <link> tag for the specified Vite entrypoints.
62
- def vite_stylesheet_tag(*names, **options)
63
- stylesheet_link_tag(*sources_from_vite_manifest(names, type: :stylesheet), **options)
64
- end
65
-
66
- private
67
-
68
- def sources_from_vite_manifest(names, type:)
69
- names.map { |name| vite_asset_path(name, type: type) }
70
- end
71
- end
@@ -1,140 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Public: Registry for accessing resources managed by Vite, using a generated
4
- # manifest file which maps entrypoint names to file paths.
5
- #
6
- # Example:
7
- # lookup_entrypoint('calendar', type: :javascript)
8
- # => { "file" => "/vite/assets/calendar-1016838bab065ae1e314.js", "imports" => [] }
9
- #
10
- # NOTE: Using "autoBuild": true` in `config/vite.json` file will trigger a build
11
- # on demand as needed, before performing any lookup.
12
- class ViteRails::Manifest
13
- class MissingEntryError < StandardError
14
- end
15
-
16
- def initialize(vite_rails)
17
- @vite_rails = vite_rails
18
- end
19
-
20
- # Public: Strict version of lookup.
21
- #
22
- # Returns a relative path for the asset, or raises an error if not found.
23
- def lookup!(*args, **options)
24
- lookup(*args, **options) || missing_entry_error(*args, **options)
25
- end
26
-
27
- # Public: Computes the path for a given Vite asset using manifest.json.
28
- #
29
- # Returns a relative path, or nil if the asset is not found.
30
- #
31
- # Example:
32
- # ViteRails.manifest.lookup('calendar.js')
33
- # # { "file" => "/vite/assets/calendar-1016838bab065ae1e122.js", "imports" => [] }
34
- def lookup(name, type: nil)
35
- build if should_build?
36
-
37
- find_manifest_entry(with_file_extension(name, type))
38
- end
39
-
40
- # Public: Refreshes the cached mappings by reading the updated manifest.
41
- def refresh
42
- @manifest = load_manifest
43
- end
44
-
45
- # Public: Scopes an asset to the output folder in public, as a path.
46
- def prefix_vite_asset(path)
47
- File.join("/#{ config.public_output_dir }", path)
48
- end
49
-
50
- private
51
-
52
- delegate :config, :builder, :dev_server_running?, to: :@vite_rails
53
-
54
- # NOTE: Auto compilation is convenient when running tests, when the developer
55
- # won't focus on the frontend, or when running the Vite server is not desired.
56
- def should_build?
57
- config.auto_build && !dev_server_running?
58
- end
59
-
60
- # Internal: Finds the specified entry in the manifest.
61
- def find_manifest_entry(name)
62
- if dev_server_running?
63
- { 'file' => prefix_vite_asset(name.to_s) }
64
- else
65
- manifest[name.to_s]
66
- end
67
- end
68
-
69
- # Internal: Performs a Vite build.
70
- def build
71
- ViteRails.logger.tagged('Vite') { builder.build }
72
- end
73
-
74
- # Internal: The parsed data from manifest.json.
75
- #
76
- # NOTE: When using build-on-demand in development and testing, the manifest
77
- # is reloaded automatically before each lookup, to ensure it's always fresh.
78
- def manifest
79
- return refresh if config.auto_build
80
-
81
- @manifest ||= load_manifest
82
- end
83
-
84
- # Internal: Returns a Hash with the entries in the manifest.json.
85
- def load_manifest
86
- if config.manifest_path.exist?
87
- JSON.parse(config.manifest_path.read).each do |_, entry|
88
- entry['file'] = prefix_vite_asset(entry['file'])
89
- entry['imports'] = entry['imports']&.map { |path| prefix_vite_asset(path) }
90
- end
91
- else
92
- {}
93
- end
94
- end
95
-
96
- # Internal: Adds a file extension to the file name, unless it already has one.
97
- def with_file_extension(name, entry_type)
98
- return name unless File.extname(name.to_s).empty?
99
-
100
- extension = extension_for_type(entry_type)
101
- extension ? "#{ name }.#{ extension }" : name
102
- end
103
-
104
- # Internal: Allows to receive :javascript and :stylesheet as :type in helpers.
105
- def extension_for_type(entry_type)
106
- case entry_type
107
- when :javascript then 'js'
108
- when :stylesheet then 'css'
109
- when :typescript then dev_server_running? ? 'ts' : 'js'
110
- else entry_type
111
- end
112
- end
113
-
114
- # Internal: Raises a detailed message when an entry is missing in the manifest.
115
- def missing_entry_error(name, type: nil, **_options)
116
- file_name = with_file_extension(name, type)
117
- raise ViteRails::Manifest::MissingEntryError, <<~MSG
118
- Vite Rails can't find #{ file_name } in #{ config.manifest_path }.
119
-
120
- Possible causes:
121
- #{ missing_entry_causes.map { |cause| "\t- #{ cause }" }.join("\n") }
122
-
123
- Your manifest contains:
124
- #{ JSON.pretty_generate(@manifest) }
125
- MSG
126
- end
127
-
128
- def missing_entry_causes
129
- local = config.auto_build
130
- [
131
- (dev_server_running? && 'Vite has not yet re-built your latest changes.'),
132
- (local && !dev_server_running? && "\"autoBuild\": false in your #{ config.mode } configuration."),
133
- (local && !dev_server_running? && 'The Vite development server has crashed or is no longer available.'),
134
- 'You have misconfigured config/vite.json file.',
135
- (!local && 'Assets have not been precompiled'),
136
- ].compact
137
- rescue StandardError
138
- []
139
- end
140
- end
@@ -1,53 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Public: Executes Vite commands, providing conveniences for debugging.
4
- class ViteRails::Runner
5
- def initialize(argv)
6
- detect_unsupported_switches!(argv)
7
- @argv = argv
8
- end
9
-
10
- # Public: Executes Vite with the specified arguments.
11
- def run
12
- execute_command(@argv.clone)
13
- end
14
-
15
- private
16
-
17
- UNSUPPORTED_SWITCHES = %w[--host --port --https --root -r --config -c]
18
- private_constant :UNSUPPORTED_SWITCHES
19
-
20
- # Internal: Allows to prevent configuration mistakes by ensuring the Rails app
21
- # and vite-plugin-ruby are using the same configuration for the dev server.
22
- def detect_unsupported_switches!(args)
23
- return unless (unsupported = UNSUPPORTED_SWITCHES & args).any?
24
-
25
- $stdout.puts "Please set the following switches in your vite.json instead: #{ unsupported }."
26
- exit!
27
- end
28
-
29
- # Internal: Executes the command with the specified arguments.
30
- def execute_command(args)
31
- cmd = vite_executable
32
- cmd.prepend('node', '--inspect-brk') if args.include?('--debug')
33
- cmd.prepend('node', '--trace-deprecation') if args.delete('--trace-deprecation')
34
- args.append('--mode', ViteRails.mode) unless args.include?('--mode') || args.include?('-m')
35
- cmd += args
36
- Dir.chdir(File.expand_path('.', Dir.pwd)) { Kernel.exec(ViteRails.config.to_env, *cmd) }
37
- end
38
-
39
- # Internal: Resolves to an executable for Vite.
40
- def vite_executable
41
- executable_exists?(path = vite_bin_path) ? [path] : %w[yarn vite]
42
- end
43
-
44
- # Internal: Only so that we can easily cover both paths in tests
45
- def executable_exists?(path)
46
- File.exist?(path)
47
- end
48
-
49
- # Internal: Returns a path where a Vite executable should be found.
50
- def vite_bin_path
51
- ENV["#{ ViteRails::ENV_PREFIX }_VITE_BIN_PATH"] || `yarn bin vite`.chomp.presence || "#{ `npm bin`.chomp }/vite"
52
- end
53
- end
data/package.json DELETED
@@ -1,19 +0,0 @@
1
- {
2
- "name": "only-for-workflows",
3
- "version": "unknown",
4
- "license": "MIT",
5
- "scripts": {
6
- "docs": "npm -C docs run docs",
7
- "docs:build": "npm -C docs run docs:build",
8
- "docs:search": "npm -C docs run docs:search",
9
- "docs:lint": "npm -C docs run lint",
10
- "build": "rm -rf package/dist && npm -C package run prerelease",
11
- "release": "rm -rf package/dist && npm -C package run release",
12
- "lint": "npm -C package run lint",
13
- "test": "npm -C package run test"
14
- },
15
- "dependencies": {
16
- "vite": "^2.0.0-beta.46",
17
- "vite-plugin-ruby": "^1.0.5"
18
- }
19
- }
@@ -1,16 +0,0 @@
1
- {
2
- "assetHost": null,
3
- "assetsDir": "assets",
4
- "autoBuild": false,
5
- "buildCacheDir": "tmp/cache/vite",
6
- "publicOutputDir": "vite",
7
- "configPath": "config/vite.json",
8
- "publicDir": "public",
9
- "entrypointsDir": "entrypoints",
10
- "sourceCodeDir": "app/frontend",
11
- "host": "localhost",
12
- "https": null,
13
- "port": 3036,
14
- "hideBuildConsoleOutput": false,
15
- "watchAdditionalPaths": []
16
- }
data/test/builder_test.rb DELETED
@@ -1,77 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'test_helper'
4
-
5
- class BuilderTest < ViteRails::Test
6
- def setup
7
- refresh_config
8
- ViteRails.builder.send(:files_digest_path).tap do |path|
9
- path.delete if path.exist?
10
- end
11
- end
12
-
13
- def teardown
14
- setup
15
- end
16
-
17
- def vite_env
18
- ViteRails.config.to_env
19
- end
20
-
21
- def test_custom_environment_variables
22
- assert_nil vite_env['FOO']
23
- ViteRails.env['FOO'] = 'BAR'
24
- assert vite_env['FOO'] == 'BAR'
25
- end
26
-
27
- def test_freshness
28
- assert ViteRails.builder.stale?
29
- assert !ViteRails.builder.fresh?
30
- end
31
-
32
- def test_build
33
- assert !ViteRails.builder.build
34
- end
35
-
36
- def test_freshness_on_build_success
37
- assert ViteRails.builder.stale?
38
- status = OpenStruct.new(success?: true)
39
- Open3.stub :capture3, [:sterr, :stdout, status] do
40
- assert ViteRails.builder.build
41
- assert ViteRails.builder.fresh?
42
- end
43
- end
44
-
45
- def test_freshness_on_build_fail
46
- assert ViteRails.builder.stale?
47
- status = OpenStruct.new(success?: false)
48
- Open3.stub :capture3, [:sterr, :stdout, status] do
49
- assert !ViteRails.builder.build
50
- assert ViteRails.builder.fresh?
51
- end
52
- end
53
-
54
- def test_files_digest_path
55
- assert_equal ViteRails.builder.send(:files_digest_path).basename.to_s, "last-compilation-digest-#{ ViteRails.config.mode }"
56
- end
57
-
58
- def test_watched_files_digest
59
- previous_digest = ViteRails.builder.send(:watched_files_digest)
60
- refresh_config
61
- assert_equal previous_digest, ViteRails.builder.send(:watched_files_digest)
62
- end
63
-
64
- def test_external_env_variables
65
- assert_equal 'production', vite_env['VITE_RUBY_MODE']
66
- assert_equal Rails.root.to_s, vite_env['VITE_RUBY_ROOT']
67
-
68
- ENV['VITE_RUBY_MODE'] = 'foo.bar'
69
- ENV['VITE_RUBY_ROOT'] = '/baz'
70
- refresh_config
71
- assert_equal 'foo.bar', vite_env['VITE_RUBY_MODE']
72
- assert_equal '/baz', vite_env['VITE_RUBY_ROOT']
73
- ensure
74
- ENV.delete('VITE_RUBY_MODE')
75
- ENV.delete('VITE_RUBY_ROOT')
76
- end
77
- end