
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
stimulus-controller-resolver
Advanced tools
Resolve your Stimulus Controllers any way you want – even lazy!
If you have a lot of Stimulus Controllers that import other modules, the size can really start to add up. (I have some Controllers that mount Svelte components!) Wouldn't it be great if you could lazy load some of your Controllers?
This package allows you to supply a custom (async!) resolver function for your controllers. It is supposed to provide an alternative to putting all your controllers into your main bundle.
npm i stimulus-controller-resolver
To load all your Controllers lazily:
import { Application } from '@hotwired/stimulus'
import StimulusControllerResolver from 'stimulus-controller-resolver'
const application = Application.start()
StimulusControllerResolver.install(application, async controllerName => (
(await import(`./controllers/${controllerName}-controller.js`)).default
))
Depending on your configuration, your bundler should then split out all the files in the controllers-folder into seperate chunks.
If you want to preload some controllers but still load all the other ones lazily, you can use the good old application.register:
import ImportantController from './controllers/important-controller.js'
import CrucialController from './controllers/crucial-controller.js'
application.register('important-controller', ImportantController)
application.register('crucial-controller', CrucialController)
StimulusControllerResolver.install(application, async controllerName => (
(await import(`./controllers/${controllerName}-controller.js`)).default
))
If you're using Vite, you can make use of Vite's Glob Import. This package exports an additional function called createViteGlobResolver that handles this for you. Pass it one or more globs:
import { Application } from "@hotwired/stimulus"
import StimulusControllerResolver, { createViteGlobResolver } from 'stimulus-controller-resolver'
const application = Application.start()
StimulusControllerResolver.install(application, createViteGlobResolver(
import.meta.glob('../controllers/*-controller.js'),
import.meta.glob('../../components/**/*-controller.js')
))
By default, the filenames will be transformed according to the Stimulus Identifier Rules, starting from the controllers or components folders:
| Path | Stimulus Identifier |
|---|---|
| ../controllers/stickiness-controller.js | stickiness |
| ../../components/deep_dir/slider-controller.js | deep-dir--slider |
If process.env.NODE_ENV === 'development', it also prints a helpful message if you request a controller that is not available:
Stimulus Controller Resolver can't resolve "missing". Available: ['this-one', 'and-this-one']
If you need more flexibility, you can provide your own Regex to extract the identifiers from your paths. The first match will be transformed according to the aforementioned Stimulus Identifier Rules.
StimulusControllerResolver.install(application, createViteGlobResolver([{
glob: import.meta.glob('../sprinkles/**/*_controller.js'),
regex: /^.+sprinkles\/(.+?)_controller.js$/,
}]))
If you still need more flexibility, you can provide your own toIdentifier function. For example, if you wanted ../cards/album/stimulus_controller.js to be available as album-card, you could do:
StimulusControllerResolver.install(application, createViteGlobResolver({
glob: import.meta.glob('../cards/*/stimulus_controller.js'),
toIdentifier(key) {
return key.split("cards/")[1].split("/stimulus_controller")[0] + "-card"
},
}))
Combine the different ways however you need!
StimulusControllerResolver.install(application, createViteGlobResolver(
import.meta.glob('../controllers/*-controller.js'),
{
glob: import.meta.glob('../shop/components/*/stimulus_controller.js'),
regex: /example/
},
{
glob: import.meta.glob('../shop/components/*/stimulus_controller.js'),
toIdentifier(key) { return key.toLowerCase() }
},
))
[!TIP] If after all this, you need even more flexibility, you can always implement your completely own custom resolver function, as described above.
StimulusControllerResolver.install(application, resolverFn)
application: This is your instance of Stimulus.Application.resolverFn(controllerName): Controller: A function that receives the name of the controller (that's the part you write in data-controller="this-is-the-name") and returns the Controller class you want to use for this controllerName. This will only be called the first time each controllerName is encountered.install() returns an instance of StimulusControllerResolver, on which you can call:
instance.stop() // to stop getting new controller definitions
// and
instance.start() // to start again
install() will automatically call start(), so most of the time you shouldn't have to do anything.
FAQs
Resolve your Stimulus Controllers any way you want – even lazy!
We found that stimulus-controller-resolver demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.