@csedl/svelte-on-rails 0.0.5 → 0.0.7

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 CHANGED
@@ -2,17 +2,19 @@
2
2
 
3
3
  Works together with the [svelte-on-rails](https://gitlab.com/sedl/svelte-on-rails) gem.
4
4
 
5
- ## Installation
5
+ ## Prerequisites
6
+
7
+ - Please follow the [gem](https://gitlab.com/sedl/svelte-on-rails) instructions.
8
+ - Svelte >= 5
9
+ - tested with ruby 3.2.2 and rails 7.1
6
10
 
7
- First, you must have svelte and the gem running on your project, please follow the [gem](https://gitlab.com/sedl/svelte-on-rails) instructions.
11
+ ## Installation
8
12
 
9
13
  ```
10
14
  npm i @csedl/svelte-on-rails
11
15
  ```
12
16
 
13
- ## Usage example with Rails/turbo/vite
14
-
15
- A file like `initializeSvelte.js` that is imported into your entrypoint file
17
+ Create a file like `initializeSvelte.js` and import it into your entrypoint file, like `application.js`
16
18
 
17
19
  ```javascript
18
20
  import { initializeSvelteComponents, cleanupSvelteComponents } from 'svelte-on-rails';
@@ -34,7 +36,8 @@ document.addEventListener('turbo:before-cache', () => {
34
36
  });
35
37
  ```
36
38
 
37
- => the latter boolean is for debugging logs to the console
39
+ - The last boolean argument is for debugging logs to the console
40
+ - I have inserted very detailed error logs, so please check your browser console for logs.
38
41
 
39
42
  ## How it works
40
43
 
@@ -52,16 +55,8 @@ to the wrapping element. Then, this lib
52
55
  - adds the attribute `data-svelte-initialized='true'`
53
56
  - removes the class `svelte-on-rails-not-initialized-component`
54
57
 
55
- ## Requirements
56
-
57
- - Svelte >= 5
58
- - tested with ruby 3.2.2 and rails 7.1
59
-
60
58
  ## Testing
61
59
 
62
- I developed this all on Rails-7, together with `vite_rails`.
63
-
64
-
65
60
  There is a [rails project](https://gitlab.com/sedl/svelte-on-rails-tests) with testings (based on playwright) where the gem and this package are tested together.
66
61
 
67
62
  The gem includes its own tests.
package/index.js CHANGED
@@ -1,123 +1,4 @@
1
- import {mount, hydrate, unmount} from 'svelte';
2
1
 
3
- // Store for tracking initialized Svelte component instances
4
- const svelteInstances = new WeakMap();
2
+ export {initializeSvelteComponents} from "./src/initializeSvelteComponents";
5
3
 
6
- /**
7
- * Initializes Svelte components on elements with the specified class.
8
- * @param {string} componentsRoot - Root path for component files
9
- * @param {Object} components - Object containing imported Svelte components
10
- * @param {boolean} debug - Enable debug logging
11
- */
12
- export function initializeSvelteComponents(componentsRoot, components, debug = false) {
13
- const virginClass = 'svelte-on-rails-not-initialized-component';
14
- const virginElements = document.getElementsByClassName(virginClass);
15
- debugLog(`Found ${virginElements.length} elements with class "${virginClass}"`, debug);
16
- if (virginElements.length === 0) return;
17
-
18
- // Convert live HTMLCollection to a static array
19
- const elementsArray = Array.from(virginElements);
20
-
21
- // Iterate over the static array
22
- for (const element of elementsArray) {
23
- const action = element.innerHTML ? 'hydrate' : 'mount';
24
- const componentName = element.getAttribute('data-svelte-component');
25
-
26
- // Check component
27
- const componentPath = `${componentsRoot}/${componentName}.svelte`.replace(/\/+/g, '/');
28
- if (!components[componentPath]) {
29
- console.error(`[initializeSvelteComponents] Component not found for path: ${componentPath}`);
30
- continue; // Proceed to the next element
31
- }
32
- const component = components[componentPath].default;
33
-
34
- // Parse props
35
- let props = {};
36
- const attrKey = 'data-props';
37
- const propsString = element.getAttribute(attrKey);
38
- try {
39
- if (propsString) props = JSON.parse(propsString);
40
- } catch (e) {
41
- console.error(`[initializeSvelteComponents] Error parsing ${attrKey} ("${propsString}") for ${componentName}:`, e);
42
- continue; // Proceed to the next element
43
- }
44
-
45
- try {
46
- let instance;
47
- if (action === 'mount') {
48
- element.innerHTML = '';
49
- instance = mount(component, {
50
- target: element,
51
- props,
52
- hydrate: false,
53
- });
54
- debugLog2(`Mounted successfully ${componentName}, with parsed props:`, props, debug);
55
- } else if (action === 'hydrate') {
56
- instance = hydrate(component, {
57
- target: element,
58
- hydrate: true,
59
- props: props,
60
- });
61
- debugLog2(`Hydrated successfully ${componentName}, with parsed props:`, props, debug);
62
- } else {
63
- console.error(`[initializeSvelteComponents] Unknown action: "${action}" for component ${componentName}`);
64
- continue; // Proceed to the next element
65
- }
66
-
67
- // Store instance and set attributes only after successful mount/hydrate
68
- svelteInstances.set(element, instance);
69
- element.classList.remove(virginClass);
70
- element.setAttribute('data-svelte-initialized', 'true');
71
- } catch (e) {
72
- console.error(`[initializeSvelteComponents] Error ${action} ${componentName}:`, e);
73
- }
74
- }
75
- }
76
-
77
- /**
78
- * Cleans up all initialized Svelte component instances.
79
- */
80
- /**
81
- * Cleans up all initialized Svelte component instances.
82
- */
83
- export function cleanupSvelteComponents(debug = false) {
84
- const initializedClass = 'svelte-initialized';
85
- const initializedElements = document.querySelectorAll(`[data-${initializedClass}]`);
86
- debugLog(`Cleaning up ${initializedElements.length} initialized Svelte components`, debug);
87
-
88
- for (const element of initializedElements) {
89
- try {
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
- } catch (e) {
100
- if (debug) {
101
- console.error(`[cleanupSvelteComponents] Error unmounting component for element:`, e);
102
- }
103
- // Fallback: Clear the element's content to ensure cleanup
104
- element.innerHTML = '';
105
- }
106
-
107
- // Remove the instance from storage and clean up attributes
108
- svelteInstances.delete(element);
109
- element.removeAttribute('data-svelte-initialized');
110
- }
111
- }
112
-
113
- function debugLog(msg, debug) {
114
- if (debug) {
115
- console.log(`[initializeSvelteComponents] ${msg}`);
116
- }
117
- }
118
-
119
- function debugLog2(msg, object, debug) {
120
- if (debug) {
121
- console.log(`[initializeSvelteComponents] ${msg}`, object);
122
- }
123
- }
4
+ export {cleanupSvelteComponents} from "./src/cleanupSvelteComponents";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@csedl/svelte-on-rails",
3
- "version": "0.0.5",
3
+ "version": "0.0.7",
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": {
@@ -0,0 +1,38 @@
1
+ import { unmount } from 'svelte';
2
+ import { debugLog, debugLog2 } from './debugUtils.js';
3
+
4
+ // Store for tracking initialized Svelte component instances
5
+ const svelteInstances = new WeakMap();
6
+
7
+ /**
8
+ * Cleans up all initialized Svelte component instances.
9
+ */
10
+ export function cleanupSvelteComponents(debug = false) {
11
+ const initializedClass = 'svelte-initialized';
12
+ const initializedElements = document.querySelectorAll(`[data-${initializedClass}]`);
13
+ debugLog(`Cleaning up ${initializedElements.length} initialized Svelte components`, debug);
14
+
15
+ for (const element of initializedElements) {
16
+ try {
17
+ // Check if an instance exists for the element
18
+ const instance = svelteInstances.get(element);
19
+ if (instance) {
20
+ // In Svelte 5, use unmount to destroy the instance
21
+ unmount(instance);
22
+ debugLog2(`Unmounted Svelte component for element:`, element, debug);
23
+ } else {
24
+ debugLog(`No instance found for element, skipping unmount:`, element, debug);
25
+ }
26
+ } catch (e) {
27
+ if (debug) {
28
+ console.error(`[cleanupSvelteComponents] Error unmounting component for element:`, e);
29
+ }
30
+ // Fallback: Clear the element's content to ensure cleanup
31
+ element.innerHTML = '';
32
+ }
33
+
34
+ // Remove the instance from storage and clean up attributes
35
+ svelteInstances.delete(element);
36
+ element.removeAttribute('data-svelte-initialized');
37
+ }
38
+ }
@@ -0,0 +1,25 @@
1
+ export function debugLog(msg, debug) {
2
+ if (debug) {
3
+ console.log(`[initializeSvelteComponents] ${msg}`);
4
+ }
5
+ }
6
+
7
+ export function debugLog2(msg, object, debug) {
8
+ if (debug) {
9
+ console.log(`[initializeSvelteComponents] ${msg}`, object);
10
+ }
11
+ }
12
+
13
+ export function checkComponentsRoot(componentsRoot, firstImport) {
14
+ const reg = new RegExp(`^${componentsRoot}`);
15
+
16
+ if (!reg.test(firstImport)) {
17
+ console.error(
18
+ `[initializeSvelteComponents] ERROR\nThe componentsRoot («${componentsRoot}») does not match.\n` +
19
+ `The first found import is: «${firstImport}».\n` +
20
+ `components root must equal the beginning of the import path, example:\n` +
21
+ `componentsRoot: «/javascript», and ` +
22
+ `import: «/javascript/components/MyComponent.svelte» => would work`
23
+ );
24
+ }
25
+ }
@@ -0,0 +1,84 @@
1
+ import { mount, hydrate } from 'svelte';
2
+ import { debugLog, debugLog2, checkComponentsRoot } from './debugUtils.js';
3
+
4
+ // Store for tracking initialized Svelte component instances
5
+ const svelteInstances = new WeakMap();
6
+
7
+ /**
8
+ * Initializes Svelte components on elements with the specified class.
9
+ * @param {string} componentsRoot - Root path for component files
10
+ * @param {Object} components - Object containing imported Svelte components
11
+ * @param {boolean} debug - Enable debug logging
12
+ */
13
+ export function initializeSvelteComponents(componentsRoot, components, debug = false) {
14
+ const virginClass = 'svelte-on-rails-not-initialized-component';
15
+ const virginElements = document.getElementsByClassName(virginClass);
16
+ debugLog(`Found ${virginElements.length} elements with class "${virginClass}"`, debug);
17
+ if (virginElements.length === 0) return;
18
+
19
+ // Convert live HTMLCollection to a static array
20
+ const elementsArray = Array.from(virginElements);
21
+
22
+ // Check if componentsRoot is setup correctly
23
+ checkComponentsRoot(componentsRoot, Object.keys(components)[0]);
24
+
25
+ // Iterate over the static array
26
+ for (const element of elementsArray) {
27
+ const action = element.innerHTML ? 'hydrate' : 'mount';
28
+ const componentName = element.getAttribute('data-svelte-component');
29
+
30
+ // Check component
31
+ const componentPath = `${componentsRoot}/${componentName}.svelte`.replace(/\/+/g, '/');
32
+ if (!components[componentPath]) {
33
+ console.error(
34
+ `[initializeSvelteComponents] ERROR\n` +
35
+ `Component not found for path: «${componentPath}»\n` +
36
+ `Imported paths are:\n` +
37
+ `«${Object.keys(components).join('»\n«')}»`
38
+ );
39
+ continue; // Proceed to the next element
40
+ }
41
+ const component = components[componentPath].default;
42
+
43
+ // Parse props
44
+ let props = {};
45
+ const attrKey = 'data-props';
46
+ const propsString = element.getAttribute(attrKey);
47
+ try {
48
+ if (propsString) props = JSON.parse(propsString);
49
+ } catch (e) {
50
+ console.error(`[initializeSvelteComponents] Error parsing ${attrKey} ("${propsString}") for ${componentName}:`, e);
51
+ continue; // Proceed to the next element
52
+ }
53
+
54
+ try {
55
+ let instance;
56
+ if (action === 'mount') {
57
+ element.innerHTML = '';
58
+ instance = mount(component, {
59
+ target: element,
60
+ props,
61
+ hydrate: false,
62
+ });
63
+ debugLog2(`Mounted successfully ${componentName}, with parsed props:`, props, debug);
64
+ } else if (action === 'hydrate') {
65
+ instance = hydrate(component, {
66
+ target: element,
67
+ hydrate: true,
68
+ props: props,
69
+ });
70
+ debugLog2(`Hydrated successfully ${componentName}, with parsed props:`, props, debug);
71
+ } else {
72
+ console.error(`[initializeSvelteComponents] Unknown action: "${action}" for component ${componentName}`);
73
+ continue; // Proceed to the next element
74
+ }
75
+
76
+ // Store instance and set attributes only after successful mount/hydrate
77
+ svelteInstances.set(element, instance);
78
+ element.classList.remove(virginClass);
79
+ element.setAttribute('data-svelte-initialized', 'true');
80
+ } catch (e) {
81
+ console.error(`[initializeSvelteComponents] Error ${action} ${componentName}:`, e);
82
+ }
83
+ }
84
+ }