@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.
Files changed (3) hide show
  1. package/README.md +15 -10
  2. package/index.js +23 -22
  3. 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
- const components = import.meta.glob('/javascript/**/*.svelte', { eager: true })
19
- const componentsRoot = '/javascript'
18
+ import { initializeSvelteComponents, cleanupSvelteComponents } from 'svelte-on-rails';
20
19
 
21
- // hydrate
20
+ const components = import.meta.glob('/javascript/**/*.svelte', { eager: true });
21
+ const componentsRoot = '/javascript';
22
22
 
23
- import {initializeSvelteComponents} from "../javascript/node-module";
24
- initializeSvelteComponents(componentsRoot, components, true)
23
+ // Initialize Svelte components
24
+ initializeSvelteComponents(componentsRoot, components, true);
25
25
 
26
- // Turbo-Event-Listener
26
+ // Turbo event listener for page load
27
27
  document.addEventListener('turbo:load', () => {
28
- initializeSvelteComponents(componentsRoot, components, true);
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
- - hydrates or mounts the element
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 { mount, hydrate, unmount } from 'svelte';
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 «${virginClass}»`, debug);
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 the instance for later cleanup
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
- // In Svelte 5, use unmount to clean up the component
97
- unmount(element);
98
- debugLog2(`Unmounted Svelte component for element:`, element, debug);
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
- console.error(`[cleanupSvelteComponents] Error unmounting component for element:`, e);
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);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@csedl/svelte-on-rails",
3
- "version": "0.0.3",
3
+ "version": "0.0.5",
4
4
  "description": "Hydrates or mounts svelte components that are rendered from ruby-gem svelte-on-rails.",
5
5
  "main": "index.js",
6
6
  "exports": {