@csedl/svelte-on-rails 7.1.1 → 10.0.0

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,6 +2,10 @@
2
2
 
3
3
  Works together with the [svelte-on-rails](https://svelte-on-rails.dev) gem.
4
4
 
5
+ Versions here do not have many updates here, and are more dependend on the versions from the gem.
6
+ So, i added no releases here.
7
+ Checkout Release Notes on the gems repository.
8
+
5
9
  ## Prerequisites
6
10
 
7
11
  - Please follow the [gem](https://svelte-on-rails.dev) instructions.
@@ -25,6 +29,13 @@ For configs you can do
25
29
  ```javascript
26
30
  import { SvelteOnRails } from '@csedl/svelte-on-rails'
27
31
  SvelteOnRails.debug = true
32
+
33
+ // source for svelte components for hydration
34
+ // by default svelte components are mapped by sourceCodeDir from vite.json
35
+ // which usually is app/frontend/**/*.svelte
36
+ SvelteOnRails.includeRailsViews = true
37
+ // when set to true, additionaly, app/views/**/*.svelte is mapped
38
+ // => this is a 2016 new introduced feature, status: experimental
28
39
  ```
29
40
 
30
41
  The `SvelteOnRails` Object is accessible by `window.SvelteOnRails`
@@ -42,12 +53,3 @@ with the properties given by the ruby gem.
42
53
  on Svelte Components.
43
54
  - Helper for adding eventListeners on Svelte Components.
44
55
 
45
- ## Options
46
-
47
- by setting
48
-
49
- ```javascript
50
- window.debugSvelteOnRailsComponents = true
51
- ```
52
-
53
- you have a console log of the component initialization and disconnection.
package/index.js CHANGED
@@ -1,5 +1,5 @@
1
- import {Application} from "@hotwired/stimulus"
2
1
  import SvelteOnRails from "./src/config.js";
2
+ import {Application} from "@hotwired/stimulus"
3
3
  import {dispatchSvelteStreamEvent} from "./src/dispatch-event.js";
4
4
  import {actionCableDebugLog} from "./src/logger.js";
5
5
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@csedl/svelte-on-rails",
3
- "version": "7.1.1",
4
- "description": "Mounts Svelte components from the svelte-on-rails gem and handles Actions received by Web Socket.",
3
+ "version": "10.0.0",
4
+ "description": "Hydrates Svelte components from the svelte-on-rails gem and can handle Actions received by Web Socket.",
5
5
  "main": "index.js",
6
6
  "type": "module",
7
7
  "exports": {
@@ -11,7 +11,7 @@
11
11
  },
12
12
  "peerDependencies": {
13
13
  "@hotwired/stimulus": "^3.2.2",
14
- "svelte": "^5.25.12"
14
+ "svelte": "^5.50.0"
15
15
  },
16
16
  "scripts": {
17
17
  "test": "echo \"Error: no test specified\" && exit 1"
@@ -26,7 +26,7 @@
26
26
  "ssr"
27
27
  ],
28
28
  "author": "Christian Sedlmair",
29
- "license": "Copyright 2028 + MIT, see: LICENSE.md",
29
+ "license": "LICENSE.md",
30
30
  "bugs": {
31
31
  "url": "https://gitlab.com/sedl/csedl-svelte-on-rails/-/issues"
32
32
  },
package/src/config.js CHANGED
@@ -1,5 +1,6 @@
1
1
  // src/config.js
2
2
  let _debug = false; // Internal state to track debug
3
+ let _includeRailsViews = false;
3
4
 
4
5
  import {debugLog} from "./logger.js";
5
6
 
@@ -8,9 +9,15 @@ const SvelteOnRails = {
8
9
  return _debug;
9
10
  },
10
11
  set debug(value) {
11
- _debug = value;
12
-
13
- debugLog('debugging active')
12
+ _debug = !!value;
13
+ debugLog(`Debug mode: ${_debug ? "enabled" : "disabled"}`);
14
+ },
15
+ get includeRailsViews() { // ← clearer, reads like a question
16
+ return _includeRailsViews;
17
+ },
18
+ set includeRailsViews(value) {
19
+ _includeRailsViews = !!value;
20
+ debugLog(`Rails views as components: ${_includeRailsViews ? "enabled" : "disabled"}`);
14
21
  },
15
22
  };
16
23
 
@@ -1,30 +1,35 @@
1
1
  import {mount, hydrate} from 'svelte';
2
- import { debugLog } from "./logger.js";
2
+ import {debugLog} from "./logger.js";
3
+ import SvelteOnRails from "./config.js";
3
4
 
4
5
  // Store for tracking initialized Svelte component instances
5
6
  const svelteInstances = new WeakMap();
6
7
 
7
- const allComponents = import.meta.glob('/**/*.svelte', {eager: true});
8
8
 
9
9
 
10
- export function initializeSvelteComponent(element, debug = false) {
11
10
 
12
- const action = element.innerHTML ? 'hydrate' : 'mount';
13
- const componentName = element.getAttribute('data-svelte-component');
11
+ export function initializeSvelteComponent(element, debug = false) {
14
12
 
13
+ const incl = SvelteOnRails.includeRailsViews;
14
+ let allComponents
15
+ if (SvelteOnRails.includeRailsViews) {
16
+ allComponents = import.meta.glob(['/**/*.svelte', '/../../app/views/**/*.svelte'], {eager: true});
17
+ debugLog("Svelte components loaded from vite-source + app/views");
18
+ } else {
19
+ allComponents = import.meta.glob(['/**/*.svelte'], {eager: true});
20
+ debugLog("Svelte components loaded from vite-source");
21
+ }
15
22
 
16
- // Check component
17
- const componentPath = `${componentName}.svelte`.replace(/\/+/g, '/');
23
+ const action = element.innerHTML ? 'hydrate' : 'mount';
18
24
 
19
25
 
20
- // fetch component
21
- let component
22
- for (const cmp in allComponents) {
23
- if (cmp === componentPath) {
24
- component = allComponents[cmp].default;
25
- debugLog(`found component «${cmp}»`, component)
26
- } else {
27
- }
26
+ // find component
27
+ const componentName = element.getAttribute('data-component');
28
+ debugLog(`Start ${action}: ${componentName}`);
29
+ let foundComponent = allComponents[componentName];
30
+ if (!foundComponent) {
31
+ console.error(`[svelte-on-rails:initializeSvelteComponent] component not found: «${componentName}»\navailable components:\n${Object.keys(allComponents).join('\n')}`);
32
+ return;
28
33
  }
29
34
 
30
35
 
@@ -43,15 +48,17 @@ export function initializeSvelteComponent(element, debug = false) {
43
48
  try {
44
49
  let instance;
45
50
  if (action === 'mount') {
51
+ debugLog(`Start mounting ${componentName} (${typeof component})`);
46
52
  element.innerHTML = '';
47
- instance = mount(component, {
53
+ instance = mount(foundComponent.default, {
48
54
  target: element,
49
55
  props,
50
56
  hydrate: false,
51
57
  });
52
58
  debugLog(`Mounted successfully ${componentName}, with parsed props:`, props, debug);
53
59
  } else if (action === 'hydrate') {
54
- instance = hydrate(component, {
60
+ debugLog(`Start hydrating: ${componentName} (${typeof component})`);
61
+ instance = hydrate(foundComponent.default, {
55
62
  target: element,
56
63
  hydrate: true,
57
64
  props: props,
@@ -0,0 +1,25 @@
1
+ import {fileURLToPath} from 'node:url';
2
+ import {dirname, resolve} from 'node:path';
3
+
4
+
5
+ export async function loadComponentModule(compiledFile) {
6
+ // Get the directory of the current script
7
+ const __filename = fileURLToPath(import.meta.url);
8
+ const __dirname = dirname(__filename);
9
+
10
+ try {
11
+ // Convert relative path to absolute path
12
+ const absolutePath = resolve(__dirname, compiledFile);
13
+
14
+ // Convert absolute path to a file URL
15
+ const modulePath = `file://${absolutePath}`;
16
+
17
+ // Import the module
18
+ const module = await import(modulePath);
19
+ return module.default;
20
+ } catch (error) {
21
+ console.error(`=> compiledFile: «${compiledFile}»`);
22
+ console.error(`[loadComponentModule] Error loading component from ${compiledFile}:`, error);
23
+ process.exit(1);
24
+ }
25
+ }
@@ -0,0 +1,15 @@
1
+ export function readPropsFromStdin() {
2
+ return new Promise((resolve, reject) => {
3
+ let input = '';
4
+ process.stdin.setEncoding('utf8');
5
+ process.stdin.on('data', (chunk) => (input += chunk));
6
+ process.stdin.on('end', () => {
7
+ try {
8
+ resolve(JSON.parse(input));
9
+ } catch (error) {
10
+ reject(new Error(`Error parsing JSON from STDIN: ${error.message}\nInput: «${input}»`));
11
+ }
12
+ });
13
+ process.stdin.on('error', (error) => reject(new Error(`Error reading STDIN: ${error.message}`)));
14
+ });
15
+ }
@@ -0,0 +1,38 @@
1
+
2
+ import { render } from 'svelte/server';
3
+ import { readPropsFromStdin } from "./readStdin.js";
4
+ import { loadComponentModule } from "./loadComponent.js";
5
+
6
+ const compiledComponentPath = process.argv[2];
7
+
8
+
9
+ (async () => {
10
+ console.log(`[svelte-on-rails:debug] awaiting load component => «${compiledComponentPath}»`);
11
+ const compiledComponent = await loadComponentModule(compiledComponentPath);
12
+
13
+ console.log(`[svelte-on-rails:debug] component read: «${compiledComponent}»`);
14
+
15
+ const props = await readPropsFromStdin();
16
+ console.log(`[svelte-on-rails:debug] props read: «${JSON.stringify(props)}»`);
17
+
18
+ try {
19
+ // Svelte 5+ SSR rendering (runes-compatible; signals omitted on server for perf)
20
+ // Returns { body: string, head: string } — no 'html' or 'css' props
21
+ const { body, head } = render(compiledComponent, { props });
22
+
23
+ const res = {
24
+ status: 'SUCCESS',
25
+ html: body, // Use 'body' as the main HTML content (replaces old 'html')
26
+ head: head || '', // Optional: Include <head> content (styles, meta, etc.)
27
+ // Add bodyAttributes or other result props if needed in future
28
+ };
29
+
30
+ console.log('[svelte-on-rails:successful-json-response]' + JSON.stringify(res));
31
+ } catch (error) {
32
+ console.error('[svelte-on-rails:debug] Error rendering component:', error);
33
+ process.exit(1);
34
+ }
35
+ })();
36
+
37
+
38
+
@@ -1,11 +1,11 @@
1
1
  import {Controller} from "@hotwired/stimulus"
2
2
  import {initializeSvelteComponent} from "./initializeSvelteComponent";
3
3
  import {cleanupSvelteComponent} from "./cleanupSvelteComponent";
4
+ import {debugLog} from "./logger.js";
4
5
 
5
6
  export default class extends Controller {
6
7
 
7
8
  connect() {
8
-
9
9
  const stat = this.element.getAttribute('data-svelte-status');
10
10
  if (stat === 'do-not-hydrate-me') return;
11
11