@csedl/svelte-on-rails 0.0.3 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -10
- package/index.js +23 -22
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -15,37 +15,42 @@ npm i @csedl/svelte-on-rails
|
|
|
15
15
|
A file like `initializeSvelte.js` that is imported into your entrypoint file
|
|
16
16
|
|
|
17
17
|
```javascript
|
|
18
|
-
|
|
19
|
-
const componentsRoot = '/javascript'
|
|
18
|
+
import { initializeSvelteComponents, cleanupSvelteComponents } from 'svelte-on-rails';
|
|
20
19
|
|
|
21
|
-
|
|
20
|
+
const components = import.meta.glob('/javascript/**/*.svelte', { eager: true });
|
|
21
|
+
const componentsRoot = '/javascript';
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
initializeSvelteComponents(componentsRoot, components, true)
|
|
23
|
+
// Initialize Svelte components
|
|
24
|
+
initializeSvelteComponents(componentsRoot, components, true);
|
|
25
25
|
|
|
26
|
-
// Turbo
|
|
26
|
+
// Turbo event listener for page load
|
|
27
27
|
document.addEventListener('turbo:load', () => {
|
|
28
|
-
|
|
28
|
+
initializeSvelteComponents(componentsRoot, components, true);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
// Turbo event listener for cleanup before page cache
|
|
32
|
+
document.addEventListener('turbo:before-cache', () => {
|
|
33
|
+
cleanupSvelteComponents(true);
|
|
29
34
|
});
|
|
30
35
|
```
|
|
31
36
|
|
|
37
|
+
=> the latter boolean is for debugging logs to the console
|
|
38
|
+
|
|
32
39
|
## How it works
|
|
33
40
|
|
|
34
41
|
The helper from the `svelte-on-rails` gem adds attributes like
|
|
35
42
|
|
|
36
43
|
- class `svelte-on-rails-not-initialized-component`
|
|
37
44
|
- attribute `data-svelte-component="HelloWorld"`
|
|
38
|
-
- attribute `data-svelte-on-rails-initialize-action="hydrate"` if the element is SSR, otherwise `mount`
|
|
39
45
|
- attribute `data-props={items:['one','two','three']}`
|
|
40
46
|
|
|
41
47
|
to the wrapping element. Then, this lib
|
|
42
48
|
|
|
43
49
|
- picks up these elements
|
|
44
50
|
- picks the props, parses them by `JSON.parse(..)` and provides them to the element as svelte props
|
|
45
|
-
-
|
|
51
|
+
- If the element is empty, it mountes, otherwise it hydrates the element
|
|
46
52
|
- adds the attribute `data-svelte-initialized='true'`
|
|
47
53
|
- removes the class `svelte-on-rails-not-initialized-component`
|
|
48
|
-
- removes the attribute `data-svelte-on-rails-initialize-action`
|
|
49
54
|
|
|
50
55
|
## Requirements
|
|
51
56
|
|
package/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {mount, hydrate, unmount} from 'svelte';
|
|
2
2
|
|
|
3
3
|
// Store for tracking initialized Svelte component instances
|
|
4
4
|
const svelteInstances = new WeakMap();
|
|
@@ -10,24 +10,20 @@ const svelteInstances = new WeakMap();
|
|
|
10
10
|
* @param {boolean} debug - Enable debug logging
|
|
11
11
|
*/
|
|
12
12
|
export function initializeSvelteComponents(componentsRoot, components, debug = false) {
|
|
13
|
-
|
|
14
13
|
const virginClass = 'svelte-on-rails-not-initialized-component';
|
|
15
14
|
const virginElements = document.getElementsByClassName(virginClass);
|
|
16
|
-
debugLog(`Found ${virginElements.length} elements with class
|
|
15
|
+
debugLog(`Found ${virginElements.length} elements with class "${virginClass}"`, debug);
|
|
17
16
|
if (virginElements.length === 0) return;
|
|
18
17
|
|
|
19
18
|
// Convert live HTMLCollection to a static array
|
|
20
19
|
const elementsArray = Array.from(virginElements);
|
|
21
20
|
|
|
22
21
|
// Iterate over the static array
|
|
23
|
-
|
|
24
22
|
for (const element of elementsArray) {
|
|
25
|
-
|
|
26
|
-
const action = element.getAttribute('data-svelte-on-rails-initialize-action');
|
|
23
|
+
const action = element.innerHTML ? 'hydrate' : 'mount';
|
|
27
24
|
const componentName = element.getAttribute('data-svelte-component');
|
|
28
25
|
|
|
29
26
|
// Check component
|
|
30
|
-
|
|
31
27
|
const componentPath = `${componentsRoot}/${componentName}.svelte`.replace(/\/+/g, '/');
|
|
32
28
|
if (!components[componentPath]) {
|
|
33
29
|
console.error(`[initializeSvelteComponents] Component not found for path: ${componentPath}`);
|
|
@@ -36,7 +32,6 @@ export function initializeSvelteComponents(componentsRoot, components, debug = f
|
|
|
36
32
|
const component = components[componentPath].default;
|
|
37
33
|
|
|
38
34
|
// Parse props
|
|
39
|
-
|
|
40
35
|
let props = {};
|
|
41
36
|
const attrKey = 'data-props';
|
|
42
37
|
const propsString = element.getAttribute(attrKey);
|
|
@@ -56,33 +51,32 @@ export function initializeSvelteComponents(componentsRoot, components, debug = f
|
|
|
56
51
|
props,
|
|
57
52
|
hydrate: false,
|
|
58
53
|
});
|
|
59
|
-
debugLog2(`Mounted successfully ${componentName}, parsed props:`, props, debug);
|
|
54
|
+
debugLog2(`Mounted successfully ${componentName}, with parsed props:`, props, debug);
|
|
60
55
|
} else if (action === 'hydrate') {
|
|
61
56
|
instance = hydrate(component, {
|
|
62
57
|
target: element,
|
|
63
58
|
hydrate: true,
|
|
64
59
|
props: props,
|
|
65
60
|
});
|
|
66
|
-
debugLog2(`Hydrated successfully ${componentName}, parsed props:`, debug);
|
|
61
|
+
debugLog2(`Hydrated successfully ${componentName}, with parsed props:`, props, debug);
|
|
67
62
|
} else {
|
|
68
63
|
console.error(`[initializeSvelteComponents] Unknown action: "${action}" for component ${componentName}`);
|
|
69
64
|
continue; // Proceed to the next element
|
|
70
65
|
}
|
|
71
66
|
|
|
72
|
-
// Store
|
|
73
|
-
|
|
67
|
+
// Store instance and set attributes only after successful mount/hydrate
|
|
74
68
|
svelteInstances.set(element, instance);
|
|
75
|
-
|
|
76
69
|
element.classList.remove(virginClass);
|
|
77
|
-
element.setAttribute('data-svelte-initialized', true);
|
|
78
|
-
element.removeAttribute('data-svelte-on-rails-initialize-action');
|
|
79
|
-
|
|
70
|
+
element.setAttribute('data-svelte-initialized', 'true');
|
|
80
71
|
} catch (e) {
|
|
81
72
|
console.error(`[initializeSvelteComponents] Error ${action} ${componentName}:`, e);
|
|
82
73
|
}
|
|
83
74
|
}
|
|
84
75
|
}
|
|
85
76
|
|
|
77
|
+
/**
|
|
78
|
+
* Cleans up all initialized Svelte component instances.
|
|
79
|
+
*/
|
|
86
80
|
/**
|
|
87
81
|
* Cleans up all initialized Svelte component instances.
|
|
88
82
|
*/
|
|
@@ -93,16 +87,24 @@ export function cleanupSvelteComponents(debug = false) {
|
|
|
93
87
|
|
|
94
88
|
for (const element of initializedElements) {
|
|
95
89
|
try {
|
|
96
|
-
//
|
|
97
|
-
|
|
98
|
-
|
|
90
|
+
// Check if an instance exists for the element
|
|
91
|
+
const instance = svelteInstances.get(element);
|
|
92
|
+
if (instance) {
|
|
93
|
+
// In Svelte 5, use unmount to destroy the instance
|
|
94
|
+
unmount(instance);
|
|
95
|
+
debugLog2(`Unmounted Svelte component for element:`, element, debug);
|
|
96
|
+
} else {
|
|
97
|
+
debugLog(`No instance found for element, skipping unmount:`, element, debug);
|
|
98
|
+
}
|
|
99
99
|
} catch (e) {
|
|
100
|
-
|
|
100
|
+
if (debug) {
|
|
101
|
+
console.error(`[cleanupSvelteComponents] Error unmounting component for element:`, e);
|
|
102
|
+
}
|
|
101
103
|
// Fallback: Clear the element's content to ensure cleanup
|
|
102
104
|
element.innerHTML = '';
|
|
103
105
|
}
|
|
104
106
|
|
|
105
|
-
// Remove the instance from storage
|
|
107
|
+
// Remove the instance from storage and clean up attributes
|
|
106
108
|
svelteInstances.delete(element);
|
|
107
109
|
element.removeAttribute('data-svelte-initialized');
|
|
108
110
|
}
|
|
@@ -114,7 +116,6 @@ function debugLog(msg, debug) {
|
|
|
114
116
|
}
|
|
115
117
|
}
|
|
116
118
|
|
|
117
|
-
|
|
118
119
|
function debugLog2(msg, object, debug) {
|
|
119
120
|
if (debug) {
|
|
120
121
|
console.log(`[initializeSvelteComponents] ${msg}`, object);
|