Webpacker: Add app/javascript/initializers and require all files by default in packs/application.js

Created on 23 Jan 2018  路  5Comments  路  Source: rails/webpacker

Lots of frameworks need a generic initialization before they're ready for use. This goes for Action Cable, Active Storage, Rails UJS, and Turbolinks, to name a few. To be able to programmatically add these initializers via gems or other means, it'd be nicer if the initialization happened in individual files, and all those files are just required by pack/application.js. Just like we do with config/initializers on the Ruby side.

For example:

// app/javascript/initializers/actioncable.js
import ActionCable from 'actioncable'
window.Cable = ActionCable.createConsumer()

// app/javascript/initializers/activestorage.js
import * as ActiveStorage from 'activestorage'
ActiveStorage.start()

// app/javascript/initializers/rails_ujs.js
import Rails from 'rails-ujs'
Rails.start()

// app/javascript/initializers/turbolinks.js
import Turbolinks from 'turbolinks'
Turbolinks.start()

// app/javascript/packs/application.js
// Code that requires all the files in app/javascript/initializers/*.js

Most helpful comment

I've listed four initializations that need to happen for default Rails frameworks. It's surely helpful just for those alone. This wouldn't be buried somewhere, simply a convention to kick things off with. So the files live in the initializer directory and there's an explicit call in the default application.js pack to include them.

If you have a framework or library that doesn't need initialization, you don't add an initializer. If you need more control over initialization timing, you just do it directly in the application.js file.

The main purpose is to give libraries and frameworks a way to work with a default Rails setup without resorting to munging a file. We munge a lot of files right now and it's a brittle practice.

All 5 comments

In my experience, more and more javascript projects are going away from initializing / requiring modules automatically (this may be overcompensation for having to deal with "everything is global" during the jquery years).
Also, there are a lot of use cases where more control is needed, e.g. in my current project we only initialize action cable after login, so the action cable initializer would need to be disabled.

I'm not strictly against this, just want to point out that it is contraire to what I have seen in recent javascript(-only) projects.
I think it must be taken into consideration how to change/disable a single initializer. The way it is implemented in #1209 all initializers can be disabled by removing the import '../initializers'; statement.

@dhh May be we can document this instead? Like @p0wl pointed out there are so many different ways initialization can happen in an es6 project since with webpack, one can simply import an initializer and use it anywhere instead of using it with a global window object.

For example: (this is an action cable client singleton)

export const channels = {
  SomeSubscription: 'SomeChannel',
  AnotherSubscription: 'AnotherChannel'
}

class ActionCableClient {
  static instance

  url = () => '/cable'
  consumer = ActionCable.createConsumer()
  subscriptions = Map()

  constructor () {
    if (ActionCableClient.instance) return ActionCableClient.instance

    this.consumer = ActionCable.createConsumer(this.url())
    ActionCableClient.instance = this
  }

  reconnect = () => (this.consumer = ActionCable.createConsumer(this.url()))

 // ..... more functions
}

I am not sure if this would be as helpful as we are thinking it to be. What do you think?

But anyway I guess this still might be useful as a starting point so everything works out of the box.

I've listed four initializations that need to happen for default Rails frameworks. It's surely helpful just for those alone. This wouldn't be buried somewhere, simply a convention to kick things off with. So the files live in the initializer directory and there's an explicit call in the default application.js pack to include them.

If you have a framework or library that doesn't need initialization, you don't add an initializer. If you need more control over initialization timing, you just do it directly in the application.js file.

The main purpose is to give libraries and frameworks a way to work with a default Rails setup without resorting to munging a file. We munge a lot of files right now and it's a brittle practice.

Merge this puppy in.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

ijdickinson picture ijdickinson  路  3Comments

iChip picture iChip  路  3Comments

suhomozgy-andrey picture suhomozgy-andrey  路  3Comments

amandapouget picture amandapouget  路  3Comments

pioz picture pioz  路  3Comments