Skip to content

Commit bba6357

Browse files
committed
Puma plugins to run yarn or bun
1 parent 5fc0b10 commit bba6357

File tree

3 files changed

+149
-0
lines changed

3 files changed

+149
-0
lines changed

README.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,19 @@ Or, in Rails 7+, you can preconfigure your new application to use a specific bun
3939

4040
## FAQ
4141

42+
### Puma plugin
43+
44+
Using [Puma](https://github.com/puma/puma) in development you can now add a plugin to build and compile your javascript. Just add the following to your `puma.rb`.
45+
46+
```
47+
# config/puma.rb
48+
plugin :yarn if ENV.fetch("RAILS_ENV", "development") == "development"
49+
50+
# or if you're using Bun
51+
plugin :bun if ENV.fetch("RAILS_ENV", "development") == "development"
52+
53+
```
54+
4255
### Is there a work-around for lack of glob syntax on Windows?
4356

4457
The default build script for esbuild relies on the `app/javascript/*.*` glob pattern to compile multiple entrypoints automatically. This glob pattern is not available by default on Windows, so you need to change the build script in `package.json` to manually list the entrypoints you wish to compile.

lib/puma/plugin/bun.rb

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
require "puma/plugin"
2+
3+
Puma::Plugin.create do
4+
attr_reader :puma_pid, :bun_pid, :log_writer
5+
6+
def start(launcher)
7+
@log_writer = launcher.log_writer
8+
@puma_pid = $$
9+
@bun_pid = fork do
10+
Thread.new { monitor_puma }
11+
# Using IO.popen(command, 'r+') will avoid watch_command read from $stdin.
12+
# If we use system(*command) instead, IRB and Debug can't read from $stdin
13+
# correctly bacause some keystrokes will be taken by watch_command.
14+
IO.popen("bun run build --watch", "r+") do |io|
15+
IO.copy_stream(io, $stdout)
16+
end
17+
end
18+
19+
launcher.events.on_stopped { stop_bun }
20+
21+
in_background do
22+
monitor_bun
23+
end
24+
end
25+
26+
private
27+
def stop_bun
28+
Process.waitpid(bun_pid, Process::WNOHANG)
29+
log "Stopping Bun..."
30+
Process.kill(:INT, bun_pid) if bun_pid
31+
Process.wait(bun_pid)
32+
rescue Errno::ECHILD, Errno::ESRCH
33+
end
34+
35+
def monitor_puma
36+
monitor(:puma_dead?, "Detected Puma has gone away, stopping Bun...")
37+
end
38+
39+
def monitor_bun
40+
monitor(:bun_dead?, "Detected Bun has gone away, stopping Puma...")
41+
end
42+
43+
def monitor(process_dead, message)
44+
loop do
45+
if send(process_dead)
46+
log message
47+
Process.kill(:INT, $$)
48+
break
49+
end
50+
sleep 2
51+
end
52+
end
53+
54+
def bun_dead?
55+
Process.waitpid(bun_pid, Process::WNOHANG)
56+
false
57+
rescue Errno::ECHILD, Errno::ESRCH
58+
true
59+
end
60+
61+
def puma_dead?
62+
Process.ppid != puma_pid
63+
end
64+
65+
def log(...)
66+
log_writer.log(...)
67+
end
68+
end

lib/puma/plugin/yarn.rb

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
require "puma/plugin"
2+
3+
Puma::Plugin.create do
4+
attr_reader :puma_pid, :yarn_pid, :log_writer
5+
6+
def start(launcher)
7+
@log_writer = launcher.log_writer
8+
@puma_pid = $$
9+
@yarn_pid = fork do
10+
Thread.new { monitor_puma }
11+
# Using IO.popen(command, 'r+') will avoid watch_command read from $stdin.
12+
# If we use system(*command) instead, IRB and Debug can't read from $stdin
13+
# correctly bacause some keystrokes will be taken by watch_command.
14+
IO.popen("yarn build --watch", "r+") do |io|
15+
IO.copy_stream(io, $stdout)
16+
end
17+
end
18+
19+
launcher.events.on_stopped { stop_yarn }
20+
21+
in_background do
22+
monitor_yarn
23+
end
24+
end
25+
26+
private
27+
def stop_yarn
28+
Process.waitpid(yarn_pid, Process::WNOHANG)
29+
log "Stopping Yarn..."
30+
Process.kill(:INT, yarn_pid) if yarn_pid
31+
Process.wait(yarn_pid)
32+
rescue Errno::ECHILD, Errno::ESRCH
33+
end
34+
35+
def monitor_puma
36+
monitor(:puma_dead?, "Detected Puma has gone away, stopping Yarn...")
37+
end
38+
39+
def monitor_yarn
40+
monitor(:yarn_dead?, "Detected Yarn has gone away, stopping Puma...")
41+
end
42+
43+
def monitor(process_dead, message)
44+
loop do
45+
if send(process_dead)
46+
log message
47+
Process.kill(:INT, $$)
48+
break
49+
end
50+
sleep 2
51+
end
52+
end
53+
54+
def yarn_dead?
55+
Process.waitpid(yarn_pid, Process::WNOHANG)
56+
false
57+
rescue Errno::ECHILD, Errno::ESRCH
58+
true
59+
end
60+
61+
def puma_dead?
62+
Process.ppid != puma_pid
63+
end
64+
65+
def log(...)
66+
log_writer.log(...)
67+
end
68+
end

0 commit comments

Comments
 (0)