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

Add Bun support #49241

Merged
merged 15 commits into from Sep 12, 2023
Merged

Add Bun support #49241

merged 15 commits into from Sep 12, 2023

Conversation

terracatta
Copy link
Contributor

@terracatta terracatta commented Sep 12, 2023

Related PRs

This PR should only be merged when all of the following PRs have been merged and releases cut for each gem, otherwise the --javascript bun option will not work as expected.

Also as a bonus I updated execJS so we can use the bun runtime for that as well.

Introduction

Bun is a new and viable alternative to the node.js runtime, yarn package manager, and esbuild bundler. Bun's primary differentiating characteristic is speed. It's often many multiple times faster than node.js and friends.

Since most vanilla Rails projects are looking to simply sprinkle a little JS here and there (but sometimes want a bit better more of the JS ecosystem than the import-maps provide) Bun is a really good fit and can be easily adopted by new rails projects.

Since it's such a nice fit, I propose we make it a first class citizen for folks looking to build a new Rails project with common defaults like stimulus, turbo, tailwind, etc. We should be able to spin up these projects with bun and not have to spend hours surgically removing remnants of Yarn/Node.

Example Bun and Tailwind Run

After this PR is merged, you will be able to spin up a new Rails project with options like rails new --javascript bun --css tailwind --database sqlite3 and everything should "just work".

☁  rails [add_bun_support] ⚡  bundle exec rails new ~/code/terracatta/jason-rails-test --dev --javascript bun --css tailwind --database sqlite3
      create
      create  Gemfile
         run  bundle install
Resolving dependencies...Fetching gem metadata from https://rubygems.org/..........
............
Using rake 13.0.6
Using base64 0.1.1
Using bigdecimal 3.1.4
Using concurrent-ruby 1.2.2
Using connection_pool 2.4.1
Using ruby2_keywords 0.0.5
Using websocket-extensions 0.1.5
Using mutex_m 0.1.2
Using builder 3.2.4
Using erubi 1.12.0
Using mini_mime 1.1.5
Using bundler 2.3.22
Using rack 3.0.8
Using io-console 0.6.0
Using stringio 3.0.8
Using minitest 5.20.0
Using timeout 0.4.0
Using i18n 1.14.1
Using racc 1.7.1
Using drb 2.1.1
Using crass 1.0.6
Using nio4r 2.5.9
Using webrick 1.8.1
Using zeitwerk 2.6.11
Using thor 1.2.2
Using marcel 1.0.2
Using rackup 2.1.0
Using nokogiri 1.15.4 (arm64-darwin)
Using tzinfo 2.0.6
Using loofah 2.21.3
Using rack-session 2.0.0
Using rack-test 2.1.0
Using psych 5.1.0
Using net-protocol 0.2.1
Using reline 0.3.8
Using activesupport 7.1.0.alpha from source at `/Users/jmeller/code/terracatta/rails`
Using rails-html-sanitizer 1.6.0
Using date 3.3.3
Using net-pop 0.1.2
Using net-smtp 0.3.3
Using rdoc 6.5.0
Using websocket-driver 0.7.6
Using net-imap 0.3.7
Using irb 1.8.1
Using mail 2.8.1
Using activemodel 7.1.0.alpha from source at `/Users/jmeller/code/terracatta/rails`
Using rails-dom-testing 2.2.0
Using globalid 1.2.1
Using actionview 7.1.0.alpha from source at `/Users/jmeller/code/terracatta/rails`
Using activejob 7.1.0.alpha from source at `/Users/jmeller/code/terracatta/rails`
Using actionpack 7.1.0.alpha from source at `/Users/jmeller/code/terracatta/rails`
Using activerecord 7.1.0.alpha from source at `/Users/jmeller/code/terracatta/rails`
Using actioncable 7.1.0.alpha from source at `/Users/jmeller/code/terracatta/rails`
Using actionmailer 7.1.0.alpha from source at `/Users/jmeller/code/terracatta/rails`
Using railties 7.1.0.alpha from source at `/Users/jmeller/code/terracatta/rails` and installing its executables
Using activestorage 7.1.0.alpha from source at `/Users/jmeller/code/terracatta/rails`
Using actionmailbox 7.1.0.alpha from source at `/Users/jmeller/code/terracatta/rails`
Using actiontext 7.1.0.alpha from source at `/Users/jmeller/code/terracatta/rails`
Using rails 7.1.0.alpha from source at `/Users/jmeller/code/terracatta/rails`
Bundle complete! 1 Gemfile dependency, 59 gems now installed.
Use `bundle info [gemname]` to see where a bundled gem is installed.
         run  bundle lock --add-platform=x86_64-linux
Resolving dependencies...Fetching gem metadata from https://rubygems.org/........
............................................
Writing lockfile to /Users/jmeller/code/terracatta/jason-rails-test/Gemfile.lock
         run  bundle lock --add-platform=aarch64-linux
Resolving dependencies...Fetching gem metadata from https://rubygems.org/........
.....................................................
Writing lockfile to /Users/jmeller/code/terracatta/jason-rails-test/Gemfile.lock
         run  bundle exec rails new /Users/jmeller/code/terracatta/jason-rails-test --dev --javascript bun --css tailwind --database sqlite3
       exist
      remove  Gemfile
      remove  Gemfile.lock
      create  README.md
      create  Rakefile
      create  .ruby-version
      create  config.ru
      create  .gitignore
      create  .gitattributes
      create  Gemfile
         run  git init -b main from "."
Initialized empty Git repository in /Users/jmeller/code/terracatta/jason-rails-test/.git/
      create  app
      create  app/assets/config/manifest.js
      create  app/assets/stylesheets/application.css
      create  app/channels/application_cable/channel.rb
      create  app/channels/application_cable/connection.rb
      create  app/controllers/application_controller.rb
      create  app/helpers/application_helper.rb
      create  app/jobs/application_job.rb
      create  app/mailers/application_mailer.rb
      create  app/models/application_record.rb
      create  app/views/layouts/application.html.erb
      create  app/views/layouts/mailer.html.erb
      create  app/views/layouts/mailer.text.erb
      create  app/assets/images
      create  app/assets/images/.keep
      create  app/controllers/concerns/.keep
      create  app/models/concerns/.keep
      create  bin
      create  bin/rails
      create  bin/rake
      create  bin/setup
      create  Dockerfile
      create  .dockerignore
      create  bin/docker-entrypoint
      create  config
      create  config/routes.rb
      create  config/application.rb
      create  config/environment.rb
      create  config/cable.yml
      create  config/puma.rb
      create  config/storage.yml
      create  config/environments
      create  config/environments/development.rb
      create  config/environments/production.rb
      create  config/environments/test.rb
      create  config/initializers
      create  config/initializers/assets.rb
      create  config/initializers/content_security_policy.rb
      create  config/initializers/cors.rb
      create  config/initializers/filter_parameter_logging.rb
      create  config/initializers/inflections.rb
      create  config/initializers/new_framework_defaults_7_1.rb
      create  config/initializers/permissions_policy.rb
      create  config/locales
      create  config/locales/en.yml
      create  config/master.key
      append  .gitignore
      create  config/boot.rb
      create  config/database.yml
      create  db
      create  db/seeds.rb
      create  lib
      create  lib/tasks
      create  lib/tasks/.keep
      create  lib/assets
      create  lib/assets/.keep
      create  log
      create  log/.keep
      create  public
      create  public/404.html
      create  public/422.html
      create  public/500.html
      create  public/apple-touch-icon-precomposed.png
      create  public/apple-touch-icon.png
      create  public/favicon.ico
      create  public/robots.txt
      create  tmp
      create  tmp/.keep
      create  tmp/pids
      create  tmp/pids/.keep
      create  tmp/cache
      create  tmp/cache/assets
      create  vendor
      create  vendor/.keep
      create  test/fixtures/files
      create  test/fixtures/files/.keep
      create  test/controllers
      create  test/controllers/.keep
      create  test/mailers
      create  test/mailers/.keep
      create  test/models
      create  test/models/.keep
      create  test/helpers
      create  test/helpers/.keep
      create  test/integration
      create  test/integration/.keep
      create  test/channels/application_cable/connection_test.rb
      create  test/test_helper.rb
      create  test/system
      create  test/system/.keep
      create  test/application_system_test_case.rb
      create  storage
      create  storage/.keep
      create  tmp/storage
      create  tmp/storage/.keep
      remove  config/initializers/cors.rb
      remove  config/initializers/new_framework_defaults_7_1.rb
         run  bundle install
Fetching https://github.com/terracatta/cssbundling-rails.git
Fetching https://github.com/terracatta/stimulus-rails.git
Fetching https://github.com/terracatta/turbo-rails.git
Fetching https://github.com/rails/jsbundling-rails.git
Fetching gem metadata from https://rubygems.org/...........
Resolving dependencies...
Using rake 13.0.6
Using base64 0.1.1
Using bigdecimal 3.1.4
Using concurrent-ruby 1.2.2
Using connection_pool 2.4.1
Using ruby2_keywords 0.0.5
Using websocket-extensions 0.1.5
Using mutex_m 0.1.2
Using builder 3.2.4
Using erubi 1.12.0
Using racc 1.7.1
Using crass 1.0.6
Using public_suffix 5.0.3
Using bundler 2.3.22
Using nio4r 2.5.9
Using zeitwerk 2.6.11
Using timeout 0.4.0
Using marcel 1.0.2
Using mini_mime 1.1.5
Using date 3.3.3
Using rack 3.0.8
Using bindex 0.8.1
Using minitest 5.20.0
Using matrix 0.4.2
Using regexp_parser 2.8.1
Using sqlite3 1.6.5 (arm64-darwin)
Using stringio 3.0.8
Using i18n 1.14.1
Using io-console 0.6.0
Using error_highlight 0.5.1
Using rexml 3.2.6
Using rubyzip 2.3.2
Using websocket 1.2.9
Using websocket-driver 0.7.6
Using tzinfo 2.0.6
Using webrick 1.8.1
Using drb 2.1.1
Using psych 5.1.0
Using nokogiri 1.15.4 (arm64-darwin)
Using addressable 2.8.5
Using activesupport 7.1.0.alpha from source at `/Users/jmeller/code/terracatta/rails`
Using net-protocol 0.2.1
Using rack-session 2.0.0
Using rdoc 6.5.0
Using xpath 3.2.0
Using net-imap 0.3.7
Using net-pop 0.1.2
Using net-smtp 0.3.3
Using rackup 2.1.0
Using loofah 2.21.3
Using rack-test 2.1.0
Using sprockets 4.2.1
Using mail 2.8.1
Using thor 1.2.2
Using reline 0.3.8
Using selenium-webdriver 4.12.0
Using puma 6.3.1
Using rails-html-sanitizer 1.6.0
Using irb 1.8.1
Using capybara 3.39.2
Using debug 1.8.0
Using activemodel 7.1.0.alpha from source at `/Users/jmeller/code/terracatta/rails`
Using rails-dom-testing 2.2.0
Using globalid 1.2.1
Using actionview 7.1.0.alpha from source at `/Users/jmeller/code/terracatta/rails`
Using activejob 7.1.0.alpha from source at `/Users/jmeller/code/terracatta/rails`
Using actionpack 7.1.0.alpha from source at `/Users/jmeller/code/terracatta/rails`
Using jbuilder 2.11.5
Using activerecord 7.1.0.alpha from source at `/Users/jmeller/code/terracatta/rails`
Using actionmailer 7.1.0.alpha from source at `/Users/jmeller/code/terracatta/rails`
Using actioncable 7.1.0.alpha from source at `/Users/jmeller/code/terracatta/rails`
Using railties 7.1.0.alpha from source at `/Users/jmeller/code/terracatta/rails` and installing its executables
Using activestorage 7.1.0.alpha from source at `/Users/jmeller/code/terracatta/rails`
Using sprockets-rails 3.4.2
Using actionmailbox 7.1.0.alpha from source at `/Users/jmeller/code/terracatta/rails`
Using actiontext 7.1.0.alpha from source at `/Users/jmeller/code/terracatta/rails`
Using cssbundling-rails 1.2.0 from https://github.com/terracatta/cssbundling-rails.git (at add_bun_support@2673455)
Using stimulus-rails 1.2.2 from https://github.com/terracatta/stimulus-rails.git (at jem_add_bun_support@c234d9e)
Using web-console 4.2.1
Using turbo-rails 1.4.0 from https://github.com/terracatta/turbo-rails.git (at add_bun_support@de710c1)
Using jsbundling-rails 1.1.2 from https://github.com/rails/jsbundling-rails.git (at main@e308e76)
Using rails 7.1.0.alpha from source at `/Users/jmeller/code/terracatta/rails`
Bundle complete! 15 Gemfile dependencies, 82 gems now installed.
Use `bundle info [gemname]` to see where a bundled gem is installed.
         run  bundle lock --add-platform=x86_64-linux
Fetching gem metadata from https://rubygems.org/...........
Resolving dependencies...
Writing lockfile to /Users/jmeller/code/terracatta/jason-rails-test/Gemfile.lock
         run  bundle lock --add-platform=aarch64-linux
Fetching gem metadata from https://rubygems.org/...........
Resolving dependencies...
Writing lockfile to /Users/jmeller/code/terracatta/jason-rails-test/Gemfile.lock
         run  bundle binstubs bundler
       rails  javascript:install:bun
       apply  /Users/jmeller/.rvm/gems/ruby-3.2.2/bundler/gems/jsbundling-rails-e308e76e22ae/lib/install/install.rb
  Compile into app/assets/builds
      create    app/assets/builds
      create    app/assets/builds/.keep
      append    app/assets/config/manifest.js
      append    .gitignore
      append    .gitignore
  Add JavaScript include tag in application layout
      insert    app/views/layouts/application.html.erb
  Create default entrypoint in app/javascript/application.js
      create    app/javascript
      create    app/javascript/application.js
  Add default package.json
      create    package.json
  Add bin/dev to start foreman
      create    bin/dev
         run  bundle install
....snip
         run  bundle lock --add-platform=x86_64-linux
Writing lockfile to /Users/jmeller/code/terracatta/jason-rails-test/Gemfile.lock
         run  bundle lock --add-platform=aarch64-linux
Writing lockfile to /Users/jmeller/code/terracatta/jason-rails-test/Gemfile.lock
       apply  /Users/jmeller/.rvm/gems/ruby-3.2.2/bundler/gems/jsbundling-rails-e308e76e22ae/lib/install/bun/install.rb
  Add default Procfile.dev
      create    Procfile.dev
  Ensure foreman is installed
         run    gem install foreman from "."
Successfully installed foreman-0.87.2
Parsing documentation for foreman-0.87.2
Done installing documentation for foreman after 0 seconds
1 gem installed
  Add default bun.config.js
      create    bun.config.js
  Add build script to package.json
         run  bundle install
....snip
         run  bundle lock --add-platform=x86_64-linux
Writing lockfile to /Users/jmeller/code/terracatta/jason-rails-test/Gemfile.lock
         run  bundle lock --add-platform=aarch64-linux
Writing lockfile to /Users/jmeller/code/terracatta/jason-rails-test/Gemfile.lock
       rails  turbo:install stimulus:install
       apply  /Users/jmeller/.rvm/gems/ruby-3.2.2/bundler/gems/turbo-rails-de710c16a780/lib/install/turbo_with_bun.rb
  Import Turbo
      append    app/javascript/application.js
  Install Turbo
         run    bun add @hotwired/turbo-rails from "."
bun add v1.0.0 (822a00c4)

 installed @hotwired/turbo-rails@7.3.0


 3 packages installed [4.00ms]
         run  bundle install
...snip
         run  bundle lock --add-platform=x86_64-linux
Writing lockfile to /Users/jmeller/code/terracatta/jason-rails-test/Gemfile.lock
         run  bundle lock --add-platform=aarch64-linux
Writing lockfile to /Users/jmeller/code/terracatta/jason-rails-test/Gemfile.lock
Run turbo:install:redis to switch on Redis and use it in development for turbo streams
       apply  /Users/jmeller/.rvm/gems/ruby-3.2.2/bundler/gems/stimulus-rails-c234d9e7466f/lib/install/stimulus_with_bun.rb
  Create controllers directory
      create    app/javascript/controllers
      create    app/javascript/controllers/index.js
      create    app/javascript/controllers/application.js
      create    app/javascript/controllers/hello_controller.js
  Import Stimulus controllers
      append    app/javascript/application.js
  Install Stimulus
         run    bun add @hotwired/stimulus from "."
bun add v1.0.0 (822a00c4)

 installed @hotwired/stimulus@3.2.2


 1 packages installed [4.00ms]
         run  bundle install
...snip
         run  bundle lock --add-platform=x86_64-linux
Writing lockfile to /Users/jmeller/code/terracatta/jason-rails-test/Gemfile.lock
         run  bundle lock --add-platform=aarch64-linux
Writing lockfile to /Users/jmeller/code/terracatta/jason-rails-test/Gemfile.lock
       apply  /Users/jmeller/.rvm/gems/ruby-3.2.2/bundler/gems/cssbundling-rails-2673455f3f25/lib/install/tailwind/install.rb
  Install Tailwind (+PostCSS w/ autoprefixer)
      create    tailwind.config.js
      create    app/assets/stylesheets/application.tailwind.css
         run    bun add tailwindcss@latest postcss@latest autoprefixer@latest from "."
bun add v1.0.0 (822a00c4)

 installed tailwindcss@3.3.3 with binaries:
  - tailwind
  - tailwindcss
 installed postcss@8.4.29
 installed autoprefixer@10.4.15 with binaries:
  - autoprefixer


 91 packages installed [1031.00ms]
  Add build:css script
         run    bun run build:css from "."
$ tailwindcss -i ./app/assets/stylesheets/application.tailwind.css -o ./app/assets/builds/application.css --minify

Rebuilding...

Done in 104ms.
         run  bundle install
...snip
         run  bundle lock --add-platform=x86_64-linux
Writing lockfile to /Users/jmeller/code/terracatta/jason-rails-test/Gemfile.lock
         run  bundle lock --add-platform=aarch64-linux
Writing lockfile to /Users/jmeller/code/terracatta/jason-rails-test/Gemfile.lock

Not once does the word "yarn" appear!

Dockerfile Support

This PR also updates the Dockerfile to support install/using the Bun runtime. Here is what it looks like to build it after the run above...

☁  jason-rails-test [main] ⚡  docker build -t test .
[+] Building 196.4s (21/21) FINISHED                                                                             docker:desktop-linux
 => [internal] load build definition from Dockerfile                                                                             0.0s
 => => transferring dockerfile: 2.03kB                                                                                           0.0s
 => [internal] load .dockerignore                                                                                                0.0s
 => => transferring context: 729B                                                                                                0.0s
 => resolve image config for docker.io/docker/dockerfile:1                                                                       0.7s
 => CACHED docker-image://docker.io/docker/dockerfile:1@sha256:ac85f380a63b13dfcefa89046420e1781752bab202122f8f50032edf31be0021  0.0s
 => [internal] load metadata for registry.docker.com/library/ruby:3.2.2-slim                                                     0.9s
 => [internal] load build context                                                                                                0.0s
 => => transferring context: 14.58kB                                                                                             0.0s
 => [base 1/2] FROM registry.docker.com/library/ruby:3.2.2-slim@sha256:b86f08332ea5f9b73c427018f28af83628c139567cc72823270cac6a  0.0s
 => CACHED [base 2/2] WORKDIR /rails                                                                                             0.0s
 => CACHED [stage-2 1/4] RUN apt-get update -qq &&     apt-get install --no-install-recommends -y curl libsqlite3-0 libvips &&   0.0s
 => CACHED [build 1/8] RUN apt-get update -qq &&     apt-get install --no-install-recommends -y build-essential curl git libvip  0.0s
 => CACHED [build 2/8] RUN curl -fsSL https://bun.sh/install | bash -s -- "bun-v1.0.0"                                           0.0s
 => [build 3/8] COPY Gemfile Gemfile.lock ./                                                                                     0.0s
 => [build 4/8] RUN bundle install &&     rm -rf ~/.bundle/ "/usr/local/bundle"/ruby/*/cache "/usr/local/bundle"/ruby/*/bundl  188.8s
 => [build 5/8] COPY package.json bun.lockb ./                                                                                   0.0s
 => [build 6/8] RUN bun install --frozen-lockfile                                                                                1.2s
 => [build 7/8] COPY . .                                                                                                         0.0s
 => [build 8/8] RUN SECRET_KEY_BASE_DUMMY=1 ./bin/rails assets:precompile                                                        2.4s
 => [stage-2 2/4] COPY --from=build /usr/local/bundle /usr/local/bundle                                                          0.6s
 => [stage-2 3/4] COPY --from=build /rails /rails                                                                                0.2s
 => [stage-2 4/4] RUN useradd rails --create-home --shell /bin/bash &&     chown -R rails:rails db log storage tmp               0.2s
 => exporting to image                                                                                                           0.5s
 => => exporting layers                                                                                                          0.5s
 => => writing image sha256:e260ae4702d7b40ed951f08a754f4fecebbe06f6e0ac0e4172bceabd1124ddfc                                     0.0s
 => => naming to docker.io/library/test                                                                                          0.0s

@duduribeiro
Copy link
Contributor

This PR should only be merged when all of the following PRs have been merged and releases cut for each gem, otherwise the --javascript bun option will not work as expected.

As per @rafaelfranca comment here, we may not need the release cut for each gem since this will not be released before the other gems.

@duduribeiro
Copy link
Contributor

I loved this change <3

thanks @terracatta. Excited to see this live

@rafaelfranca rafaelfranca merged commit 274bc97 into rails:main Sep 12, 2023
4 checks passed
@rafaelfranca
Copy link
Member

Amazing work! Just in time to our release

@terracatta
Copy link
Contributor Author

Amazing work! Just in time to our release

Thanks for such a timely review!

@terracatta terracatta deleted the add_bun_support branch September 12, 2023 21:08
@marco-beduschi
Copy link

awesome stuff! 🖤

@ghiculescu
Copy link
Member

Just in time to our release

We need to get hotwired/turbo-rails#494 and https://github.com/hotwired/stimulus-rails merged (and all the assets gems re-released) if the next Rails beta is happening soon.

def using_bun?
# Cannot assume yarn.lock has been generated yet so we look for
# a file known to be generated by the jsbundling-rails gem
@using_bun ||= using_js_runtime? && Pathname(destination_root).join("bun.config.js").exist?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Memoization only helps here if this returns true. Is there a big performance gain to memoizing @terracatta? If not we could remove it to simplify the code.

@naoyanickf
Copy link

@terracatta hi, I tried -j bun option with rails new. It's very nice!

I have a question.

  • yarn.lock get automatically generated
  • When adding an npm package with bun add zod, yarn.lock doesn't update
  • This seems a bit confusing.

I think yarn.lock is unnecessary for the default. How do you think about it?

@rafaelfranca
Copy link
Member

I think yarn.lock is unnecessary for the default. How do you think about it?

Agree. Mind to open a PR?

@naoyanickf
Copy link

@rafaelfranca

see hotwired/stimulus-rails#125
this seems a known issue

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants