@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 +11 -9
- package/index.js +1 -1
- package/package.json +4 -4
- package/src/config.js +10 -3
- package/src/initializeSvelteComponent.js +24 -17
- package/src/ssr/loadComponent.js +25 -0
- package/src/ssr/readStdin.js +15 -0
- package/src/ssr/render.js +38 -0
- package/src/svelte-on-rails-controller.js +1 -1
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
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@csedl/svelte-on-rails",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "
|
|
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.
|
|
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": "
|
|
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
|
-
|
|
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 {
|
|
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
|
-
|
|
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
|
-
|
|
17
|
-
const componentPath = `${componentName}.svelte`.replace(/\/+/g, '/');
|
|
23
|
+
const action = element.innerHTML ? 'hydrate' : 'mount';
|
|
18
24
|
|
|
19
25
|
|
|
20
|
-
//
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
|