@constela/start 1.8.21 → 1.8.22
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 +57 -0
- package/dist/{chunk-L3BK4NOH.js → chunk-E4SJFPTB.js} +4 -4
- package/dist/{chunk-OXGKHULH.js → chunk-IZPFJSX3.js} +36 -3
- package/dist/cli/index.js +2 -2
- package/dist/index.js +2 -2
- package/dist/runtime/entry-server.d.ts +3 -1
- package/dist/runtime/entry-server.js +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -166,6 +166,63 @@ Define reusable layouts:
|
|
|
166
166
|
}
|
|
167
167
|
```
|
|
168
168
|
|
|
169
|
+
### Components with Local State in Layouts
|
|
170
|
+
|
|
171
|
+
Layout components can use `localState` and `localActions` for instance-scoped state:
|
|
172
|
+
|
|
173
|
+
```json
|
|
174
|
+
{
|
|
175
|
+
"version": "1.0",
|
|
176
|
+
"type": "layout",
|
|
177
|
+
"components": {
|
|
178
|
+
"Sidebar": {
|
|
179
|
+
"params": { "items": { "type": "list" } },
|
|
180
|
+
"localState": {
|
|
181
|
+
"expandedCategory": { "type": "string", "initial": "" }
|
|
182
|
+
},
|
|
183
|
+
"localActions": [
|
|
184
|
+
{
|
|
185
|
+
"name": "toggleCategory",
|
|
186
|
+
"steps": [{ "do": "set", "target": "expandedCategory", "value": { "expr": "var", "name": "payload" } }]
|
|
187
|
+
}
|
|
188
|
+
],
|
|
189
|
+
"view": {
|
|
190
|
+
"kind": "each",
|
|
191
|
+
"items": { "expr": "param", "name": "items" },
|
|
192
|
+
"as": "item",
|
|
193
|
+
"body": {
|
|
194
|
+
"kind": "element",
|
|
195
|
+
"tag": "div",
|
|
196
|
+
"children": [
|
|
197
|
+
{
|
|
198
|
+
"kind": "element",
|
|
199
|
+
"tag": "button",
|
|
200
|
+
"props": {
|
|
201
|
+
"onClick": {
|
|
202
|
+
"event": "click",
|
|
203
|
+
"action": "toggleCategory",
|
|
204
|
+
"payload": { "expr": "var", "name": "item", "path": "name" }
|
|
205
|
+
}
|
|
206
|
+
},
|
|
207
|
+
"children": [
|
|
208
|
+
{ "kind": "text", "value": { "expr": "var", "name": "item", "path": "name" } }
|
|
209
|
+
]
|
|
210
|
+
}
|
|
211
|
+
]
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
},
|
|
216
|
+
"view": { ... }
|
|
217
|
+
}
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
Key features:
|
|
221
|
+
- Components in layouts support `localState` and `localActions`
|
|
222
|
+
- `param` expressions inside components are substituted with prop values
|
|
223
|
+
- Works with dynamic props from `each` loops
|
|
224
|
+
- Each component instance maintains independent state
|
|
225
|
+
|
|
169
226
|
Use layout in pages:
|
|
170
227
|
|
|
171
228
|
```json
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
generateMetaTags,
|
|
4
4
|
renderPage,
|
|
5
5
|
wrapHtml
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-IZPFJSX3.js";
|
|
7
7
|
|
|
8
8
|
// src/router/file-router.ts
|
|
9
9
|
import fg from "fast-glob";
|
|
@@ -2728,15 +2728,15 @@ async function createDevServer(options = {}) {
|
|
|
2728
2728
|
query: Object.fromEntries(url.searchParams.entries()),
|
|
2729
2729
|
path: pathname
|
|
2730
2730
|
};
|
|
2731
|
-
const
|
|
2731
|
+
const hmrUrl = hmrServer ? `ws://${host}:${hmrServer.port}` : void 0;
|
|
2732
|
+
const hydrationScript = generateHydrationScript(composedProgram, widgets, routeContext, hmrUrl);
|
|
2732
2733
|
const metaTags = generateMetaTags(composedProgram.route, {
|
|
2733
2734
|
params: match.params,
|
|
2734
2735
|
query: Object.fromEntries(url.searchParams.entries()),
|
|
2735
2736
|
path: pathname
|
|
2736
2737
|
});
|
|
2737
2738
|
const cssHead = css ? (Array.isArray(css) ? css : [css]).map((p) => `<link rel="stylesheet" href="/${p}">`).join("\n") : "";
|
|
2738
|
-
const
|
|
2739
|
-
const head = [metaTags, cssHead, hmrScript].filter(Boolean).join("\n");
|
|
2739
|
+
const head = [metaTags, cssHead].filter(Boolean).join("\n");
|
|
2740
2740
|
const themeState = composedProgram.state?.["theme"];
|
|
2741
2741
|
let initialTheme;
|
|
2742
2742
|
if (themeState) {
|
|
@@ -47,10 +47,20 @@ function toJsIdentifier(id) {
|
|
|
47
47
|
}
|
|
48
48
|
return result;
|
|
49
49
|
}
|
|
50
|
-
function generateHydrationScript(program, widgets, route) {
|
|
50
|
+
function generateHydrationScript(program, widgets, route, hmrUrl) {
|
|
51
51
|
const serializedProgram = escapeJsonForScript(serializeProgram(program));
|
|
52
52
|
const hasWidgets = widgets && widgets.length > 0;
|
|
53
|
-
const
|
|
53
|
+
const enableHmr = hmrUrl && hmrUrl.length > 0;
|
|
54
|
+
let imports;
|
|
55
|
+
if (enableHmr) {
|
|
56
|
+
const baseImports = ["hydrateApp", "createHMRClient", "createHMRHandler", "createErrorOverlay"];
|
|
57
|
+
if (hasWidgets) {
|
|
58
|
+
baseImports.push("createApp");
|
|
59
|
+
}
|
|
60
|
+
imports = `import { ${baseImports.join(", ")} } from '@constela/runtime';`;
|
|
61
|
+
} else {
|
|
62
|
+
imports = hasWidgets ? `import { hydrateApp, createApp } from '@constela/runtime';` : `import { hydrateApp } from '@constela/runtime';`;
|
|
63
|
+
}
|
|
54
64
|
const widgetDeclarations = hasWidgets ? widgets.map((widget) => {
|
|
55
65
|
const jsId = toJsIdentifier(widget.id);
|
|
56
66
|
const serializedWidget = escapeJsonForScript(
|
|
@@ -81,11 +91,34 @@ if (container_${jsId}) {
|
|
|
81
91
|
program,
|
|
82
92
|
container: document.getElementById('app')
|
|
83
93
|
}`;
|
|
94
|
+
let hmrSetup = "";
|
|
95
|
+
if (enableHmr) {
|
|
96
|
+
const escapedHmrUrl = escapeJsString(hmrUrl);
|
|
97
|
+
const handlerOptions = route ? `{
|
|
98
|
+
container: document.getElementById('app'),
|
|
99
|
+
program,
|
|
100
|
+
route
|
|
101
|
+
}` : `{
|
|
102
|
+
container: document.getElementById('app'),
|
|
103
|
+
program
|
|
104
|
+
}`;
|
|
105
|
+
hmrSetup = `
|
|
106
|
+
|
|
107
|
+
const overlay = createErrorOverlay();
|
|
108
|
+
const handler = createHMRHandler(${handlerOptions});
|
|
109
|
+
const client = createHMRClient({
|
|
110
|
+
url: '${escapedHmrUrl}',
|
|
111
|
+
onUpdate: (file, newProgram) => { handler.handleUpdate(newProgram); },
|
|
112
|
+
onError: (file, errors) => { overlay.show(errors); },
|
|
113
|
+
onConnect: () => { console.log('[HMR] Connected'); }
|
|
114
|
+
});
|
|
115
|
+
client.connect();`;
|
|
116
|
+
}
|
|
84
117
|
return `${imports}
|
|
85
118
|
|
|
86
119
|
const program = ${serializedProgram};
|
|
87
120
|
${routeDeclaration ? "\n" + routeDeclaration : ""}${widgetDeclarations ? "\n" + widgetDeclarations : ""}
|
|
88
|
-
hydrateApp(${hydrateOptions});${widgetMounting}`;
|
|
121
|
+
hydrateApp(${hydrateOptions});${hmrSetup}${widgetMounting}`;
|
|
89
122
|
}
|
|
90
123
|
function wrapHtml(content, hydrationScript, head, options) {
|
|
91
124
|
let langAttr = "";
|
package/dist/cli/index.js
CHANGED
package/dist/index.js
CHANGED
|
@@ -28,14 +28,14 @@ import {
|
|
|
28
28
|
transformCsv,
|
|
29
29
|
transformMdx,
|
|
30
30
|
transformYaml
|
|
31
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-E4SJFPTB.js";
|
|
32
32
|
import {
|
|
33
33
|
evaluateMetaExpression,
|
|
34
34
|
generateHydrationScript,
|
|
35
35
|
generateMetaTags,
|
|
36
36
|
renderPage,
|
|
37
37
|
wrapHtml
|
|
38
|
-
} from "./chunk-
|
|
38
|
+
} from "./chunk-IZPFJSX3.js";
|
|
39
39
|
|
|
40
40
|
// src/build/ssg.ts
|
|
41
41
|
import { mkdir, writeFile } from "fs/promises";
|
|
@@ -54,13 +54,15 @@ interface HydrationRouteContext {
|
|
|
54
54
|
* - Serializes the program data
|
|
55
55
|
* - Calls hydrateApp with the program and container element
|
|
56
56
|
* - Optionally mounts widgets using createApp
|
|
57
|
+
* - When hmrUrl is provided, sets up HMR client, handler, and error overlay
|
|
57
58
|
*
|
|
58
59
|
* @param program - The compiled program to hydrate
|
|
59
60
|
* @param widgets - Optional array of widget configurations to mount after hydration
|
|
60
61
|
* @param route - Optional route context for dynamic routes
|
|
62
|
+
* @param hmrUrl - Optional WebSocket URL for HMR connection (development mode)
|
|
61
63
|
* @returns JavaScript module code as string
|
|
62
64
|
*/
|
|
63
|
-
declare function generateHydrationScript(program: CompiledProgram, widgets?: WidgetConfig[], route?: HydrationRouteContext): string;
|
|
65
|
+
declare function generateHydrationScript(program: CompiledProgram, widgets?: WidgetConfig[], route?: HydrationRouteContext, hmrUrl?: string): string;
|
|
64
66
|
/**
|
|
65
67
|
* Wraps rendered content in a complete HTML document.
|
|
66
68
|
*
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@constela/start",
|
|
3
|
-
"version": "1.8.
|
|
3
|
+
"version": "1.8.22",
|
|
4
4
|
"description": "Meta-framework for Constela applications",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -45,10 +45,10 @@
|
|
|
45
45
|
"tailwindcss": "^4.0.0",
|
|
46
46
|
"ws": "^8.18.0",
|
|
47
47
|
"@constela/compiler": "0.14.4",
|
|
48
|
-
"@constela/
|
|
48
|
+
"@constela/core": "0.15.2",
|
|
49
49
|
"@constela/router": "18.0.0",
|
|
50
50
|
"@constela/runtime": "0.19.2",
|
|
51
|
-
"@constela/
|
|
51
|
+
"@constela/server": "11.0.1"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
54
|
"@types/ws": "^8.5.0",
|