vite_ruby 3.11.0 → 4.0.0.alpha1

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.
@@ -1,15 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "rack/proxy"
3
+ require 'rack/proxy'
4
4
 
5
5
  # Public: Allows to relay asset requests to the Vite development server.
6
6
  class ViteRuby::DevServerProxy < Rack::Proxy
7
7
  HOST_WITH_PORT_REGEX = %r{^(.+?)(:\d+)/}
8
- VITE_DEPENDENCY_PREFIX = "/@"
8
+ VITE_DEPENDENCY_PREFIX = '/@'
9
9
 
10
10
  def initialize(app = nil, options = {})
11
11
  @vite_ruby = options.delete(:vite_ruby) || ViteRuby.instance
12
- options[:streaming] = false if config.mode == "test" && !options.key?(:streaming)
12
+ options[:streaming] = false if config.mode == 'test' && !options.key?(:streaming)
13
13
  super
14
14
  end
15
15
 
@@ -17,7 +17,7 @@ class ViteRuby::DevServerProxy < Rack::Proxy
17
17
  def perform_request(env)
18
18
  if vite_should_handle?(env) && dev_server_running?
19
19
  forward_to_vite_dev_server(env)
20
- super
20
+ super(env)
21
21
  else
22
22
  @app.call(env)
23
23
  end
@@ -30,48 +30,41 @@ private
30
30
  def_delegators :@vite_ruby, :config, :dev_server_running?
31
31
 
32
32
  def rewrite_uri_for_vite(env)
33
- uri = env.fetch("REQUEST_URI") { [env["PATH_INFO"], env["QUERY_STRING"]].reject { |str| str.to_s.strip.empty? }.join("?") }
34
- env["PATH_INFO"], env["QUERY_STRING"] = (env["REQUEST_URI"] = normalize_uri(uri)).split("?")
33
+ uri = env.fetch('REQUEST_URI') { [env['PATH_INFO'], env['QUERY_STRING']].reject { |str| str.to_s.strip.empty? }.join('?') }
34
+ env['PATH_INFO'], env['QUERY_STRING'] = (env['REQUEST_URI'] = normalize_uri(uri)).split('?')
35
35
  end
36
36
 
37
37
  def normalize_uri(uri)
38
38
  uri
39
- .sub(HOST_WITH_PORT_REGEX, "/") # Hanami adds the host and port.
40
- .sub(".ts.js", ".ts") # Hanami's javascript helper always adds the extension.
41
- .sub(/\.(sass|scss|styl|stylus|less|pcss|postcss)\.css$/, '.\1') # Rails' stylesheet_link_tag always adds the extension.
39
+ .sub(HOST_WITH_PORT_REGEX, '/') # Hanami adds the host and port.
40
+ .sub('.ts.js', '.ts') # Hanami's javascript helper always adds the extension.
41
+ .sub(/(\.(?!min|module)\w+)\.css$/, '\1') # Rails' stylesheet_link_tag always adds the extension.
42
42
  end
43
43
 
44
44
  def forward_to_vite_dev_server(env)
45
45
  rewrite_uri_for_vite(env)
46
- env["QUERY_STRING"] ||= ""
47
- env["rack.backend"] = URI(config.origin)
48
- env["HTTP_HOST"] = env["HTTP_X_FORWARDED_HOST"] = config.host
49
- env["HTTP_X_FORWARDED_SERVER"] = config.host_with_port
50
- env["HTTP_PORT"] = env["HTTP_X_FORWARDED_PORT"] = config.port.to_s
51
- env["HTTP_X_FORWARDED_PROTO"] = env["HTTP_X_FORWARDED_SCHEME"] = config.protocol
52
- env["HTTPS"] = env["HTTP_X_FORWARDED_SSL"] = "off" unless config.https
53
- env["SCRIPT_NAME"] = ""
46
+ env['QUERY_STRING'] ||= ''
47
+ env['HTTP_HOST'] = env['HTTP_X_FORWARDED_HOST'] = config.host
48
+ env['HTTP_X_FORWARDED_SERVER'] = config.host_with_port
49
+ env['HTTP_PORT'] = env['HTTP_X_FORWARDED_PORT'] = config.port.to_s
50
+ env['HTTP_X_FORWARDED_PROTO'] = env['HTTP_X_FORWARDED_SCHEME'] = config.protocol
51
+ env['HTTPS'] = env['HTTP_X_FORWARDED_SSL'] = 'off' unless config.https
52
+ env['SCRIPT_NAME'] = ''
54
53
  end
55
54
 
56
55
  def vite_should_handle?(env)
57
- path = normalize_uri(env["PATH_INFO"])
58
- return true if path.start_with?(vite_url_prefix) # Vite asset
59
-
60
- true if file_in_vite_root?(path) # Fallback if Vite can serve the file
56
+ path = normalize_uri(env['PATH_INFO'])
57
+ return true if path.start_with?(vite_asset_url_prefix) # Vite asset
58
+ return true if path.start_with?(VITE_DEPENDENCY_PREFIX) # Packages and imports
59
+ return true if file_in_vite_root?(path) # Fallback if Vite can serve the file
61
60
  end
62
61
 
63
- # NOTE: When using an empty 'public_output_dir', we need to rely on a
64
- # filesystem check to check whether Vite should serve the request.
65
62
  def file_in_vite_root?(path)
66
- path.include?(".") && # Check for extension, avoid filesystem if possible.
67
- config.vite_root_dir.join(path.start_with?("/") ? path[1..] : path).file?
63
+ path.include?('.') && # Check for extension, avoid filesystem if possible.
64
+ config.vite_root_dir.join(path.start_with?('/') ? path[1..-1] : path).file?
68
65
  end
69
66
 
70
- # NOTE: Vite is configured to use 'public_output_dir' as the base, which can
71
- # be customized by the user in development to not match any of the routes.
72
- #
73
- # If the path starts with that prefix, it will be redirected to Vite.
74
- def vite_url_prefix
75
- @vite_url_prefix ||= config.public_output_dir.empty? ? VITE_DEPENDENCY_PREFIX : "/#{config.public_output_dir}/"
67
+ def vite_asset_url_prefix
68
+ @vite_asset_url_prefix ||= config.public_output_dir.empty? ? "\0" : "/#{ config.public_output_dir }/"
76
69
  end
77
70
  end
@@ -3,7 +3,7 @@
3
3
  # Internal: Provides common functionality for errors.
4
4
  class ViteRuby::Error < StandardError
5
5
  def message
6
- super.sub(":troubleshooting:", <<~MSG)
6
+ super.sub(':troubleshooting:', <<~MSG)
7
7
  Visit the Troubleshooting guide for more information:
8
8
  https://vite-ruby.netlify.app/guide/troubleshooting.html#troubleshooting
9
9
  MSG
data/lib/vite_ruby/io.rb CHANGED
@@ -1,13 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "open3"
3
+ require 'open3'
4
4
 
5
5
  # Public: Builds on top of Ruby I/O open3 providing a friendlier experience.
6
6
  module ViteRuby::IO
7
7
  class << self
8
8
  # Internal: A modified version of capture3 that can continuosly print stdout.
9
9
  # NOTE: Streaming output provides a better UX when running bin/vite build.
10
- def capture(*cmd, with_output: $stdout.method(:puts), stdin_data: "", **opts)
10
+ def capture(*cmd, with_output: $stdout.method(:puts), stdin_data: '', **opts)
11
11
  return Open3.capture3(*cmd, **opts) unless with_output
12
12
 
13
13
  Open3.popen3(*cmd, **opts) { |stdin, stdout, stderr, wait_threads|
@@ -15,13 +15,13 @@ module ViteRuby::IO
15
15
  stdin.close
16
16
  out = Thread.new { read_lines(stdout, &with_output) }
17
17
  err = Thread.new { stderr.read }
18
- [out.value, err.value.to_s, wait_threads.value]
18
+ [out.value, err.value, wait_threads.value]
19
19
  }
20
20
  end
21
21
 
22
22
  # Internal: Reads and yield every line in the stream. Returns the full content.
23
23
  def read_lines(io)
24
- buffer = +""
24
+ buffer = +''
25
25
  while line = io.gets
26
26
  buffer << line
27
27
  yield line
@@ -7,7 +7,7 @@
7
7
  # lookup_entrypoint('calendar', type: :javascript)
8
8
  # => { "file" => "/vite/assets/calendar-1016838bab065ae1e314.js", "imports" => [] }
9
9
  #
10
- # NOTE: Using `"autoBuild": true` in `config/vite.json` file will trigger a build
10
+ # NOTE: Using "autoBuild": true` in `config/vite.json` file will trigger a build
11
11
  # on demand as needed, before performing any lookup.
12
12
  class ViteRuby::Manifest
13
13
  def initialize(vite_ruby)
@@ -19,37 +19,19 @@ class ViteRuby::Manifest
19
19
  #
20
20
  # Raises an error if the resource could not be found in the manifest.
21
21
  def path_for(name, **options)
22
- lookup!(name, **options).fetch("file")
22
+ lookup!(name, **options).fetch('file')
23
23
  end
24
24
 
25
- # Internal: Recursively collects all imported chunks for a given entry.
26
- # Returns chunks in dependency-first order (deepest imports first), deduped.
27
- def import_chunks_for(entry, seen_filenames: Set.new)
28
- chunks = []
29
-
30
- entry["imports"]&.each do |chunk|
31
- filename = chunk["file"]
32
- next if seen_filenames.include?(filename)
33
- seen_filenames.add(filename)
34
-
35
- chunks.concat(import_chunks_for(chunk, seen_filenames: seen_filenames))
36
- chunks << chunk
37
- end
38
-
39
- chunks
40
- end
41
-
42
- # Public: Returns scripts, imported modules, and stylesheets for the specified
25
+ # Public: Returns entries, imported modules, and stylesheets for the specified
43
26
  # entrypoint files.
44
27
  def resolve_entries(*names, **options)
45
28
  entries = names.map { |name| lookup!(name, **options) }
46
- script_paths = entries.map { |entry| entry.fetch("file") }
47
29
 
48
- imports = dev_server_running? ? [] : entries.flat_map { |entry| import_chunks_for(entry) }
30
+ imports = dev_server_running? ? [] : entries.flat_map { |entry| entry['imports'] }.compact.uniq
49
31
  {
50
- scripts: script_paths,
51
- imports: imports.filter_map { |entry| entry.fetch("file") }.uniq,
52
- stylesheets: dev_server_running? ? [] : (entries + imports).flat_map { |entry| entry["css"] }.compact.uniq,
32
+ main: entries.map(&TO_ENTRY),
33
+ imports: imports.map(&TO_ENTRY).uniq,
34
+ stylesheets: dev_server_running? ? [] : (entries + imports).flat_map { |entry| entry['css'] }.compact.uniq,
53
35
  }
54
36
  end
55
37
 
@@ -60,7 +42,7 @@ class ViteRuby::Manifest
60
42
 
61
43
  # Public: The path from where the browser can download the Vite HMR client.
62
44
  def vite_client_src
63
- prefix_asset_with_host("@vite/client") if dev_server_running?
45
+ prefix_asset_with_host('@vite/client') if dev_server_running?
64
46
  end
65
47
 
66
48
  # Public: The content of the preamble needed by the React Refresh plugin.
@@ -68,27 +50,21 @@ class ViteRuby::Manifest
68
50
  if dev_server_running?
69
51
  <<~REACT_REFRESH
70
52
  <script type="module">
71
- #{react_preamble_code}
53
+ import RefreshRuntime from '#{ prefix_asset_with_host('@react-refresh') }'
54
+ RefreshRuntime.injectIntoGlobalHook(window)
55
+ window.$RefreshReg$ = () => {}
56
+ window.$RefreshSig$ = () => (type) => type
57
+ window.__vite_plugin_react_preamble_installed__ = true
72
58
  </script>
73
59
  REACT_REFRESH
74
60
  end
75
61
  end
76
62
 
77
- # Public: Source script for the React Refresh plugin.
78
- def react_preamble_code
79
- if dev_server_running?
80
- <<~REACT_PREAMBLE_CODE
81
- import RefreshRuntime from '#{prefix_asset_with_host("@react-refresh")}'
82
- RefreshRuntime.injectIntoGlobalHook(window)
83
- window.$RefreshReg$ = () => {}
84
- window.$RefreshSig$ = () => (type) => type
85
- window.__vite_plugin_react_preamble_installed__ = true
86
- REACT_PREAMBLE_CODE
87
- end
88
- end
89
-
90
63
  protected
91
64
 
65
+ # Internal: Returns a [src, attrs] entry.
66
+ TO_ENTRY = ->(entry) { [entry.fetch('file'), entry.slice('integrity').symbolize_keys] }
67
+
92
68
  # Internal: Strict version of lookup.
93
69
  #
94
70
  # Returns a relative path for the asset, or raises an error if not found.
@@ -104,7 +80,7 @@ protected
104
80
  # manifest.lookup('calendar.js')
105
81
  # => { "file" => "/vite/assets/calendar-1016838bab065ae1e122.js", "imports" => [] }
106
82
  def lookup(name, **options)
107
- @build_mutex.synchronize { builder.build || (return nil) } if should_build?
83
+ @build_mutex.synchronize { builder.build } if should_build?
108
84
 
109
85
  find_manifest_entry resolve_entry_name(name, **options)
110
86
  end
@@ -112,7 +88,7 @@ protected
112
88
  private
113
89
 
114
90
  # Internal: The prefix used by Vite.js to request files with an absolute path.
115
- FS_PREFIX = "/@fs/"
91
+ FS_PREFIX = '/@fs/'
116
92
 
117
93
  extend Forwardable
118
94
 
@@ -127,7 +103,7 @@ private
127
103
  # Internal: Finds the specified entry in the manifest.
128
104
  def find_manifest_entry(name)
129
105
  if dev_server_running?
130
- {"file" => prefix_vite_asset(name)}
106
+ { 'file' => prefix_vite_asset(name) }
131
107
  else
132
108
  manifest[name]
133
109
  end
@@ -145,36 +121,29 @@ private
145
121
 
146
122
  # Internal: Loads and merges the manifest files, resolving the asset paths.
147
123
  def load_manifest
148
- config.manifest_paths
149
- .map { |path| JSON.parse(path.read) }
150
- .inject({}, &:merge)
151
- .tap { |manifest| resolve_references(manifest) }
124
+ files = [config.manifest_path, config.assets_manifest_path].select(&:exist?)
125
+ files.map { |path| JSON.parse(path.read) }.inject({}, &:merge).tap(&method(:resolve_references))
152
126
  end
153
127
 
154
128
  # Internal: Scopes an asset to the output folder in public, as a path.
155
129
  def prefix_vite_asset(path)
156
- File.join(vite_asset_origin || "/", config.public_output_dir, path)
130
+ File.join("/#{ config.public_output_dir }", path)
157
131
  end
158
132
 
159
133
  # Internal: Prefixes an asset with the `asset_host` for tags that do not use
160
134
  # the framework tag helpers.
161
135
  def prefix_asset_with_host(path)
162
- File.join(vite_asset_origin || config.asset_host || "/", config.public_output_dir, path)
163
- end
164
-
165
- # Internal: The origin of assets managed by Vite.
166
- def vite_asset_origin
167
- config.origin if dev_server_running? && config.skip_proxy
136
+ File.join(config.asset_host || '/', config.public_output_dir, path)
168
137
  end
169
138
 
170
139
  # Internal: Resolves the paths that reference a manifest entry.
171
140
  def resolve_references(manifest)
172
141
  manifest.each_value do |entry|
173
- entry["file"] = prefix_vite_asset(entry["file"])
142
+ entry['file'] = prefix_vite_asset(entry['file'])
174
143
  %w[css assets].each do |key|
175
144
  entry[key] = entry[key].map { |path| prefix_vite_asset(path) } if entry[key]
176
145
  end
177
- entry["imports"]&.map! { |name| manifest.fetch(name) }
146
+ entry['imports']&.map! { |name| manifest.fetch(name) }
178
147
  end
179
148
  end
180
149
 
@@ -183,7 +152,7 @@ private
183
152
  return resolve_virtual_entry(name) if type == :virtual
184
153
 
185
154
  name = with_file_extension(name.to_s, type)
186
- raise ArgumentError, "Asset names can not be relative. Found: #{name}" if name.start_with?(".")
155
+ raise ArgumentError, "Asset names can not be relative. Found: #{ name }" if name.start_with?('.')
187
156
 
188
157
  # Explicit path, relative to the source_code_dir.
189
158
  name.sub(%r{^~/(.+)$}) { return Regexp.last_match(1) }
@@ -192,7 +161,7 @@ private
192
161
  name.sub(%r{^/(.+)$}) { return resolve_absolute_entry(Regexp.last_match(1)) }
193
162
 
194
163
  # Sugar: Prefix with the entrypoints dir if the path is not nested.
195
- name.include?("/") ? name : File.join(config.entrypoints_dir, name)
164
+ name.include?('/') ? name : File.join(config.entrypoints_dir, name)
196
165
  end
197
166
 
198
167
  # Internal: Entry names in the manifest are relative to the Vite.js.
@@ -213,7 +182,7 @@ private
213
182
  # Internal: Adds a file extension to the file name, unless it already has one.
214
183
  def with_file_extension(name, entry_type)
215
184
  if File.extname(name).empty? && (ext = extension_for_type(entry_type))
216
- "#{name}.#{ext}"
185
+ "#{ name }.#{ ext }"
217
186
  else
218
187
  name
219
188
  end
@@ -222,16 +191,16 @@ private
222
191
  # Internal: Allows to receive :javascript and :stylesheet as :type in helpers.
223
192
  def extension_for_type(entry_type)
224
193
  case entry_type
225
- when :javascript then "js"
226
- when :stylesheet then "css"
227
- when :typescript then "ts"
194
+ when :javascript then 'js'
195
+ when :stylesheet then 'css'
196
+ when :typescript then 'ts'
228
197
  else entry_type
229
198
  end
230
199
  end
231
200
 
232
201
  # Internal: Raises a detailed message when an entry is missing in the manifest.
233
202
  def missing_entry_error(name, **options)
234
- raise ViteRuby::MissingEntrypointError.new(
203
+ raise ViteRuby::MissingEntrypointError, OpenStruct.new(
235
204
  file_name: resolve_entry_name(name, **options),
236
205
  last_build: builder.last_build_metadata,
237
206
  manifest: @manifest,
@@ -5,38 +5,31 @@
5
5
  # NOTE: The complexity here is justified by the improved usability of providing
6
6
  # a more specific error message depending on the situation.
7
7
  class ViteRuby::MissingEntrypointError < ViteRuby::Error
8
- attr_reader :file_name, :last_build, :manifest, :config
8
+ extend Forwardable
9
+ def_delegators :@info, :file_name, :last_build, :manifest, :config
9
10
 
10
- def initialize(file_name:, last_build:, manifest:, config:)
11
- @file_name, @last_build, @manifest, @config = file_name, last_build, manifest, config
11
+ def initialize(info)
12
+ @info = info
12
13
  super <<~MSG
13
- Vite Ruby can't find #{file_name} in the manifests.
14
+ Vite Ruby can't find #{ file_name } in #{ config.manifest_path.relative_path_from(config.root) } or #{ config.assets_manifest_path.relative_path_from(config.root) }.
14
15
 
15
16
  Possible causes:
16
- #{possible_causes(last_build)}
17
+ #{ possible_causes(last_build) }
17
18
  :troubleshooting:
18
- #{"Manifest files found:\n#{config.manifest_paths.map { |path| " #{path.relative_path_from(config.root)}" }.join("\n")}\n" if last_build.success}
19
- #{"Last build in #{config.mode} mode:\n#{last_build.to_json}\n" if last_build.success}
19
+ #{ "\nContent in your manifests:\n#{ JSON.pretty_generate(manifest) }\n" if last_build.success }
20
+ #{ "Last build in #{ config.mode } mode:\n#{ last_build.to_json }\n" unless last_build.success.nil? }
20
21
  MSG
21
22
  end
22
23
 
23
24
  def possible_causes(last_build)
24
- if last_build.success == false
25
- FAILED_BUILD_CAUSES
26
- .sub(":mode:", config.mode)
27
- .sub(":errors:", last_build.errors.to_s.gsub(/^(?!$)/, " "))
28
- elsif config.auto_build
29
- DEFAULT_CAUSES
30
- else
31
- DEFAULT_CAUSES + NO_AUTO_BUILD_CAUSES
32
- end
33
- end
25
+ return FAILED_BUILD_CAUSES.sub(':mode:', config.mode) if last_build.success == false
26
+ return DEFAULT_CAUSES if config.auto_build
34
27
 
35
- FAILED_BUILD_CAUSES = <<~MSG
36
- - The last build failed. Try running `bin/vite build --clear --mode=:mode:` manually and check for errors.
28
+ DEFAULT_CAUSES + NO_AUTO_BUILD_CAUSES
29
+ end
37
30
 
38
- Errors:
39
- :errors:
31
+ FAILED_BUILD_CAUSES = <<-MSG
32
+ - The last build failed. Try running `bin/vite build --clear --mode=:mode:` manually and check for errors.
40
33
  MSG
41
34
 
42
35
  DEFAULT_CAUSES = <<-MSG
@@ -7,7 +7,7 @@ class ViteRuby::MissingExecutableError < ViteRuby::Error
7
7
  ❌ The vite binary is not available. Have you installed the npm packages?
8
8
 
9
9
  :troubleshooting:
10
- #{error}
10
+ #{ error }
11
11
  MSG
12
12
  end
13
13
  end
@@ -8,11 +8,11 @@ class ViteRuby::Runner
8
8
 
9
9
  # Public: Executes Vite with the specified arguments.
10
10
  def run(argv, exec: false)
11
- config.within_root {
11
+ Dir.chdir(config.root) {
12
12
  cmd = command_for(argv)
13
13
  return Kernel.exec(*cmd) if exec
14
14
 
15
- log_or_noop = ->(line) { logger.info("vite") { line } } unless config.hide_build_console_output
15
+ log_or_noop = ->(line) { logger.info('vite') { line } } unless config.hide_build_console_output
16
16
  ViteRuby::IO.capture(*cmd, chdir: config.root, with_output: log_or_noop)
17
17
  }
18
18
  rescue Errno::ENOENT => error
@@ -23,31 +23,23 @@ private
23
23
 
24
24
  extend Forwardable
25
25
 
26
- def_delegators :@vite_ruby, :config, :logger, :env
26
+ def_delegators :@vite_ruby, :config, :logger
27
27
 
28
28
  # Internal: Returns an Array with the command to run.
29
29
  def command_for(args)
30
- [config.to_env(env)].tap do |cmd|
31
- exec_args, vite_args = args.partition { |arg| arg.start_with?("--node-options") }
32
- cmd.push(*vite_executable(*exec_args))
33
- cmd.push(*vite_args)
34
- cmd.push("--mode", config.mode) unless args.include?("--mode") || args.include?("-m")
30
+ [config.to_env].tap do |cmd|
31
+ args = args.clone
32
+ cmd.push('node', '--inspect-brk') if args.delete('--inspect')
33
+ cmd.push('node', '--trace-deprecation') if args.delete('--trace_deprecation')
34
+ cmd.push(vite_executable)
35
+ cmd.push(*args)
36
+ cmd.push('--mode', config.mode) unless args.include?('--mode') || args.include?('-m')
35
37
  end
36
38
  end
37
39
 
38
40
  # Internal: Resolves to an executable for Vite.
39
- def vite_executable(*exec_args)
41
+ def vite_executable
40
42
  bin_path = config.vite_bin_path
41
- return [bin_path] if bin_path && File.exist?(bin_path)
42
-
43
- x = case config.package_manager
44
- when "npm" then %w[npx]
45
- when "pnpm" then %w[pnpm exec]
46
- when "bun" then %w[bun x --bun]
47
- when "yarn" then %w[yarn]
48
- else raise ArgumentError, "Unknown package manager #{config.package_manager.inspect}"
49
- end
50
-
51
- [*x, *exec_args, "vite"]
43
+ File.exist?(bin_path) ? bin_path : "#{ `npm bin`.chomp }/vite"
52
44
  end
53
45
  end
@@ -1,9 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class ViteRuby
4
- VERSION = "3.11.0"
4
+ VERSION = '4.0.0.alpha1'
5
5
 
6
6
  # Internal: Versions used by default when running `vite install`.
7
- DEFAULT_VITE_VERSION = "^8.0.0"
8
- DEFAULT_PLUGIN_VERSION = "^5.2.0"
7
+ DEFAULT_VITE_VERSION = '^2.7.7'
8
+ DEFAULT_PLUGIN_VERSION = '^3.0.5'
9
9
  end