Skip to content

Instantly share code, notes, and snippets.

@frullah
Last active October 24, 2021 10:07
Show Gist options
  • Save frullah/483a8126bc69d4f4f348f73c6570a3d6 to your computer and use it in GitHub Desktop.
Save frullah/483a8126bc69d4f4f348f73c6570a3d6 to your computer and use it in GitHub Desktop.
vite stimulus lazy loader
// this code is used for lazy loading stimulus controller with Vite
// Vite lazy import only works with relative path
// so place this code in stimulus controllers folder
// reference: https://github.com/vitejs/vite/issues/1931#issuecomment-868310388
// this code is originally from https://github.com/hotwired/stimulus-rails/blob/49cfc34beb92ad56140a78db6be4c7733207be30/app/assets/javascripts/stimulus-loading.js
const controllerAttribute = "data-controller"
const registeredControllers = {}
// Lazy load controllers registered beneath the `under` path in the import map to the passed application instance.
export function lazyLoadControllersFrom(application, element = document) {
lazyLoadExistingControllers(application, element)
lazyLoadNewControllers(application, element)
}
function lazyLoadExistingControllers(application, element) {
queryControllerNamesWithin(element)
.forEach(controllerName => loadController(controllerName, application))
}
function lazyLoadNewControllers(application, element) {
new MutationObserver((mutationsList) => {
for (const { attributeName, target, type } of mutationsList) {
switch (type) {
case "attributes": {
if (target.getAttribute(controllerAttribute)) {
extractControllerNamesFrom(target)
.forEach(controllerName => loadController(controllerName, application))
}
}
case "childList": {
lazyLoadExistingControllers(application, target)
}
}
}
}).observe(element, {
attributeFilter: [controllerAttribute],
subtree: true,
childList: true
})
}
function queryControllerNamesWithin(element) {
return Array
.from(element.querySelectorAll(`[${controllerAttribute}]`))
.map(extractControllerNamesFrom)
.flat()
}
function extractControllerNamesFrom(element) {
return element
.getAttribute(controllerAttribute)
.split(/\s+/)
.filter(content => content.length)
}
function loadController(name, application) {
import(`./${controllerFilename(name)}_controller.js`)
.then(module => registerController(name, module, application))
}
function controllerFilename(name) {
return name.replace(/--/g, "/").replace(/-/g, "_")
}
// register controller only once
function registerController(name, module, application) {
if (name in registeredControllers) return
application.register(name, module.default)
registeredControllers[name] = true
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment