Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds Bun support #392

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/ruby.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ jobs:
with:
node-version: '18'

- uses: oven-sh/setup-bun@v1

- uses: ruby/setup-ruby@v1
with:
ruby-version: ${{ matrix.ruby }}
Expand Down
14 changes: 14 additions & 0 deletions test/rake_tasks_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,20 @@ def test_rake_vite_install_dependencies_in_production_environment
'Expected development dependencies to be installed as well'
end

def test_rake_vite_install_dependencies_supports_bun
ViteRuby.commands.send(:with_node_env, 'production') do
Dir.chdir(test_app_path) do
`touch bun.lockb`
`bundle exec rake vite:install_dependencies`
end
end

assert_includes installed_node_module_names, 'right-pad',
'Expected development dependencies to be installed as well'
ensure
FileUtils.rm_f(File.expand_path('bun.lockb', test_app_path))
end

private

def test_app_path
Expand Down
2 changes: 1 addition & 1 deletion test/runner_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def test_build_command_with_argument
end

def test_command_capture
ViteRuby::Runner.stub_any_instance(:vite_executable, 'echo') {
ViteRuby::PackageManager::Yarn.stub_any_instance(:vite_executable, 'echo') {
stdout, stderr, status = ViteRuby.run(['"Hello"'])
assert_equal %("Hello" --mode production\n), stdout
assert_equal '', stderr
Expand Down
4 changes: 3 additions & 1 deletion test/test_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@
require 'minitest/reporters'
require 'minitest/stub_any_instance'

Minitest::Reporters.use! [Minitest::Reporters::DefaultReporter.new(color: true, location: true, fast_fail: true)]
unless ENV['RM_INFO']
Minitest::Reporters.use! [Minitest::Reporters::DefaultReporter.new(color: true, location: true, fast_fail: true)]
end

require 'rails'
require 'rails/test_help'
Expand Down
12 changes: 7 additions & 5 deletions vite_ruby/lib/tasks/vite.rake
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,13 @@ namespace :vite do

desc 'Ensure build dependencies like Vite are installed before bundling'
task :install_dependencies do
install_env_args = ENV['VITE_RUBY_SKIP_INSTALL_DEV_DEPENDENCIES'] == 'true' ? {} : { 'NODE_ENV' => 'development' }
cmd = ViteRuby.commands.legacy_npm_version? ? 'npx ci --yes' : 'npx --yes ci'
result = system(install_env_args, cmd)
# Fallback to `yarn` if `npx` is not available.
system(install_env_args, 'yarn install --frozen-lockfile') if result.nil?
install_env_args = if ENV['VITE_RUBY_SKIP_INSTALL_DEV_DEPENDENCIES'] == 'true'
{}
else
{ 'NODE_ENV' => 'development' }
end

system(install_env_args, ViteRuby.package_manager.install_dependencies_command)
end

desc "Provide information on ViteRuby's environment"
Expand Down
6 changes: 5 additions & 1 deletion vite_ruby/lib/vite_ruby.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class ViteRuby
class << self
extend Forwardable

def_delegators :instance, :config, :configure, :commands, :digest, :env, :run, :run_proxy?
def_delegators :instance, :config, :configure, :commands, :digest, :env, :run, :run_proxy?, :package_manager
def_delegators :config, :mode

def instance
Expand Down Expand Up @@ -128,6 +128,10 @@ def commands
@commands ||= ViteRuby::Commands.new(self)
end

def package_manager
@package_manager ||= ViteRuby::PackageManager.resolve(root: config.root)
end

# Public: Current instance configuration for Vite.
def config
unless defined?(@config)
Expand Down
20 changes: 4 additions & 16 deletions vite_ruby/lib/vite_ruby/cli/install.rb
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ def install_js_dependencies
package_json = root.join('package.json')
write(package_json, '{}') unless package_json.exist?
deps = js_dependencies.join(' ')
run_with_capture("#{ npm_install } -D #{ deps }", stdin_data: "\n")
run_with_capture("#{ add_dependencies_command } -D #{ deps }", stdin_data: "\n")
end

# Internal: Adds compilation output dirs to git ignore.
Expand All @@ -101,7 +101,7 @@ def install_gitignore

# Internal: The root path for the Ruby application.
def root
@root ||= silent_warnings { config.root }
config.root
end

def say(*args)
Expand All @@ -116,19 +116,7 @@ def run_with_capture(*args, **options)
end

# Internal: Support all popular package managers.
def npm_install
return 'yarn add' if root.join('yarn.lock').exist?
return 'pnpm install' if root.join('pnpm-lock.yaml').exist?

'npm install'
end

# Internal: Avoid printing warning about missing vite.json, we will create one.
def silent_warnings
old_stderr = $stderr
$stderr = StringIO.new
yield
ensure
$stderr = old_stderr
def add_dependencies_command
ViteRuby.package_manager.add_dependencies_command
end
end
2 changes: 1 addition & 1 deletion vite_ruby/lib/vite_ruby/cli/upgrade_packages.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@ class ViteRuby::CLI::UpgradePackages < ViteRuby::CLI::Install
def call(**)
say 'Upgrading npm packages'
deps = js_dependencies.join(' ')
run_with_capture("#{ npm_install } -D #{ deps }")
run_with_capture("#{ add_dependencies_command } -D #{ deps }")
end
end
1 change: 1 addition & 0 deletions vite_ruby/lib/vite_ruby/commands.rb
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ def print_info
$stdout.puts "npm: #{ `npm --version` }"
$stdout.puts "yarn: #{ `yarn --version` rescue nil }"
$stdout.puts "pnpm: #{ `pnpm --version` rescue nil }"
$stdout.puts "bun: #{ `bun --version` rescue nil }"
$stdout.puts "ruby: #{ `ruby --version` }"

$stdout.puts "\n"
Expand Down
1 change: 1 addition & 0 deletions vite_ruby/lib/vite_ruby/config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ def config_from_file(path, mode:)

# Internal: If any of these files is modified the build won't be skipped.
DEFAULT_WATCHED_PATHS = %w[
bun.lockb
package-lock.json
package.json
pnpm-lock.yaml
Expand Down
33 changes: 33 additions & 0 deletions vite_ruby/lib/vite_ruby/package_manager.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# frozen_string_literal: true

module ViteRuby::PackageManager
def self.resolve(root:)
package_manager_name = ENV.fetch('VITE_RUBY_PACKAGE_MANAGER', detect_package_manager(root))
package_manager_class_for(package_manager_name).new(root: root)
end

def self.package_manager_class_for(package_manager_name)
case package_manager_name.to_sym
when :bun
ViteRuby::PackageManager::Bun
when :pnpm
ViteRuby::PackageManager::Pnpm
when :yarn
ViteRuby::PackageManager::Yarn
else
ViteRuby::PackageManager::Npm
end
end

def self.detect_package_manager(root)
if root.join('bun.lockb').exist?
:bun
elsif root.join('pnpm-lock.yaml').exist?
:pnpm
elsif root.join('yarn.lock').exist?
:yarn
else
:npm
end
end
end
56 changes: 56 additions & 0 deletions vite_ruby/lib/vite_ruby/package_manager/base.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# frozen_string_literal: true

class ViteRuby::PackageManager::Base
attr_reader :root

def initialize(root: ViteRuby.config.root)
@root = root
end

# Internal: Returns an Array with the command to run.
def command_for(args)
[config.to_env(env)].tap do |cmd|
args = args.clone

# Apply runtime arguments for nodejs
if nodejs_runtime? && (args.include?('--inspect') || args.include?('--trace_deprecation'))
cmd.push('node')
cmd.push('--inspect-brk') if args.delete('--inspect')
cmd.push('--trace-deprecation') if args.delete('--trace_deprecation')
end

# Add vite executable
cmd.push(*vite_executable)

# Adds vite's arguments
cmd.push(*args)

# Force `mode`, a vite's argument, to be set
cmd.push('--mode', config.mode) unless args.include?('--mode') || args.include?('-m')
end
end

private

def nodejs_runtime?
true
end

# Internal: Resolves to an executable for Vite.
def vite_executable
bin_path = config.vite_bin_path
[bin_path] if File.exist?(bin_path)
end

def commands
ViteRuby.commands
end

def config
ViteRuby.config
end

def env
ViteRuby.env
end
end
26 changes: 26 additions & 0 deletions vite_ruby/lib/vite_ruby/package_manager/bun.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# frozen_string_literal: true

module ViteRuby::PackageManager
class Bun < Base
def install_dependencies_command(frozen: true)
frozen ? 'bun install --frozen-lockfile' : 'bun install'
end

def add_dependencies_command
'bun install'
end

private

def nodejs_runtime?
false
end

def vite_executable
shimmed_vite_executable = super || ['vite']

# Forces a script or package to use Bun's runtime instead of Node.js (via symlinking node)
shimmed_vite_executable.unshift('bun', '--bun')
end
end
end
24 changes: 24 additions & 0 deletions vite_ruby/lib/vite_ruby/package_manager/npm.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# frozen_string_literal: true

module ViteRuby::PackageManager
class Npm < Base
def install_dependencies_command(frozen: true)
if frozen
commands.legacy_npm_version? ? 'npm ci --yes' : 'npm --yes ci'
else
'npm install'
end
end

def add_dependencies_command
'npm install'
end

private

# Internal: Resolves to an executable for Vite.
def vite_executable
super || ["#{ `npm bin`.chomp }/vite"]
end
end
end
13 changes: 13 additions & 0 deletions vite_ruby/lib/vite_ruby/package_manager/pnpm.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# frozen_string_literal: true

module ViteRuby::PackageManager
class Pnpm < Npm
def install_dependencies_command(*)
'pnpm install'
end

def add_dependencies_command
'pnpm install'
end
end
end
19 changes: 19 additions & 0 deletions vite_ruby/lib/vite_ruby/package_manager/yarn.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# frozen_string_literal: true

module ViteRuby::PackageManager
class Yarn < Base
def install_dependencies_command(frozen: true)
frozen ? 'yarn install --frozen-lockfile' : 'yarn install'
end

def add_dependencies_command
'yarn add'
end

private

def vite_executable
super || %w[yarn vite]
end
end
end
21 changes: 1 addition & 20 deletions vite_ruby/lib/vite_ruby/runner.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,25 +27,6 @@ def run(argv, exec: false)

# Internal: Returns an Array with the command to run.
def command_for(args)
[config.to_env(env)].tap do |cmd|
args = args.clone
cmd.push('node', '--inspect-brk') if args.delete('--inspect')
cmd.push('node', '--trace-deprecation') if args.delete('--trace_deprecation')
cmd.push(*vite_executable)
cmd.push(*args)
cmd.push('--mode', config.mode) unless args.include?('--mode') || args.include?('-m')
end
end

# Internal: Resolves to an executable for Vite.
def vite_executable
bin_path = config.vite_bin_path
return [bin_path] if File.exist?(bin_path)

if config.root.join('yarn.lock').exist?
%w[yarn vite]
else
["#{ `npm bin`.chomp }/vite"]
end
@vite_ruby.package_manager.command_for(args)
end
end
Loading