GithubHelp home page GithubHelp logo

rails / jsbundling-rails Goto Github PK

View Code? Open in Web Editor NEW
800.0 25.0 142.0 130 KB

Bundle and transpile JavaScript in Rails with esbuild, rollup.js, or Webpack.

License: MIT License

Ruby 79.53% Shell 5.67% JavaScript 14.80%

jsbundling-rails's Introduction

JavaScript Bundling for Rails

Use Bun, esbuild, rollup.js, or Webpack to bundle your JavaScript, then deliver it via the asset pipeline in Rails. This gem provides installers to get you going with the bundler of your choice in a new Rails application, and a convention to use app/assets/builds to hold your bundled output as artifacts that are not checked into source control (the installer adds this directory to .gitignore by default).

You develop using this approach by running the bundler in watch mode in a terminal with yarn build --watch (and your Rails server in another, if you're not using something like puma-dev). You can also use ./bin/dev, which will start both the Rails server and the JS build watcher (along with a CSS build watcher, if you're also using cssbundling-rails).

Whenever the bundler detects changes to any of the JavaScript files in your project, it'll bundle app/javascript/application.js into app/assets/builds/application.js (and all other entry points configured). You can refer to the build output in your layout using the standard asset pipeline approach with <%= javascript_include_tag "application", defer: true %>.

When you deploy your application to production, the javascript:build task attaches to the assets:precompile task to ensure that all your package dependencies from package.json have been installed via your javascript package manager (bun, npm, pnpm, or yarn), and then runs the build script defined in package.json to process all the entry points, as it would in development. The latter files are then picked up by the asset pipeline, digested, and copied into public/assets, as any other asset pipeline file.

This also happens in testing where the bundler attaches to the test:prepare task to ensure the JavaScript has been bundled before testing commences. If your testing library of choice does not call the test:prepare Rake task, ensure that your test suite runs javascript:build to bundle JavaScript before testing commences.

That's it!

You can configure your bundler options in the build script in package.json or via the installer-generated bun.config.js for Bun, rollup.config.js for rollup.js or webpack.config.json for Webpack (esbuild does not have a default configuration format, and we don't intend to use esbuild as an API in order to hack around it).

If you're already using webpacker and you're wondering if you should migrate to jsbundling-rails, have a look at the high-level comparison. If you're looking to migrate from webpacker, see the migration guide.

If you want to use webpack features like code splitting and hot module reloading, consider using the official fork of webpacker, shakapacker.

Installation

If you are installing esbuild, rollup, or webpack, you must already have node installed on your system. You will also need npx version 7.1.0 or later.

If you are using Bun, then you must have the Bun runtime already installed on your system.

To get started run:

./bin/bundle add jsbundling-rails
./bin/rails javascript:install:[bun|esbuild|rollup|webpack]

Or, in Rails 7+, you can preconfigure your new application to use a specific bundler with rails new myapp -j [bun|esbuild|rollup|webpack].

FAQ

Is there a work-around for lack of glob syntax on Windows?

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.

Why does bun/esbuild overwrite my application.css?

If you import CSS in your application.js while using esbuild or Bun, you'll be creating both an app/assets/builds/application.js and app/assets/builds/application.css file when bundling. The latter can conflict with the app/assets/builds/application.css produced by cssbundling-rails. The solution is to either change the output file for bun/esbuild (and the references for that) or for cssbundling. Both are specified in package.json.

How can I reference static assets in JavaScript code?

Suppose you have an image app/javascript/images/example.png that you need to reference in frontend code built with esbuild.

  1. Create the image at app/javascript/images/example.png.
  2. In package.json, under "scripts" and "build", add the additional arguments:
    • --loader:.png=file This instructs esbuild to copy png files to the build directory.
    • --asset-names=[name]-[hash].digested This tells esbuild to append .digested to the file name so that sprockets or propshaft will not append an additional digest hash to the file.
  3. When esbuild runs, it will copy the png file to something like app/assets/builds/example-5SRKKTLZ.digested.png.
  4. In frontend code, the image is available for import by its original name: import Example from "../images/example.png".
  5. The image itself can now be referenced by its imported name, e.g. in React, <img src={Example} />.
  6. The path of the image resolves to /assets/example-5SRKKTLZ.digested.png, which is served by the asset pipeline.

License

JavaScript Bundling for Rails is released under the MIT License.

jsbundling-rails's People

Contributors

abevoelker avatar adambutler avatar aduth avatar afcapel avatar afdev82 avatar ankane avatar baylorrae avatar benkoshy avatar bholtbholt avatar brenogazzola avatar calvinwalzel avatar davekaro avatar davidcolby avatar davidstosik avatar dhh avatar domchristie avatar dorianmariecom avatar elia avatar excid3 avatar ghiculescu avatar hlfh avatar intrepidd avatar justin808 avatar ksylvest avatar lordofthedanse avatar mibradev avatar rafaelfranca avatar rstacruz avatar terracatta avatar the-spectator avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

jsbundling-rails's Issues

Heroku deployment issues (apologies for Heroku specific issue)

Hello. I'm hesitant to post this here as it's specific to Heroku but I figure it's such a common deployment environment I may as well ask. I'm, not a bad person I swear 👼. Been searching for hours over the past few days to prevent this.

I'm facing two issues with my deployment:

  • Code is building twice due to nodejs+ruby buildpacks (asset compilation builds again)
  • Code splitting does not work

The first is rather minor as the build time are so freaking amazing with esbuild (thx again David). But I can't seem to find a way to use Node 16.9 (or anything newer than 12.6.2) without including the nodejs buildpack which calls yarn install + yarn biuld and then assets are compiled again during the rails buildpack.


The second is also minor for me but something I'd love to get working and that is code splitting. Locally all is well but when I enable splitting in esbuild i get the following error:

Failed to load module script: Expected a JavaScript module script but the server responded with a MIME type of "text/html". Strict MIME type checking is enforced for module scripts per HTML spec

I understand the error, but can't seem to figure out what I need to do for either of these issues. There's a bunch of discussion around the first issue on webpacker and in the ruby buildpack but nothing seemed to translate. I'd love to get this working but neither is a major issue.


My versions:

Rails 6.1.4 (sprockets-rails 3.4.0)
Esbuild 13.14
jsbundling-rails (0.2.1)
node 16.9.0
ruby 3.0.2

My esbuild file. (take from it what you wish I have sourcemaps, live-reload, metafile, postcss, incremental build, shimming and a lot of other goodies in here). The only relevant part i'm aware of to this issue is splitting: true

#! /usr/bin/env node

/* eslint-disable */

//utils
const chalk = require('chalk')
const chokidar = require('chokidar')
const fs = require('fs')
const http = require('http')

// esbuild + plugins
const esBuild = require('esbuild')
const svgrPlugin = require('esbuild-plugin-svgr')
const esbuildPostCssPlugin = require('@deanc/esbuild-plugin-postcss')
const postCssImportPlugin = require('postcss-import')
const postCssTailwindNestingPlugin = require('tailwindcss/nesting')
const postCssTailwindPlugin = require('tailwindcss')
const postCssAutoPrefixer = require('autoprefixer')


//Args
const watch = process.argv.includes('--watch')
const minify = process.argv.includes('--minify')
const isProduction = process.env.NODE_ENV === 'production'


const clients = [];
const svgrOptions = {
  typescript: false
}

const loaders = {
  '.png': 'file'
}

const postCssPlugins = [
  postCssImportPlugin,
  postCssTailwindNestingPlugin,
  postCssTailwindPlugin('./tailwind.config.js'),
  postCssAutoPrefixer
].filter(Boolean)

const watchOptions = {
  onRebuild: (error, result) => {
    clients.forEach((res) => res.write("data: update\n\n"))
    clients.length = 0
    console.log(error ? error : "...");
  }
}

const banner = watch
  ? { js: ' (() => new EventSource("http://localhost:8082").onmessage = () => location.reload())()', }
  : {}

const inject = ['app/javascript/shims/react-shim.js']

const buildOptions = {
  entryPoints: ['app/javascript/application.js'],
  plugins: [
    svgrPlugin(svgrOptions),
    esbuildPostCssPlugin({
      cssPath: './app/assets/stylesheets/',
      plugins: postCssPlugins
    })
  ],
  external: ['*.png', '/images/background.svg'],
  logLevel: 'info',
  target: 'es2020',
  bundle: true,
  platform: 'browser',
  outdir: 'app/assets/builds/',
  sourcemap: !isProduction,
  loader: loaders,
  publicPath: '/assets',
  format: 'esm',
  metafile: true,
  splitting: false, // Want this to work but getting MIME type errors in heroku.
//  watch: watch && watchOptions,
  incremental: watch,
  inject,
  minify,
  banner
}

console.log(buildOptions)

const runBuild = async () => {
  try {
    let result = await esBuild.build({
      ...buildOptions
    })
    console.log(`${chalk.green(' ✔ Success')}`)
    fs.writeFileSync('meta.json', JSON.stringify(result.metafile))

    if (watch) {
      chokidar.watch([
        './app/javascript/**',
        './app/assets/stylesheets/**',
        'tailwind.config.js'
      ]).on('change', async (event, path) => {
        try {
          let rebuild = await result.rebuild()
          clients.forEach((res) => res.write("data: update\n\n"))
          clients.length = 0

          console.log(chalk.green("Rebuild successful."))
          fs.writeFileSync('meta.json', JSON.stringify(rebuild.metafile))
        } catch (err) {
          console.log(chalk.red("Error Rebuilding: "), err)
        }
      })
    }
  } catch (err) {
    console.log(chalk.red("Error Rebuilding: "), err)
    process.exit(1)
  }
}

runBuild()

if (watch) {
  http.createServer((req, res) => {
    return clients.push(
      res.writeHead(200, {
        "Content-Type": "text/event-stream",
        "Cache-Control": "no-cache",
        "Access-Control-Allow-Origin": "*",
        Connection: "keep-alive",
      }),
    )
  }).listen(8082)
}

esbuild plugins

Absolutely love this jsbundling-rails approach. This may be related/redundant to #8.

What is the recommended approach if I wanted to use esbuild plugins?

I noticed that both rollup/webpack have config files that are added on install but not esbuild.


image

The precompile task fails to move js files to the public directory if they did not exist already

While testing other PRs, I've noticed that the assets:precompile fails to move files from the build directory to the assets directory, if the build directory was empty (did not run yarn build --watch before)

Reproduction:

Running inside rails-dev-box

cd ..
mkdir demo
cd demo

../rails/railties/exe/rails new mega --dev -j webpack
rm -rf app/assets/builds/* public/assets public/packs 
RACK_ENV=production RAILS_ENV=production bin/rails assets:precompile

Result

app/assets/builds/actioncable-[hash].js
app/assets/builds/application.js

public/assets/actioncable-[hash].js
public/assets/actioncable.esm-[hash].js
public/assets/actiontext-[hash].js
public/assets/activestorage-[hash].js
public/assets/activestorage.esm-[hash].js
public/assets/manifest-[hash].js
public/assets/[email protected][hash].js
public/assets/stimulus-autoloader-[hash].js
public/assets/stimulus-[hash].js
public/assets/stimulus-importmap-autoloader-[hash].js
public/assets/trix-[hash].js
public/assets/turbo-[hash].js

You can see the application.js was not moved. It is also not listed in .sprockets-manifest. This means that javascript_include_tag cannot find it.

However... if I do assets:precompile again:

$ webpack --config webpack.config.js
assets by status 7.78 KiB [cached] 1 asset
asset application.js 95.8 KiB [compared for emit] [minimized] (name: application)
orphan modules 189 KiB [orphan] 15 modules
runtime modules 6.75 KiB 9 modules
cacheable modules 190 KiB
  ./app/javascript/application.js + 7 modules 173 KiB [built] [code generated]
  ./node_modules/@rails/actioncable/src/index.js + 8 modules 16.8 KiB [built] [code generated]
webpack 5.52.1 compiled successfully in 2163 ms
Done in 2.99s.
I, [2021-09-13T13:17:01.433519 #146072]  INFO -- : Writing /vagrant/demo/mega/public/assets/actioncable-a9fcb0bc8b59438ae97a7b2e98a4160c8a7075e52e73ae5ac605ef1ab23748cb.js
I, [2021-09-13T13:17:01.433902 #146072]  INFO -- : Writing /vagrant/demo/mega/public/assets/actioncable-a9fcb0bc8b59438ae97a7b2e98a4160c8a7075e52e73ae5ac605ef1ab23748cb.js.gz
I, [2021-09-13T13:17:01.434675 #146072]  INFO -- : Writing /vagrant/demo/mega/public/assets/application-82f15c75ea5d419d0cad09eed8beb84807bfbd5741fae8b7bc8c0ae209a6bded.js
I, [2021-09-13T13:17:01.435301 #146072]  INFO -- : Writing /vagrant/demo/mega/public/assets/application-82f15c75ea5d419d0cad09eed8beb84807bfbd5741fae8b7bc8c0ae209a6bded.js.gz

There they go. This time the missing files are moved and everything works.

Support chunk lazy loading

Our production app using Webpacker 6 currently has many files that employ lazy loading of chunks:

// Only load Trix when necessary
  async bootstrap () {
    const { default: Trix } = await import(/* webpackChunkName: "trix" */ '../../chunks/trix')
    return Trix
  }

We are switching to jsbundling, but the code above is broken. It seems that Webpack can't find the chunk because of the fingerprint that Sprockets add to the end of the file name.

Is this possible with jsbundling right now, or would it be necessary for me to open a PR to add the support? I tried reading through the Webpacker code, but couldn't figure out what it was doing to make this work.

No way to use the gem without sprockets installed

Hello. I'm so glad to see great progress on assets management in Rails, especially in Rails 7. I noticed that sprockets are no longer a required dependency, and webpacker is deprecated, so I decided to migrate to the latest assets management with jsbundling-rails, cssbundling-rails, esbuild, and tailwind.

But it was a surprise for me that sprockets is still required for the latest assets management. rails new comes with sprockets installed, and the latest released version of this gem expects the assets:precompile rake task to be defined.

The biggest problem is that

    <%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
    <%= javascript_include_tag "application", "data-turbo-track": "reload", defer: true %>

does not work without sprockets. It renders just elements as is:

    <link rel="stylesheet" href="/stylesheets/application.css" data-turbo-track="reload" />
    <script src="/javascripts/application.js" data-turbo-track="reload" defer="defer"></script>

that leads to 404 responses for the Rails.

As far as I understand the goal is to replace complicated and old sprockets with foreign tools, such as esbuild and tailwind, so that's why I created this issue to track the progress.

I'm sorry if I get something wrong, and these gems still expect sprockets to be installed

Thank you

Can't load images in JavaScript files

Hi,

I'm trying to migrate my Rails + React application from Webpacker to Jsbundling + Esbuild following switch_from_webpacker guide to first replace webpacker for Jsbundling

Everything is bundled to "app/assets/builds" by webpack, images included.

application.js and application.css are working fine in application.html.erb but the images in jsx files aren't loading.

My React component are trying to load "0bdd8103a525a17c3528e4f40d701b33.svg" the same as output in assets/builds folder but public/assets has the digested version of my svg "0bdd8103a525a17c3528e4f40d701b33-b8fb0ca2943e7724f64b3717f8a3e2b3ecb321adac1053107f2c89142efffd17.svg"

import React from "react";
import Logo from "Assets/logo-brand.svg";

export default function Img() {
  return <img height="46px" src={Logo}  />;
}

I'm not sure if the correct approach is to move all assets from assets/build to public/asssets with a rake or if it is possible to skip sprockets-rails digest for images and rely on webpack digested file or if there is another way to load imagens inside js files

Thanks for jsbundling effort!

Don't know how to build task 'assets:precompile'

when running rails --tasks for instance:

~/s/hello> rails --tasks
rails aborted!
Don't know how to build task 'assets:precompile' (See the list of available tasks with `rails --tasks`)
/Users/dorianmariefr/.rvm/gems/ruby-3.0.2/gems/jsbundling-rails-0.1.9/lib/tasks/jsbundling/build.rake:10:in `<main>'
/Users/dorianmariefr/.rvm/gems/ruby-3.0.2/gems/bootsnap-1.9.1/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:60:in `load'
/Users/dorianmariefr/.rvm/gems/ruby-3.0.2/gems/bootsnap-1.9.1/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:60:in `load'
/Users/dorianmariefr/.rvm/gems/ruby-3.0.2/bundler/gems/rails-6e83d065ae37/railties/lib/rails/engine.rb:661:in `block in run_tasks_blocks'
/Users/dorianmariefr/.rvm/gems/ruby-3.0.2/bundler/gems/rails-6e83d065ae37/railties/lib/rails/engine.rb:661:in `each'
/Users/dorianmariefr/.rvm/gems/ruby-3.0.2/bundler/gems/rails-6e83d065ae37/railties/lib/rails/engine.rb:661:in `run_tasks_blocks'
/Users/dorianmariefr/.rvm/gems/ruby-3.0.2/bundler/gems/rails-6e83d065ae37/railties/lib/rails/application.rb:501:in `block in run_tasks_blocks'
/Users/dorianmariefr/.rvm/gems/ruby-3.0.2/bundler/gems/rails-6e83d065ae37/railties/lib/rails/engine/railties.rb:15:in `each'
/Users/dorianmariefr/.rvm/gems/ruby-3.0.2/bundler/gems/rails-6e83d065ae37/railties/lib/rails/engine/railties.rb:15:in `each'
/Users/dorianmariefr/.rvm/gems/ruby-3.0.2/bundler/gems/rails-6e83d065ae37/railties/lib/rails/application.rb:501:in `run_tasks_blocks'

Setup is:

rails new hello

  • Add jsbundling-rails gem
  • Point rails to main, currently 6e83d065ae3722e384d005adf3d4a01487fb89fd

jsbundling files overwriting cssbundling generated files.

While generating files for jsbundling and cssbundling, some of the files are overwritten. Not sure if that will cause my app to behave abnormally in the future, so I would like to know beforehand how it impacts my app, because I am suspecting that this is probably breaking my Bootstrap JS.

I've tried rails assets:precompile, as well as running the app from bin/dev, but it just won't work.

Some of the errors/warnings I've received:

File unchanged! The supplied flag value not found!  app/assets/config/manifest.js
File unchanged! The supplied flag value not found!  .gitignore
File unchanged! The supplied flag value not found!  .gitignore
conflict  bin/dev
Overwrite /home/ashvith/Desktop/todo-app/bin/dev? (enter "h" for help) [Ynaqdhm] Y
    force  bin/dev

I've started my app with the default settings (importmap, hotwire, sqlite3). Removed the importmap-rails, and replaced them with jsbundling-rails and cssbundling-rails.

Asset URLs in JavaScript files

Hey!

I'm using Vue.js in a project and I can't figure out how to reference image files managed by the asset pipeline in the templates. Is there a recommended way to do this?

I understand that the bundling phase occurs before the asset pipeline does anything, so is there anything like a special URL I can put in the template which the asset pipeline rewrites later?

I've also encountered this issue with cssbundling-rails and wondered if a similar solution might be available?

Thanks!

Abort install or detailed info message when NPX < 7.1

Installing jsbundinling-rails without previously upgrading npx to (>= 7.1) returns the error below when trying to set the build scripts in package.json. This is briefly mentioned in the README: You will also need npx version 7.1.0 or later.

[email protected] /usr/local/lib/node_modules/npm

Did you mean this?
    run-script

To help a successful install, the user could be informed:

  • by adding a more informative / actionable info message that checks at the beginning of the install script the currently installed npx version
  • or by aborting the install script as it will need to be run again after upgrading NPX to (>=7.1).

Although, I don't have any experience in building engines with thor, I gave it a short try and something like below could work if it's being inserted before the right install script. Any feedback is welcome.

require 'open3'

say "Checking node version"

stdout, _stderr, _status = Open3.capture3("npx -v")

npx_version = stdout.match(/\d.\d+/).to_s.to_f

unless npx_version >= 7.1
  abort "Upgrade npx to version 7.1 or later", :red
end

This could be applied to cssbunding-rails too.

yarn build --watch to scream a fail into the browser

Hello,
One nice feature to have would be jsbundling-rails to return an error in the browser in case of a yarn build failing, either the Rails way, or by a simple alert. (so we can check Puma or retry a yarn build to get proper message)
and kudos for the gem. It's really good.

compute_asset_path does not update when using jsbundling + esbuild

Hello, I have a script that is accessed by clients via GET /embed.js, I have a controller that does the redirect to the precompiled asset, so it redirects to /embed-[the-hashed-id].js This works in production but in development environment the method ActionController::Base.helpers.compute_asset_path does return an old version hash on filename after a esbuild build. My workaround right now is to restart the server, then the hash changes correclty.
This worked as expected with webpacker. the problem started when I've integrated esbuild with js-bundling

Any idea what could be the problem?

example controller:

  def show
    respond_to do |format|
      format.js { redirect_to widget_javascript_source }
    end
  end

  private

  def widget_javascript_source
    ActionController::Base.helpers.compute_asset_path("embed.js", debug: true)
  end

jsbundling with esbuild generated application.css that override the one from cssbundling with sass

Hi,

I used jsbundling with esbuild. At the install rake task, it shows me a "build" command to put to package.json.

Then, I used cssbundling with sass and it also give me a "build:css" run string that should be put to package.json.

It means that I have "build" and "build:css" script run strings in package.json. I used the default ones that was displayed by install step.

Then, I realized that both run strings generate application.css inside app build. If I edit a scss file, then application.css was generated by "build:css". But if I edit a "js" file, then application.css was generated by "build". I don't know that esbuild could generate a css.

Then, I change the runstring of "build:css" to generate another filename and it works fine together.

I think that we may include this in readme or a doc.

Thanks,

Support for building engine assets

I've noticed this gem and the potential it will bring to Rails 7. This is probably not the right place to ask but many of us are probably wondering if these changes in asset compilation will have a positive effect on engine development.

In the past we could store all of the JS inside our engine (potentially wrapped in a gem) and they would be picked up pretty easily by sprockets. With the introduction of webpacker this was made virtually impossible. We currently have to resort to wrapping all assets in a node module and distribute them via yarn/npm which adds a lot of overhead. Most of the times the JS inside an engine has no use outside of it. Because it's so tightly coupled we'd like to go back to the early experience of just being able to import it as if it's living inside of our main application.

Are there any plans to integrate something like this for the release of Rails 7?

Task to remove JS builds might break apps on PaaS like heroku

According to the sprockets doc :

rake assets:clean

Only removes old assets (keeps the most recent 3 copies) from public/assets. Useful when doing rolling deploys that may still be serving old assets while the new ones are being compiled.

PaaS like heroku run this task to keep things tidy.

Now with commit 8b4d2a9 this task will completely remove JS files from the builds directory, which is unexpected. This broke my deployment and I had to revert the gem to its previous version.

I suspect many people will have the same issue, should we rather just not enhance assets:clean with javascript:clean ?

Question about Comparison document

In comparison_with_webpacker.md:

  • jsbundling-rails is a lighter integration. webpacker can be more involved and opinionated. The difference is whether or not you use only the view helpers of shakapacker or include the webpack configuration setup.

What does the second sentence compare between?

CSS override question

Hi,

In relation to the question and answer in the readme:

Why does esbuild overwrite my application.css?
If you import CSS in your application.js while using esbuild, you'll be creating both an app/assets/builds/application.js and app/assets/builds/application.css file when bundling. The latter can conflict with the app/assets/builds/application.css produced by cssbundling-rails. The solution is to either change the output file for esbuild (and the references for that) or for cssbundling. Both are specified in package.json.

I changed the line in my package.json to "build:css": "sass ./app/assets/stylesheets/application.sass.scss ./app/assets/builds/application-styles.css --no-source-map --load-path=node_modules" but I expected to have to update my stylesheet_link_tag line in my layout and I also expected a file in app/assets/builds called application-style.css, but I don;t get that file and I didn't have to update my view.

Why does this work? Please could someone explain a little further to help me understand.

Thanks

Esbuild Configuration for Javascript Debugging

Migrated from Webpacker to Esbuild using jsbundling-rails and it seems like Javascript debugging no longer works for either VSCode nor Rubymine.

This is the ESBuild settings I am using.

require("esbuild").build({
  entryPoints: ["application.js", "administrate.js"],
  bundle: true,
  outdir: path.join(process.cwd(), "app/assets/builds"),
  absWorkingDir: path.join(process.cwd(), "app/javascript"),
  watch: watch,
  plugins: [ImportGlobPlugin()],
}).catch(() => process.exit(1));

Sourcemaps with esbuild not working.

I'm actually not sure if this is a problem with jsbundling-rails or bundler, but:

I'm bundling my js/css via esbuild --bundle --watch --sourcemap which results in 2 files being generated in assets/builds, application.js and application.js.map. The application requests these via:
/assets/application.debug-1caabe86ba2a00618598d8d7d5a8fe6c57a594dd7babeec3fe6e3ff3ce9185e1.js
/assets/application.js-4112a73edf43c31165109b2906c7bcda25ff0505d405213d39501087ea154f74.map

The sourcemap url does not exist.

If I look at the bottom of my application.js in the build folder, it ends with:

//# sourceMappingURL=application.js.map

And if I look at the end of the file coming from a server request, it ends with:

//# sourceMappingURL=/assets/application.js-5673ea8258e5021fb9befebfe3f84d5c8ff21ee5fd1e52b4f54b8347a5bbfbbb.map
//!
;

//# sourceMappingURL=application.js-4112a73edf43c31165109b2906c7bcda25ff0505d405213d39501087ea154f74.map

The first source map url works, the second does not. I'm assuming bundler is somehow fixing the existing sourcemap definition, but then also adding it's own?

Running esbuild without --sourcemaps ends up with sourcemaps that work on the client, but they are not useful.

Is there a workflow that will work here?

Sprockets::DoubleLinkError: Multiple files with the same output path cannot be linked ("application.js")

I got this issue after add gem 'jsbundling-rails', '~> 0.2.2' to my application

rake aborted!
Sprockets::DoubleLinkError: Multiple files with the same output path cannot be linked ("application.js")
In "/Users/xx/xxx/app/assets/config/manifest.js" these files were linked:

  • /Users/xx/xxx/app/assets/javascripts/application.js
  • /Users/xx/xxx/app/assets/builds/application.js

Tasks: TOP => assets:precompile
(See full trace by running task with --trace)

Please advise how can I fix this

You have to have an empty assets/builds folder in the repo

Guys, I have a very strange problem

When I deploy a project and run RAILS_ENV=production rails assets:precompile the order of invoking is

Invoke assets:precompile (first_time)
Invoke assets:environment (first_time)
Execute assets:environment
Invoke environment (first_time)
Execute environment
Invoke css:build (first_time)
Execute css:build
Invoke javascript:build (first_time)
Execute javascript:build
✨  Done in 0.25s.
Execute assets:precompile

And there are no application.css and application.js files in the assets folder. As I understand, it is because when assets:precompile was invoking, the builds folder was empty.

But if I run yarn:build at first manually, then everything is good.

Heroku not building js, rare behavior

Hello there, I've been days banging my head on this, and I could not explain what's happening:

I'm using jsbundling on a Rails 6.1.4.4 / sprockets (4.0.2), the compilation works on local, ( files are stored in app/assets/build and served properly by sprockets). I can see the app running ok, but when I deploy to Heroku the rails assets:precompile I get different results.,

In Heroku, the asset files are not copied to the public/assets. It seems that it find files on public/assets, so it skips that part.
I've done an "enhance" to the assets:precompile rake task in order to remove all the files in the public/assets before the precompilation. That way, it generates the files in Heroku, but the JS files are not copied into public/assets 🤯. It copies only a portion of the assets, like images and CSS but not the .js file. I've replicated that in a github action build where the config.assets is config.assets.compile = false so it kinda replicates what's happening in production.

I've tried many strategies to get this working but without avail. Has anyone seen this rare behavior before?

reference static assets doesn't work

About PR #58 @, I would like to use Esbuild for all our static assets instead of using a different solution for images.
I tried what is in the README, but it also generates .js/.css files in the output directory.

our script:

package.json

"scripts": {
  "build:js": "esbuild app/javascript/*.* --bundle --loader:.png=file --outdir=app/assets/builds/ --public-path=assets",
  "build:css": "sass app/stylesheets/application.scss ./app/assets/builds/application.css --no-source-map --load-path=node_modules"
}

javascript/application.js

// Entry point for the build script in your package.json
import "@hotwired/turbo-rails"
import "./controllers"
import * as bootstrap from "bootstrap"

There is an image in: javascript/images/logo.png

Is there a step I'm missing so that the image is copied to the output directory?

When generating rails 7 new with js, the file at app/javascript/controllers/index.js is different

When generating a Rails app with rails new, the file at app/javascript/controllers/index.js, whose job is to load all the controllers is:

import { application } from "controllers/application"

// Eager load all controllers defined in the import map under controllers/**/*_controller
import { eagerLoadControllersFrom } from "@hotwired/stimulus-loading"
eagerLoadControllersFrom("controllers", application)

When generating a new rails app using --javascript=webpack or --css=bootstrap, I noticed that the file at app/javascript/controllers/index.js looks like this instead:

// This file is auto-generated by ./bin/rails stimulus:manifest:update
// Run that command whenever you add a new controller or create them with
// ./bin/rails generate stimulus controllerName

import { application } from "./application"

import HelloController from "./hello_controller.js"
application.register("hello", HelloController)

Is one of these preferred over the other?

is the choice between the implementations relevant to the usage of the flag or is it just haphazard? (that is, is there some reason why it is like this)

my 2¢: It seems that smart/eager loading that eliminates the need to update the manifest file is better than having to remember to update a manifest file
-Jason

A config for esbuild

👋

After using esbuild for a few days via the esbuild-rails gem and now this gem, one thing I've found myself needing is to make an esbuild.config.js file across all of my applications.

I realize from the other gem issues one concern is that it'd turn into a mini webpacker, but config files are fairly common across JS packages and even the two other bundlers in this gem have config files so I found it super odd that esbuild does not have one generating out of the box.

Additionally, it helps ease the burden of where do I add a new config/plugin when I want to use it with esbuild, the end user then has to go create a config file and update their build script to use it, which doesn't feel quite well.

I've been enjoying having my esbuild.config.js file in the root level (Same as gemfile / rollup.config.js etc etc). I know in rails/esbuild-rails#2 you mentioned it may be better to do documentation with a link to learning where to make a config file, but that feels like a very poor developer UX when its fairly straightforward to support this basic of a config file... and it basically matches the level of configuration the rollup.config.js is.

This is of course basically what @jasoncharnes had in esbuild-rails#2. You could drop the watch function / arg down in the build but you loose the output and honestly having the build finished output is quite nice to know it... well finished.

const watch = process.argv.includes("--watch") && {
  onRebuild(error) {
    if (error) console.error("[watch] build failed", error);
    else console.log("[watch] build finished");
  },
};

 require("esbuild").build({
  entryPoints: ["app/javascript/application.js"],
  bundle: true,
  outdir: "app/assets/builds",
  watch: watch,
  plugins: [],
}).catch(() => process.exit(1));

From this point a developer can customize their esbuild system how they'd like, adding plugins to load whatever they need and so forth. It was a fair bit to explain how to make this file in a screencast I recorded, and feels like a point where someone will easily get stuck.

I hope you'll reconsider!

Add Vite as option for JS bundling

I really love the excellent work that is being done allowing people to move over from webpack to esbuild and rollup.

I'm seeing a lot of effort going in to supporting esbuild and rollup as two options with both their own pros and cons. There is however a way you can have your cake and eat it too with Vite.

It's a build tool built by the creator of Vue that has a great momentum, it combines both esbuild for dev (speed), and rollup for production (backwards compatibility). And it's aim is to "provide a faster and leaner development experience for modern web projects" which fits well with rails.

Vite is a lot more convention over configuration compared to other builders. And it has official support for a wide array of popular javascript frameworks, super fast hot module reload even on huge projects and other developer ergonomics. And there are a of community plugins and templates.

There is even a rails gem to get it working with rails I've been using vite-rails for a while instead of webpacker and I couldn't be happier!

Honestly I rolled my eyes once I saw yet another javascript build tool, but since Vite doesn't try to reinvent the wheel, just bring together the best of the latest generation of build tools in a package that is easy to use and easy to setup I think this has the potential to be a great default rails/javascript build tool for people doing React, vue angular etc.

More info:
Why Vite?
How does it compare to X?

use yarn lockfile

Hi,

I noticed that the build script is doing yarn install just here. In some cases it's what we want to do, but when deploying we would want to run yarn install --frozen-lockfile instead.

Any reason it's not using the lockfile?

I'm also thinking that the install step shouldn't be included at all in the build step. That's removing a lot of flexibility when deploying.

Thanks

sourceMappingURL missing in prod using sprockets and esbuild

Thank you for this new gem! I'm trying to get source maps working, I apologize in advance if I misunderstood (I have a limited understanding of front-end tooling).

Source maps do seem to work in our Rails 7 app in development (using sprockets 4.0.2, sprockets-rails 3.4.2, jsbundling-rails 1.0.1 and esbuild 0.14.23): assets:precompile generates a JS bundle with a sourceMappingURL clause at the end. However they do not seem to work in production: the sourceMappingURL clause is absent in this case.

Because of this we do not have proper Javascript crash reports in our monitoring system.

We have this under package.json's scripts: "build": "esbuild app/javascript/*.* --bundle --sourcemap --target=es2016 --minify --outdir=app/assets/builds". I've tried with both config.assets.debug = false (as suggested) and config.assets.debug = true in application.rb, but this does not seem to change the situation. Some debug info below (point 3 is problematic).

  1. When running that esbuild command, two files are generated as expected: app/assets/builds/application.js and app/assets/builds/application.js.map, where the last line of the JS file is //# sourceMappingURL=application.js.map.

  2. When running RAILS_ENV=development bin/rails assets:precompile, two files are generated: public/assets/application-cc3245bb241eac905dc4ffd8a5abc8d3d42445e9e1986ea93ba7b267e583f1cf.js and public/assets/application.js-67cdc37aff88978cfa5a2cdba39ef49c3da8d9b6ff16f4c7eca581548009fe0c.map, and the last lines of the JS file are (which seem correct):

//# sourceMappingURL=/assets/application.js-67cdc37aff88978cfa5a2cdba39ef49c3da8d9b6ff16f4c7eca581548009fe0c.map
//!
;
  1. When running RAILS_ENV=production bin/rails assets:precompile, two files are generated: public/assets/application-6c7fc839b49f22a88c3f45c04cde931d8a6801ad0312f478165ac605915c1d55.js and public/assets/application.js-67cdc37aff88978cfa5a2cdba39ef49c3da8d9b6ff16f4c7eca581548009fe0c.map. However the JS file has no sourceMappingURL statement at the end, it ends with //!.

Is there a way to make source maps work with jsbundling-rails and esbuild?

Glob syntax not supported for Windows out of the box [with esbuild]

Currently, the build script involves: esbuild app/javascript/*.* --bundle --outdir=app/assets/builds

On Windows, this results in an error: Could not resolve "'app/javascript/*.*'" (glob syntax must be expanded first before passing the paths to esbuild).

From what I've found troubleshooting, it appears that the glob syntax is a shell-specific feature. esbuild does not interpret it on its own. With PowerShell or CMD on Windows not supporting the glob syntax properly, the build commands doesn't work out of the box in these shell environments.

I'm not sure how to go on from here for a general solution. For now, in my project I changed the build script to reference the only .js file I have: esbuild app/javascript/application.js --bundle --outdir=app/assets/builds

Stimulus does not connect for default new rails apps with --javascript=webpacker

Hey can someone tell me if this new jsbundling-rails using Webpack paradigm can work with Stimlus?

Something seems very strange I've been back and forward over rails new lately and the flags produce some very strange results results in an impact to Stimulus being incorrectly setup when you use the --css=bootstrap flag or the --javascript=webpack flag.

Steps to reproduce:

  1. rails new TestJSBundlingRails20220122 --javascript=webpack
  2. rails generate controller Articles
  3. add def index to articles controller, uncomment line root "articles#index" in routes file, them create views/articles/index.erb with this contents:
<div data-controller="hello">
  hello world
</div>

in the app/javascript/controllers/hello_controller.js add a console message:

import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
  connect() {
    
    console.log("hello controller was connected")
    this.element.textContent = "Hello World!"
  }
}

Now load the browser.

Actual Result: You will see only "Hello world" but nothing logged in the console

DevTools - 127 0 0 1:3000: 2022-01-22 15-41-09

Expected Result: The hello controller is connected to the page correctly and the console log message show up

Sample reproduction here:

https://github.com/jasonfb/TestJSBundlingRails20220122

Can this just be a generator?

Both jsbundling-rails and cssbundling-rails do little outside of the initial generation. Is the rake task enhancements the only other behavior? If so, could you add that rake task to the generator? This removes the gems as a required dependencies and allows for customization of the rake task.

For example, I removed the gems and added this rake file.

namespace :yarn do
  desc "Build your JavaScript & CSS"
  task :build do
    system "yarn run build"
  end
end

Rake::Task["assets:precompile"].enhance(["yarn:build"])

I changed the yarn build script to build both js and css and added other yarn scripts to handle the watch behavior.

I'm not suggesting that rake task be the default, just that moving it into the generator gives us the flexibility of customizing it to best fit the project.

CSS assets paths in 3rd party libraries when using jsbundling-rails

One of the things that does not appear to be working are images referenced in CSS files that are a part of a 3rd party library such as Photoswipe.

The library itself works but the images fail. Photoswipe includes a SCSS variable for the path to the skin and when I was using Webpack I had this line set as below:

$pswp__assets-path: "../../../node_modules/photoswipe/src/css/default-skin/"; // path to skin assets folder (preloader, PNG and SVG sprite)
This is now failing, I've tried soooo many combinations of things but I cannot for the life of me seem to be able to get those images working.

This is not just a Photoswipe issue, it's happening in a number of other libraries where images are being referenced.

Any suggestions?

Failed javascript:install:webpack

./bin/rails javascript:install:webpack
...
...
Add build script
         run  npm set-script build "webpack --config webpack.config.js" from "."

Usage: npm <command>

where <command> is one of:
    access, adduser, audit, bin, bugs, c, cache, ci, cit,
    clean-install, clean-install-test, completion, config,
    create, ddp, dedupe, deprecate, dist-tag, docs, doctor,
    edit, explore, fund, get, help, help-search, hook, i, init,
    install, install-ci-test, install-test, it, link, list, ln,
    login, logout, ls, org, outdated, owner, pack, ping, prefix,
    profile, prune, publish, rb, rebuild, repo, restart, root,
    run, run-script, s, se, search, set, shrinkwrap, star,
    stars, start, stop, t, team, test, token, tst, un,
    uninstall, unpublish, unstar, up, update, v, version, view,
    whoami

npm <command> -h  quick help on <command>
npm -l            display full usage info
npm help <term>   search for help on <term>
npm help npm      involved overview

Specify configs in the ini-formatted file:
    /Users/kivanio/.npmrc
or on the command line via: npm <command> --key value
Config info can be viewed via: npm help config

[email protected] /usr/local/Cellar/node@14/14.17.6/lib/node_modules/npm

Did you mean this?
    run-script

Support non-JS bundlers?

I’ve often found that using a bundler’s CLI along with a package.json build script is the most transparent way to build assets: input / output files are clearly defined, making it easy to know what’s going on. jsbundling-rails looks like a great interface for managing JS bundlers in this way, but I’m wondering if it’s scope could be expanded to install non-JS builders.

For example, installing and running a Tailwind builder follows a very similar pattern to the JS builders:

  1. Add a package.json with a build script
  2. Add a tailwind.config.js specifying JIT mode and purge options
  3. Add the tailwind.css manifest

This looks something like: main...domchristie:tailwind

One potential problem: running more than one build script might get messy, particularly with a —watch option?

Edit: and another: preventing installations from clobbering package.json

Set Webpack `mode` from what?

Hey team,

Before submitted a dead-simple PR I just wanted to raise an issue and ask if a typo was made in the Switch from Webpacker 5 to jsbundling-rails with webpack guide. My own team recently realized that our jsbundling-rails + webpack setup was generating development builds in production — the reason ultimately being that we previously used webpacker (which I presume forced webpack into production mode because the RAILS_ENV was in production mode) and once we moved forward to jsbundling-rails + webpack (Rails 7) setup, we never actually added RAILS_ENV to our production environment (Heroku). That said, there's a bit at the end of that guide that goes:

Optional: Add support for development environment

jsbundling-rails ships with only production mode. You can dramatically speed up build times during development by switching to mode: 'development'.

// Make the following changes in webpack.config.js
+ const mode = process.env.NODE_ENV === 'development' ? 'development' : 'production';

module.exports = {
-  mode: "production",
+  mode,
-  devtool: "source-map",+  optimization: {
+    moduleIds: 'hashed',
+  }
}

Which we did do, but since there was no NODE_ENV in our production environment to begin with, didn't do much. Thinking about it a little bit, was this line a typo?

const mode = process.env.NODE_ENV === 'development' ? 'development' : 'production';

Was that supposed to actually pull the current environment from the RAILS_ENV? I feel like the line as written isn't that useful — Webpack already listens to NODE_ENV and sets the mode by default based on that value (see the "Tip" on https://webpack.js.org/configuration/mode/). If it was supposed to be RAILS_ENV then that makes more sense — I wouldn't have to set a NODE_ENV on my production (or local) hosts and webpack would always just follow the Rails environment (which feels nice).

Just wanted to ask 🙂

with esbuild, how to dynamic load stimulus controllers?

I know this is a question about esbuild not jsbundling-rails, but when webpacker is replaced by jsbundling-rails, how to make it works smoothly with stimulus?

import stimulus controllers

import { Application } from "stimulus"

const application = Application.start()
const context = require.context("controllers", true, /_controller\.js$/)
application.load(definitionsFromContext(context))

browser got this error

Uncaught TypeError: __require.context is not a function
    at application.source.js:2329
    at application.source.js:2331

esbuild does not print error messages wait failing to generate .map

Using Rails 7.0.1 and esbuild to compile my javascript,
$ esbuild app/javascript/*.* --bundle --sourcemap --outdir=app/assets/builds --watch

does not always generate an application.js.map alongside application.js. When it fails, it doesn't output any error message that would allow troubleshooting.

db:tests:prepare enchanced with javascript:build

It is not designed to do so. Right now i cannot use ActiveRecord::Migration.maintain_test_schema! because it calls yarn.

I understand importance of backwards compatibility, but there at least should be some flag. Best of all if it would rely on config.assets.compile

Move JavaScript default directory back to app/assets/javascripts ?

When sprockets and webpacker coexist, /app/javascript can avoid conflicts.

Now JavaScript is back to the management of Asset Pipeline, it seems more reasonable to set the default directory to app/assets/javascripts.

Just an idea, may be a lot of work to consider.

Detect the use of Yarn PnP when using esbuild

It seems that jsbundling-rails knows absolutely nothing about Yarn versions above 1.x. If Yarn version globally set to 2.x or 3.x, and rails new myapp -j esbuild is called - JS bundling does not work unless you fix it yourself. Yarn Modern uses Plug'n'Play as default installing option and it breaks Rails environment.

For reference: Yarn Plug'n'Play drops old idea of having node_modules dir in app folder - instead it caches all dependencies and generates .pnp.cjs file that maps them to package.json entries. Without ESBuild plugin esbuild-plugin-pnp it will not work, as ESBuild will try to find dependencies in node_modules folder.

As I think we should look for Yarn version and/or Yarn nodeLinker config - if Yarn version is higher than 1.x, nodeLinker is not explicitly set to pnpm or node_modules, choose one of the strategies:

  • Add esbuild-plugin-pnp, use ESBuild API and enable this plugin.
  • Create a .yarnrc and set linker to pnpm or node_modules.
  • Change the Yarn version for this project to 1.x.

Sprockets breaks the sourcemap comment

I'm using esbuild for js bundling, command:

esbuild app/assets/javascripts/*.* --bundle --outdir=app/assets/builds --sourcemap

Then sourcemap comment is added to builds/application.js:

//# sourceMappingURL=application.js.map

But after process by assets pipeline, it becomes:

//# sourceMappingURL=application.js.map;

It add a ; at the end and break the sourcemap config.

I found it's caused by https://github.com/rails/sprockets/blob/cddf9fb841eece80276a1ccaee1e018a356547a0/lib/sprockets/utils.rb#L100 , and there is a related fixed. rails/sprockets#515

But looks like this fix not work for js bundle from external.

I don't know the internal of asset pipeline very well. How can I make the sourcemap from esbuild work?

Separate builds for production and development/test

e.g. I would like to have:

esbuild app/javascript/*.* --bundle --outdir=app/assets/builds '--define:process.env.NODE_ENV=\"production\"' --minify

for production. (this is necessary to have the production build of react that is like 3-5x smaller)

And:

esbuild app/javascript/*.* --bundle --outdir=app/assets/builds

for development/test.

seems like there should be yarn build:production hooked into rake assets:prepare instead of yarn build

Install task fails if test:prepare task does not exist

If a Rails app does not have test:prepare defined, the install task fails with:

rails aborted!
Don't know how to build task 'test:prepare' (See the list of available tasks with `rails --tasks`)
Did you mean?  db:test:prepare

The most common path to get into this state is likely generating a new Rails app with -T to skip the default test framework (perhaps in favor of rspec or another alternative)

rails new no_tests -T
cd no_tests 
bundle add jsbundling-rails
rails javascript:install:esbuild #errors out

The same issue exists on cssbundling-rails

A simple fix is something like:

# lib/tasks/jsbundling/build.rake
Rake::Task["test:prepare"].enhance(["javascript:build"]) if Rake::Task.task_defined?("test:prepare")

Happy to open a PR to resolve this error and on cssbundling-rails, if this is something that you would like to address.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.