@constela/server 4.0.0 → 5.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
@@ -92,6 +92,46 @@ Pass external data at render time:
92
92
  }
93
93
  ```
94
94
 
95
+ ### Style Evaluation
96
+
97
+ Style expressions are evaluated during SSR, producing CSS class strings:
98
+
99
+ ```json
100
+ {
101
+ "styles": {
102
+ "button": {
103
+ "base": "px-4 py-2 rounded",
104
+ "variants": {
105
+ "variant": {
106
+ "primary": "bg-blue-500 text-white",
107
+ "secondary": "bg-gray-200 text-gray-800"
108
+ }
109
+ },
110
+ "defaultVariants": { "variant": "primary" }
111
+ }
112
+ },
113
+ "view": {
114
+ "kind": "element",
115
+ "tag": "button",
116
+ "props": {
117
+ "className": {
118
+ "expr": "style",
119
+ "name": "button",
120
+ "variants": { "variant": { "expr": "lit", "value": "primary" } }
121
+ }
122
+ }
123
+ }
124
+ }
125
+ ```
126
+
127
+ ↓ SSR
128
+
129
+ ```html
130
+ <button class="px-4 py-2 rounded bg-blue-500 text-white">...</button>
131
+ ```
132
+
133
+ Pass style presets via `RenderOptions.styles` for evaluation.
134
+
95
135
  ## Output Structure
96
136
 
97
137
  ### Code Block HTML
@@ -142,6 +182,18 @@ const html = await renderToString(compiledProgram, {
142
182
  imports: {
143
183
  config: { siteName: 'My Site' },
144
184
  },
185
+ styles: {
186
+ button: {
187
+ base: 'px-4 py-2 rounded',
188
+ variants: {
189
+ variant: {
190
+ primary: 'bg-blue-500 text-white',
191
+ secondary: 'bg-gray-200 text-gray-800',
192
+ },
193
+ },
194
+ defaultVariants: { variant: 'primary' },
195
+ },
196
+ },
145
197
  });
146
198
  ```
147
199
 
@@ -155,6 +207,13 @@ interface RenderOptions {
155
207
  path?: string;
156
208
  };
157
209
  imports?: Record<string, unknown>;
210
+ styles?: Record<string, StylePreset>;
211
+ }
212
+
213
+ interface StylePreset {
214
+ base: string;
215
+ variants?: Record<string, Record<string, string>>;
216
+ defaultVariants?: Record<string, string>;
158
217
  }
159
218
  ```
160
219
 
package/dist/index.d.ts CHANGED
@@ -6,6 +6,17 @@ import { CompiledProgram } from '@constela/compiler';
6
6
  * Renders CompiledProgram to HTML string for Server-Side Rendering.
7
7
  */
8
8
 
9
+ /**
10
+ * Style preset definition for SSR
11
+ */
12
+ interface StylePreset {
13
+ base: string;
14
+ variants?: Record<string, Record<string, string>>;
15
+ defaultVariants?: Record<string, string>;
16
+ compoundVariants?: Array<Record<string, string> & {
17
+ class: string;
18
+ }>;
19
+ }
9
20
  /**
10
21
  * Options for renderToString
11
22
  */
@@ -16,6 +27,7 @@ interface RenderOptions {
16
27
  path?: string;
17
28
  };
18
29
  imports?: Record<string, unknown>;
30
+ styles?: Record<string, StylePreset>;
19
31
  }
20
32
  /**
21
33
  * Renders a CompiledProgram to an HTML string.
package/dist/index.js CHANGED
@@ -217,7 +217,7 @@ function evaluate(expr, ctx) {
217
217
  return void 0;
218
218
  }
219
219
  case "style": {
220
- return "";
220
+ return evaluateStyle(expr, ctx);
221
221
  }
222
222
  default: {
223
223
  const _exhaustiveCheck = expr;
@@ -310,6 +310,36 @@ function evaluateBinary(op, left, right, ctx) {
310
310
  throw new Error("Unknown binary operator: " + op);
311
311
  }
312
312
  }
313
+ function evaluateStyle(expr, ctx) {
314
+ const preset = ctx.styles?.[expr.name];
315
+ if (!preset) return "";
316
+ let classes = preset.base;
317
+ if (preset.variants) {
318
+ for (const variantKey of Object.keys(preset.variants)) {
319
+ let variantValueStr = null;
320
+ if (expr.variants?.[variantKey]) {
321
+ let variantValue;
322
+ try {
323
+ variantValue = evaluate(expr.variants[variantKey], ctx);
324
+ } catch {
325
+ continue;
326
+ }
327
+ if (variantValue != null) {
328
+ variantValueStr = String(variantValue);
329
+ }
330
+ } else if (preset.defaultVariants?.[variantKey] !== void 0) {
331
+ variantValueStr = preset.defaultVariants[variantKey];
332
+ }
333
+ if (variantValueStr !== null) {
334
+ const variantClasses = preset.variants[variantKey]?.[variantValueStr];
335
+ if (variantClasses) {
336
+ classes += " " + variantClasses;
337
+ }
338
+ }
339
+ }
340
+ }
341
+ return classes.trim();
342
+ }
313
343
  function formatValue(value) {
314
344
  if (value === null || value === void 0) {
315
345
  return "";
@@ -438,7 +468,8 @@ async function renderToString(program, options) {
438
468
  query: options.route.query ?? {},
439
469
  path: options.route.path ?? ""
440
470
  } : void 0,
441
- imports: options?.imports ?? program.importData
471
+ imports: options?.imports ?? program.importData,
472
+ styles: options?.styles
442
473
  };
443
474
  return await renderNode(program.view, ctx);
444
475
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@constela/server",
3
- "version": "4.0.0",
3
+ "version": "5.0.0",
4
4
  "description": "Server-side rendering for Constela UI framework",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -15,7 +15,7 @@
15
15
  "dist"
16
16
  ],
17
17
  "peerDependencies": {
18
- "@constela/compiler": "^0.8.0"
18
+ "@constela/compiler": "^0.9.0"
19
19
  },
20
20
  "dependencies": {
21
21
  "isomorphic-dompurify": "^2.35.0",
@@ -28,7 +28,7 @@
28
28
  "tsup": "^8.0.0",
29
29
  "typescript": "^5.3.0",
30
30
  "vitest": "^2.0.0",
31
- "@constela/compiler": "0.8.0"
31
+ "@constela/compiler": "0.9.0"
32
32
  },
33
33
  "engines": {
34
34
  "node": ">=20.0.0"