0

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?

Thomas
  • 2,622
  • 1
  • 9
  • 16

1 Answers1

0

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)
})
Thomas
  • 2,622
  • 1
  • 9
  • 16