1import { Application } from '@hotwired/stimulus'; 2import { isApplicationDebug, eagerControllers, lazyControllers } from './controllers.js'; 3 4const controllerAttribute = 'data-controller'; 5const loadControllers = (application, eagerControllers, lazyControllers) => { 6 for (const name in eagerControllers) { 7 registerController(name, eagerControllers[name], application); 8 } 9 const lazyControllerHandler = new StimulusLazyControllerHandler(application, lazyControllers); 10 lazyControllerHandler.start(); 11}; 12const startStimulusApp = () => { 13 const application = Application.start(); 14 application.debug = isApplicationDebug; 15 loadControllers(application, eagerControllers, lazyControllers); 16 return application; 17}; 18class StimulusLazyControllerHandler { 19 constructor(application, lazyControllers) { 20 this.application = application; 21 this.lazyControllers = lazyControllers; 22 } 23 start() { 24 this.lazyLoadExistingControllers(document.documentElement); 25 this.lazyLoadNewControllers(document.documentElement); 26 } 27 lazyLoadExistingControllers(element) { 28 Array.from(element.querySelectorAll(`[${controllerAttribute}]`)) 29 .flatMap(extractControllerNamesFrom) 30 .forEach((controllerName) => this.loadLazyController(controllerName)); 31 } 32 loadLazyController(name) { 33 if (!this.lazyControllers[name]) { 34 return; 35 } 36 const controllerLoader = this.lazyControllers[name]; 37 delete this.lazyControllers[name]; 38 if (!canRegisterController(name, this.application)) { 39 return; 40 } 41 this.application.logDebugActivity(name, 'lazy:loading'); 42 controllerLoader() 43 .then((controllerModule) => { 44 this.application.logDebugActivity(name, 'lazy:loaded'); 45 registerController(name, controllerModule.default, this.application); 46 }) 47 .catch((error) => { 48 console.error(`Error loading controller "${name}":`, error); 49 }); 50 } 51 lazyLoadNewControllers(element) { 52 if (Object.keys(this.lazyControllers).length === 0) { 53 return; 54 } 55 new MutationObserver((mutationsList) => { 56 for (const { attributeName, target, type } of mutationsList) { 57 switch (type) { 58 case 'attributes': { 59 if (attributeName === controllerAttribute && 60 target.getAttribute(controllerAttribute)) { 61 extractControllerNamesFrom(target).forEach((controllerName) => this.loadLazyController(controllerName)); 62 } 63 break; 64 } 65 case 'childList': { 66 this.lazyLoadExistingControllers(target); 67 } 68 } 69 } 70 }).observe(element, { 71 attributeFilter: [controllerAttribute], 72 subtree: true, 73 childList: true, 74 }); 75 } 76} 77function registerController(name, controller, application) { 78 if (canRegisterController(name, application)) { 79 application.register(name, controller); 80 } 81} 82function extractControllerNamesFrom(element) { 83 const controllerNameValue = element.getAttribute(controllerAttribute); 84 if (!controllerNameValue) { 85 return []; 86 } 87 return controllerNameValue.split(/\s+/).filter((content) => content.length); 88} 89function canRegisterController(name, application) { 90 return !application.router.modulesByIdentifier.has(name); 91} 92 93export { loadControllers, startStimulusApp };
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.