How do I import Javascript with directories?
I've set up jsbundling-rails
using the esbuild
bundler for a Rails 7 project that was previously using sprockets to handle all assets. It appears that I've set it up correctly, because I can load JS files by directly referencing the file.
Though I've got a lot of files and I even want to impose some human logic on the structure of the javascript.
To do this, I want to split up some javascript in directories, like this:
app
- assets
-- javascript
--- some.js
--- some_more.js
---- base
----- some_other.js
----- some_way_other.js
I'm able to load all of this js with my application.js
at app/assets/javascript/application.js
file, like this:
import "./some"
import "./some_more"
import "./base/some_other"
import "./base/some_way_other"
the console log responds with:
but I want to be able to load/bundle this JS using this syntax:
import "./some"
import "./some_more"
import "./base"
but from the js watcher, I get:
15:39:22 js.1 | [watch] build started (change: "app/javascript/application.js")
15:39:22 js.1 | ✘ [ERROR] Could not resolve "./base"
15:39:22 js.1 |
15:39:22 js.1 | app/javascript/application.js:14:7:
15:39:22 js.1 | 14 │ import "./base"
15:39:22 js.1 | ╵ ~~~~~~~~
15:39:22 js.1 |
15:39:22 js.1 | 1 error
15:39:22 js.1 | [watch] build finished
My Procfile.dev
web: unset PORT && /root/.rbenv/shims/thin start -C config/thin_development.yml
js: yarn build --watch
I mean, is this even possible? I used to be able to do this with sprockets, but it doesn't seem like I can with esbuild. Or should I just be sticking with a massive application.js file and manually import each javascript file?
The way to do this is to customize esbuild with a configuration file known as esbuild.config.js
and to utilize the plugin esbuild-rails
created by Chris at GoRails, found at https://github.com/excid3/esbuild-rails.
esbuild.config.js
const path = require('path');
const rails = require('esbuild-rails')
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: ["application.js"],
bundle: true,
minify: true,
outdir: path.join(process.cwd(), "app/assets/builds"),
absWorkingDir: path.join(process.cwd(), "app/javascript"),
watch: watch,
write: true,
loader: { '.js': 'jsx' },
publicPath: 'assets',
target: 'es6',
// custom plugins will be inserted is this array
plugins: [rails()],
}).catch(() => process.exit(1));
Usage in application.js
import { Application } from "@hotwired/stimulus"
const application = Application.start()
import './meta/jquery'
import './meta/ajax'
import './controllers'
import libraries from "./libraries/*.js"
import custom_files from "./custom/*.js"
libraries.forEach((controller) => {
application.register(controller.name, controller.module.default)
})
custom_files.forEach((controller) => {
application.register(controller.name, controller.module.default)
})