Skip to content

Commit 34868c1

Browse files
committed
Properly building StimulusBundle assets
1 parent 99c7962 commit 34868c1

File tree

8 files changed

+162
-131
lines changed

8 files changed

+162
-131
lines changed

rollup.config.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ const moveTypescriptDeclarationsPlugin = (packagePath) => ({
6262
});
6363

6464
const files = glob.sync('src/*/assets/src/*controller.ts');
65+
// custom handling for StimulusBundle
66+
files.push('src/StimulusBundle/assets/src/loader.ts');
6567
module.exports = files.map((file) => {
6668
const packageRoot = path.join(file, '..', '..');
6769
const packagePath = path.join(packageRoot, 'package.json');
@@ -71,6 +73,11 @@ module.exports = files.map((file) => {
7173
...(packageData.peerDependencies ? Object.keys(packageData.peerDependencies) : [])
7274
];
7375

76+
// custom handling for StimulusBundle
77+
if (file.includes('StimulusBundle')) {
78+
peerDependencies.push('./controllers.js');
79+
}
80+
7481
return {
7582
input: file,
7683
output: {

src/StimulusBundle/assets/controllers.ts

Lines changed: 0 additions & 9 deletions
This file was deleted.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { ControllerConstructor } from '@hotwired/stimulus';
2+
export declare const eagerControllers: {
3+
[key: string]: ControllerConstructor;
4+
};
5+
export declare const lazyControllers: {
6+
[key: string]: () => Promise<{
7+
default: ControllerConstructor;
8+
}>;
9+
};
10+
export declare const isApplicationDebug = false;
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { Application } from '@hotwired/stimulus';
2+
export declare const loadControllers: (application: Application) => void;
3+
export declare const startStimulusApp: () => Application;
Lines changed: 35 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,80 +1,73 @@
1-
/**
2-
* Starts the Stimulus application and reads a map dump in the DOM to load controllers.
3-
*
4-
* Inspired by stimulus-loading.js from stimulus-rails.
5-
*/
61
import { Application } from '@hotwired/stimulus';
7-
import { eagerControllers, lazyControllers, isApplicationDebug } from './controllers.js';
2+
import { eagerControllers, isApplicationDebug, lazyControllers } from './controllers.js';
83

94
const controllerAttribute = 'data-controller';
10-
11-
export const loadControllers = (application) => {
12-
// loop over the controllers map and require each controller
5+
const loadControllers = (application) => {
136
for (const name in eagerControllers) {
147
registerController(name, eagerControllers[name], application);
158
}
16-
179
loadLazyControllers(application);
1810
};
19-
20-
export const startStimulusApp = () => {
11+
const startStimulusApp = () => {
2112
const application = Application.start();
2213
application.debug = isApplicationDebug;
23-
2414
loadControllers(application);
25-
2615
return application;
2716
};
28-
2917
function registerController(name, controller, application) {
3018
if (canRegisterController(name, application)) {
31-
application.register(name, controller)
19+
application.register(name, controller);
3220
}
3321
}
34-
3522
function loadLazyControllers(application) {
36-
lazyLoadExistingControllers(application, document);
37-
lazyLoadNewControllers(application, document)
23+
lazyLoadExistingControllers(application, document.documentElement);
24+
lazyLoadNewControllers(application, document.documentElement);
3825
}
39-
4026
function lazyLoadExistingControllers(application, element) {
41-
queryControllerNamesWithin(element).forEach(controllerName => loadController(controllerName, application))
27+
queryControllerNamesWithin(element).forEach(controllerName => loadController(controllerName, application));
4228
}
4329
function queryControllerNamesWithin(element) {
44-
return Array.from(element.querySelectorAll(`[${controllerAttribute}]`)).map(extractControllerNamesFrom).flat()
30+
return Array.from(element.querySelectorAll(`[${controllerAttribute}]`)).map(extractControllerNamesFrom).flat();
4531
}
4632
function extractControllerNamesFrom(element) {
47-
return element.getAttribute(controllerAttribute).split(/\s+/).filter(content => content.length)
33+
const controllerNameValue = element.getAttribute(controllerAttribute);
34+
if (!controllerNameValue) {
35+
return [];
36+
}
37+
return controllerNameValue.split(/\s+/).filter(content => content.length);
4838
}
4939
function lazyLoadNewControllers(application, element) {
50-
new MutationObserver((mutationsList) => {
51-
for (const { attributeName, target, type } of mutationsList) {
52-
switch (type) {
53-
case 'attributes': {
54-
if (attributeName === controllerAttribute && target.getAttribute(controllerAttribute)) {
55-
extractControllerNamesFrom(target).forEach(controllerName => loadController(controllerName, application))
56-
}
57-
}
58-
59-
case 'childList': {
60-
lazyLoadExistingControllers(application, target)
40+
new MutationObserver((mutationsList) => {
41+
for (const { attributeName, target, type } of mutationsList) {
42+
switch (type) {
43+
case 'attributes': {
44+
if (attributeName === controllerAttribute && target.getAttribute(controllerAttribute)) {
45+
extractControllerNamesFrom(target).forEach(controllerName => loadController(controllerName, application));
46+
}
47+
break;
48+
}
49+
case 'childList': {
50+
lazyLoadExistingControllers(application, target);
51+
}
52+
}
6153
}
62-
}
63-
}
64-
}).observe(element, { attributeFilter: [controllerAttribute], subtree: true, childList: true })
54+
}).observe(element, {
55+
attributeFilter: [controllerAttribute],
56+
subtree: true,
57+
childList: true,
58+
});
6559
}
66-
function canRegisterController(name, application){
67-
return !application.router.modulesByIdentifier.has(name)
60+
function canRegisterController(name, application) {
61+
return !application.router.modulesByIdentifier.has(name);
6862
}
69-
7063
async function loadController(name, application) {
7164
if (canRegisterController(name, application)) {
7265
if (lazyControllers[name] === undefined) {
7366
console.error(`Failed to autoload controller: ${name}`);
7467
}
75-
7668
const controllerModule = await (lazyControllers[name]());
77-
7869
registerController(name, controllerModule.default, application);
7970
}
8071
}
72+
73+
export { loadControllers, startStimulusApp };

src/StimulusBundle/assets/loader.ts

Lines changed: 0 additions & 80 deletions
This file was deleted.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// This file is dynamically rewritten by StimulusBundle + AssetMapper.
2+
import { ControllerConstructor } from '@hotwired/stimulus';
3+
4+
export const eagerControllers: { [key: string]: ControllerConstructor } = {};
5+
export const lazyControllers: { [key: string]: () => Promise<{ default: ControllerConstructor }> } = {};
6+
export const isApplicationDebug = false;
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
/**
2+
* Starts the Stimulus application and reads a map dump in the DOM to load controllers.
3+
*
4+
* Inspired by stimulus-loading.js from stimulus-rails.
5+
*/
6+
import { Application, ControllerConstructor } from '@hotwired/stimulus';
7+
import {
8+
eagerControllers,
9+
lazyControllers,
10+
isApplicationDebug,
11+
} from './controllers.js';
12+
13+
const controllerAttribute = 'data-controller';
14+
15+
export const loadControllers = (application: Application) => {
16+
// loop over the controllers map and require each controller
17+
for (const name in eagerControllers) {
18+
registerController(name, eagerControllers[name], application);
19+
}
20+
21+
loadLazyControllers(application);
22+
};
23+
24+
export const startStimulusApp = () => {
25+
const application = Application.start();
26+
application.debug = isApplicationDebug;
27+
28+
loadControllers(application);
29+
30+
return application;
31+
};
32+
33+
function registerController(name: string, controller: ControllerConstructor, application: Application) {
34+
if (canRegisterController(name, application)) {
35+
application.register(name, controller);
36+
}
37+
}
38+
39+
function loadLazyControllers(application: Application) {
40+
lazyLoadExistingControllers(application, document.documentElement);
41+
lazyLoadNewControllers(application, document.documentElement);
42+
}
43+
44+
function lazyLoadExistingControllers(application: Application, element: Element) {
45+
queryControllerNamesWithin(element).forEach(controllerName => loadController(controllerName, application));
46+
}
47+
48+
function queryControllerNamesWithin(element: Element) {
49+
return Array.from(element.querySelectorAll(`[${controllerAttribute}]`)).map(extractControllerNamesFrom).flat();
50+
}
51+
52+
function extractControllerNamesFrom(element: Element): string[] {
53+
const controllerNameValue = element.getAttribute(controllerAttribute);
54+
55+
if (!controllerNameValue) {
56+
return [];
57+
}
58+
59+
return controllerNameValue.split(/\s+/).filter(content => content.length);
60+
}
61+
62+
function lazyLoadNewControllers(application: Application, element: Element) {
63+
new MutationObserver((mutationsList) => {
64+
for (const { attributeName, target, type } of mutationsList) {
65+
switch (type) {
66+
case 'attributes': {
67+
if (attributeName === controllerAttribute && (target as Element).getAttribute(controllerAttribute)) {
68+
extractControllerNamesFrom(target as Element).forEach(controllerName => loadController(controllerName, application));
69+
}
70+
71+
break;
72+
}
73+
74+
case 'childList': {
75+
lazyLoadExistingControllers(application, target as Element);
76+
}
77+
}
78+
}
79+
}).observe(element, {
80+
attributeFilter: [controllerAttribute],
81+
subtree: true,
82+
childList: true,
83+
});
84+
}
85+
86+
function canRegisterController(name: string, application: Application) {
87+
// @ts-ignore
88+
return !application.router.modulesByIdentifier.has(name);
89+
}
90+
91+
async function loadController(name: string, application: Application) {
92+
if (canRegisterController(name, application)) {
93+
if (lazyControllers[name] === undefined) {
94+
console.error(`Failed to autoload controller: ${name}`);
95+
}
96+
97+
const controllerModule = await (lazyControllers[name]());
98+
99+
registerController(name, controllerModule.default, application);
100+
}
101+
}

0 commit comments

Comments
 (0)