Skip to content

Commit

Permalink
feat: add package_manager config option, experimental support for bun
Browse files Browse the repository at this point in the history
closes #324
  • Loading branch information
ElMassimo committed Jul 17, 2024
1 parent 330f61f commit 37e6671
Show file tree
Hide file tree
Showing 7 changed files with 52 additions and 29 deletions.
12 changes: 12 additions & 0 deletions docs/src/config/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,18 @@ You can customize this behavior using the following options.
Allows to skip Vite build output from logs, to keep the noise down.
### packageManager
- **Default:** auto-detected based on existing lockfiles, otherwise `"npm"`
- **Env Var:** `VITE_RUBY_PACKAGE_MANAGER`
Allows to specify which package manager to use, such as:
- `npm`
- `pnpm`
- `yarn`
- `bun` (experimental)
### root
- **Default:** `Rails.root`
Expand Down
1 change: 1 addition & 0 deletions vite-plugin-ruby/default.vite.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"publicOutputDir": "vite",
"configPath": "config/vite.json",
"devServerConnectTimeout": 0.01,
"packageManager": null,
"publicDir": "public",
"entrypointsDir": "entrypoints",
"sourceCodeDir": "app/frontend",
Expand Down
1 change: 1 addition & 0 deletions vite_ruby/default.vite.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"publicOutputDir": "vite",
"configPath": "config/vite.json",
"devServerConnectTimeout": 0.01,
"packageManager": null,
"publicDir": "public",
"entrypointsDir": "entrypoints",
"sourceCodeDir": "app/frontend",
Expand Down
17 changes: 8 additions & 9 deletions vite_ruby/lib/vite_ruby/cli/install.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@
class ViteRuby::CLI::Install < Dry::CLI::Command
desc 'Performs the initial configuration setup to get started with Vite Ruby.'

def call(**)
option(:package_manager, values: %w[npm pnpm yarn bun], aliases: ['package-manager'], desc: 'The package manager to use when installing JS dependencies.')

def call(package_manager: nil, **)
ENV['VITE_RUBY_PACKAGE_MANAGER'] ||= package_manager if package_manager

$stdout.sync = true

say 'Creating binstub'
Expand Down Expand Up @@ -93,8 +97,7 @@ def install_js_dependencies
FileUtils.mv root.join('vite.config.ts'), root.join('vite.config.mts'), force: true, verbose: true
end

deps = js_dependencies.join(' ')
run_with_capture("#{ npm_install } -D #{ deps }", stdin_data: "\n")
install_dependencies js_dependencies.join(' ')
end

# Internal: Adds compilation output dirs to git ignore.
Expand Down Expand Up @@ -128,12 +131,8 @@ def run_with_capture(*args, **options)
end
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'
def install_dependencies(deps)
run_with_capture("#{ config.package_manager } add -D #{ deps }", stdin_data: "\n")
end

# Internal: Avoid printing warning about missing vite.json, we will create one.
Expand Down
8 changes: 4 additions & 4 deletions vite_ruby/lib/vite_ruby/commands.rb
Original file line number Diff line number Diff line change
Expand Up @@ -109,11 +109,11 @@ def print_info
$stdout.puts "#{ framework }: #{ Gem.loaded_specs[framework]&.version }"
end

$stdout.puts "node: #{ `node --version` }"
$stdout.puts "npm: #{ `npm --version` }"
$stdout.puts "yarn: #{ `yarn --version` rescue nil }"
$stdout.puts "pnpm: #{ `pnpm --version` rescue nil }"
$stdout.puts "ruby: #{ `ruby --version` }"
$stdout.puts "node: #{ `node --version` }"

pkg = config.package_manager
$stdout.puts "#{ pkg }: #{ `#{ pkg } --version` rescue nil }"

$stdout.puts "\n"
packages = `npm ls vite vite-plugin-ruby`
Expand Down
19 changes: 15 additions & 4 deletions vite_ruby/lib/vite_ruby/config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -96,17 +96,27 @@ def within_root(&block)
def coerce_values(config)
config['mode'] = config['mode'].to_s
config['port'] = config['port'].to_i
config['root'] = Pathname.new(config['root'])
config['build_cache_dir'] = config['root'].join(config['build_cache_dir'])
config['ssr_output_dir'] = config['root'].join(config['ssr_output_dir'])
config['root'] = root = Pathname.new(config['root'])
config['build_cache_dir'] = root.join(config['build_cache_dir'])
config['ssr_output_dir'] = root.join(config['ssr_output_dir'])
coerce_booleans(config, 'auto_build', 'hide_build_console_output', 'https', 'skip_compatibility_check', 'skip_proxy')
config['package_manager'] ||= detect_package_manager(root)
end

# Internal: Coerces configuration options to boolean.
def coerce_booleans(config, *names)
names.each { |name| config[name] = [true, 'true'].include?(config[name]) }
end

def detect_package_manager(root)
return 'npm' if root.join('package-lock.json').exist?
return 'pnpm' if root.join('pnpm-lock.yaml').exist?
return 'bun' if root.join('bun.lockb').exist?
return 'yarn' if root.join('yarn.lock').exist?

'npm'
end

def initialize(attrs)
@config = attrs.tap { |config| coerce_values(config) }.freeze
ViteRuby::CompatibilityCheck.verify_plugin_version(root) unless skip_compatibility_check
Expand Down Expand Up @@ -189,15 +199,16 @@ 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
postcss.config.js
tailwind.config.js
vite.config.js
vite.config.mjs
vite.config.ts
vite.config.mts
vite.config.ts
windi.config.ts
yarn.lock
].freeze
Expand Down
23 changes: 11 additions & 12 deletions vite_ruby/lib/vite_ruby/runner.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,27 +28,26 @@ 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|
npx_options, vite_args = args.partition { |arg| arg.start_with?('--node-options') }
cmd.push(*vite_executable)

# NOTE: Vite will parse args, so we need to disambiguate and pass them to
# `npx` before specifying the `vite` executable.
cmd.insert(-2, *npx_options) unless npx_options.empty?

exec_args, vite_args = args.partition { |arg| arg.start_with?('--node-options') }
cmd.push(*vite_executable(*exec_args))
cmd.push(*vite_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
def vite_executable(*exec_args)
bin_path = config.vite_bin_path
return [bin_path] if bin_path && File.exist?(bin_path)

if config.root.join('yarn.lock').exist?
%w[yarn vite]
else
%w[npx vite]
x = case config.package_manager
when 'npm' then %w[npx]
when 'pnpm' then %w[pnpm exec]
when 'bun' then %w[bun x]
when 'yarn' then %w[yarn]
else raise ArgumentError, "Unknown package manager #{ config.package_manager.inspect }"
end

[*x, *exec_args, 'vite']
end
end

0 comments on commit 37e6671

Please sign in to comment.