paulca / paulca.com Public

Clone
git clone https://amadan.net/paulca/paulca.com

Plain git — no account needed to clone.

1// FIXME: es-module-shim won't shim the dynamic import without this explicit import
2import "@hotwired/stimulus"
3
4const controllerAttribute = "data-controller"
5
6// Eager load all controllers registered beneath the `under` path in the import map to the passed application instance.
7export function eagerLoadControllersFrom(under, application) {
8 const paths = Object.keys(parseImportmapJson()).filter(path => path.match(new RegExp(`^${under}/.*_controller$`)))
9 paths.forEach(path => registerControllerFromPath(path, under, application))
10}
11
12function parseImportmapJson() {
13 return JSON.parse(document.querySelector("script[type=importmap]").text).imports
14}
15
16function registerControllerFromPath(path, under, application) {
17 const name = path
18 .replace(new RegExp(`^${under}/`), "")
19 .replace("_controller", "")
20 .replace(/\//g, "--")
21 .replace(/_/g, "-")
22
23 if (canRegisterController(name, application)) {
24 import(path)
25 .then(module => registerController(name, module, application))
26 .catch(error => console.error(`Failed to register controller: ${name} (${path})`, error))
27 }
28}
29
30
31// Lazy load controllers registered beneath the `under` path in the import map to the passed application instance.
32export function lazyLoadControllersFrom(under, application, element = document) {
33 lazyLoadExistingControllers(under, application, element)
34 lazyLoadNewControllers(under, application, element)
35}
36
37function lazyLoadExistingControllers(under, application, element) {
38 queryControllerNamesWithin(element).forEach(controllerName => loadController(controllerName, under, application))
39}
40
41function lazyLoadNewControllers(under, application, element) {
42 new MutationObserver((mutationsList) => {
43 for (const { attributeName, target, type } of mutationsList) {
44 switch (type) {
45 case "attributes": {
46 if (attributeName == controllerAttribute && target.getAttribute(controllerAttribute)) {
47 extractControllerNamesFrom(target).forEach(controllerName => loadController(controllerName, under, application))
48 }
49 }
50
51 case "childList": {
52 lazyLoadExistingControllers(under, application, target)
53 }
54 }
55 }
56 }).observe(element, { attributeFilter: [controllerAttribute], subtree: true, childList: true })
57}
58
59function queryControllerNamesWithin(element) {
60 return Array.from(element.querySelectorAll(`[${controllerAttribute}]`)).map(extractControllerNamesFrom).flat()
61}
62
63function extractControllerNamesFrom(element) {
64 return element.getAttribute(controllerAttribute).split(/\s+/).filter(content => content.length)
65}
66
67function loadController(name, under, application) {
68 if (canRegisterController(name, application)) {
69 import(controllerFilename(name, under))
70 .then(module => registerController(name, module, application))
71 .catch(error => console.error(`Failed to autoload controller: ${name}`, error))
72 }
73}
74
75function controllerFilename(name, under) {
76 return `${under}/${name.replace(/--/g, "/").replace(/-/g, "_")}_controller`
77}
78
79function registerController(name, module, application) {
80 if (canRegisterController(name, application)) {
81 application.register(name, module.default)
82 }
83}
84
85function canRegisterController(name, application){
86 return !application.router.modulesByIdentifier.has(name)
87}
88