@cossackframework/renderer 0.6.0 → 0.7.1

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.
@@ -1,5 +1,4 @@
1
1
  import { CossackElement } from './cossack-element';
2
-
3
2
  export declare class TemplateResult {
4
3
  readonly strings: TemplateStringsArray;
5
4
  readonly values: unknown[];
@@ -7,7 +6,7 @@ export declare class TemplateResult {
7
6
  constructor(strings: TemplateStringsArray, values: unknown[]);
8
7
  }
9
8
  export declare const html: (strings: TemplateStringsArray, ...values: unknown[]) => TemplateResult;
10
- export declare const component: <T extends CossackElement>(clazz: new () => T, props?: Record<string, unknown>, children?: unknown) => TemplateResult;
9
+ export declare const component: <T extends CossackElement>(clazz: new () => T, props?: T['props'] & Record<string, unknown>, children?: unknown) => TemplateResult;
11
10
  export declare const isTemplateResult: (value: unknown) => value is TemplateResult;
12
11
  export declare class UnsafeHTMLResult {
13
12
  readonly value: string;
@@ -16,5 +15,18 @@ export declare class UnsafeHTMLResult {
16
15
  export declare const unsafeHTML: (value: string) => UnsafeHTMLResult;
17
16
  export declare const isUnsafeHTML: (value: unknown) => value is UnsafeHTMLResult;
18
17
  export declare const escapeHtml: (unsafe: unknown) => string;
19
- export declare const renderToString: (result: TemplateResult) => string;
18
+ export declare const renderToString: (result: TemplateResult, opts?: {
19
+ hydrate?: boolean;
20
+ }) => string;
20
21
  export declare const render: (result: TemplateResult, container: Node) => void;
22
+ /**
23
+ * Hydrate an existing container whose children were produced by SSR
24
+ * (`renderToString(result, { hydrate: true })`). Binds Part objects to the
25
+ * existing DOM nodes and adopts their SSR-rendered values instead of clearing
26
+ * and rebuilding — preserving the server-rendered DOM (no flash, no lost
27
+ * input/scroll, no duplicate component initialization).
28
+ *
29
+ * If the existing DOM cannot be confidently matched to the template, falls
30
+ * back to a full `render()` so behaviour is always correct.
31
+ */
32
+ export declare const hydrate: (result: TemplateResult, container: Node) => void;
@@ -13,6 +13,29 @@ export declare class KeyResult {
13
13
  readonly template: unknown;
14
14
  constructor(value: unknown, template: unknown);
15
15
  }
16
+ export declare class BindResult {
17
+ readonly component: unknown;
18
+ readonly fieldName: string;
19
+ constructor(component: unknown, fieldName: string);
20
+ }
21
+ export declare class PreventDefaultResult {
22
+ readonly handler: EventListener;
23
+ readonly novalidate: boolean;
24
+ constructor(handler: EventListener, novalidate: boolean);
25
+ }
26
+ export declare class IfDefinedResult {
27
+ readonly value: unknown;
28
+ constructor(value: unknown);
29
+ }
30
+ export declare class GuardResult {
31
+ readonly deps: unknown;
32
+ readonly factory: () => unknown;
33
+ constructor(deps: unknown, factory: () => unknown);
34
+ }
35
+ export declare class CacheResult {
36
+ readonly value: unknown;
37
+ constructor(value: unknown);
38
+ }
16
39
  /**
17
40
  * Checks against the DOM value instead of the previous rendered value.
18
41
  * Usage: <input .value=${live(x)}>
@@ -39,4 +62,203 @@ export declare const styleMap: (styleInfo: Record<string, string | undefined | n
39
62
  * Usage: html`${key(id, html`<child></child>`)}`
40
63
  */
41
64
  export declare const key: (value: unknown, template: unknown) => KeyResult;
65
+ /**
66
+ * Two-way binding for a form element's value/checked against a component
67
+ * state field. Reads the field for rendering AND writes user edits back to
68
+ * it (which triggers a re-render via the `@State` setter).
69
+ *
70
+ * Usage (`.value`/`.checked` is inferred from the bound attribute):
71
+ * <input .value="${bind(this, 'email')}" />
72
+ * <input type="checkbox" .checked="${bind(this, 'active')}" />
73
+ *
74
+ * `bind` picks the DOM property to bind from the attribute it is attached to
75
+ * (`.value` -> `value`, `.checked` -> `checked`) and the appropriate writeback
76
+ * event for the element (`input` for text-like inputs/textarea; `change` for
77
+ * checkbox/radio/range inputs and `<select>`).
78
+ *
79
+ * @param component The component instance owning the state field (usually `this`).
80
+ * @param fieldName The state field name to read from / write back to. Supports
81
+ * dot-paths into nested state (e.g. `'address.street'`), so a
82
+ * `@Store` field can be bound at any depth.
83
+ */
84
+ export declare const bind: (component: unknown, fieldName: string) => BindResult;
85
+ /**
86
+ * Read a (possibly dotted) field path off a component, walking the object graph
87
+ * via property access (so `@Store` reactive proxies traverse correctly).
88
+ * Single-segment paths take the fast path; `null`/`undefined` short-circuits.
89
+ *
90
+ * @internal — used by the bind directive; lives here so the renderer stays
91
+ * dependency-free (it mirrors `resolveStatePath` in @cossackframework/core).
92
+ */
93
+ export declare function resolveField(component: unknown, path: string): unknown;
94
+ /**
95
+ * Write a value into a (possibly dotted) field path. Resolves to the parent via
96
+ * property access (so the `@Store` Proxy trap fires on the final segment) and
97
+ * creates intermediate objects when a segment is missing.
98
+ *
99
+ * @internal — counterpart to {@link resolveField} for the bind writeback.
100
+ */
101
+ export declare function setField(component: unknown, path: string, value: unknown): void;
102
+ /**
103
+ * Wraps an event handler so the event's default is prevented before it runs.
104
+ *
105
+ * Browser-native (HTML5 constraint) validation is ALSO disabled by default —
106
+ * the bound <form> gets `novalidate` — because Cossack encourages custom
107
+ * `@Validate` validation. Pass `{ novalidate: false }` to restore native
108
+ * validation.
109
+ *
110
+ * Usage:
111
+ * <form @submit="${preventDefault(this.serverHandle)}"></form>
112
+ * <!-- keep native validation -->
113
+ * <form @submit="${preventDefault(this.serverHandle, { novalidate: false })}"></form>
114
+ *
115
+ * @param handler The event handler to invoke after preventing default.
116
+ * @param options Optional. `novalidate` (default `true`) toggles `novalidate`.
117
+ */
118
+ export declare const preventDefault: (handler: EventListener, options?: {
119
+ novalidate?: boolean;
120
+ }) => PreventDefaultResult;
121
+ /**
122
+ * Only omit an attribute when the value is `undefined`; render every other
123
+ * value (including `null`, `false`, `0`, `''`) as a normal attribute. This is
124
+ * the Lit-faithful `ifDefined` directive.
125
+ *
126
+ * By default a plain Cossack attribute binding `href="${url}"` already omits the
127
+ * attribute for `null`/`undefined`/`false`, and renders everything else. The
128
+ * difference `ifDefined` makes is two-fold:
129
+ *
130
+ * 1. Only `undefined` drops the attribute — `null` is rendered as `"null"`,
131
+ * `false` as `"false"` (useful for data attributes where you want the
132
+ * literal string rather than an omission).
133
+ * 2. When the value transitions from defined back to `undefined` on a
134
+ * re-render, the attribute is explicitly removed.
135
+ *
136
+ * Usage:
137
+ * html`<a href="${ifDefined(url)}">link</a>`
138
+ * html`<div data-flag="${ifDefined(maybeUndefined)}">...</div>`
139
+ *
140
+ * @param value The value to render, or `undefined` to omit the attribute.
141
+ */
142
+ export declare const ifDefined: (value: unknown) => IfDefinedResult;
143
+ /**
144
+ * Defers re-evaluating a template until its dependencies change. `guard` caches
145
+ * the value produced by `factory()` and reuses it on subsequent renders as long
146
+ * as `deps` is shallow-equal to the previous render; `factory` only runs again
147
+ * when a dependency changes.
148
+ *
149
+ * This is an optimization for expensive rendering (large lists, heavy
150
+ * computations): wrap the costly part so it is not recomputed on every render,
151
+ * only when the inputs it actually depends on change.
152
+ *
153
+ * Usage (single dependency):
154
+ * html`<ul>${guard(items, () => html`...expensive...`)}</ul>`
155
+ * Usage (multiple dependencies — pass an array):
156
+ * html`${guard([query, page], () => renderResults(query, page))}`
157
+ *
158
+ * The dependency comparison is shallow: for a single value it uses `===`; for
159
+ * an array each element is compared with `===` and lengths must match.
160
+ *
161
+ * @param deps A comparable value or an array of values.
162
+ * @param factory Called (with no args) to produce the value when deps change.
163
+ */
164
+ export declare const guard: (deps: unknown, factory: () => unknown) => GuardResult;
165
+ /**
166
+ * Caches and reuses previously-rendered template subtrees instead of destroying
167
+ * them when the rendered value switches to a different template. When you
168
+ * toggle between two (or more) templates behind `cache`, switching back to one
169
+ * that was rendered before reattaches its existing DOM and part tree rather
170
+ * than rebuilding it — so component state, scroll positions, focus, and DOM
171
+ * identity are preserved across the swap.
172
+ *
173
+ * Without `cache`, a conditional like `cond ? html\`<A/>\` : html\`<B/>\``
174
+ * rebuilds the previously-shown branch every time you switch back, losing that
175
+ * branch's state. With `cache`, each branch is kept alive while it is not
176
+ * displayed.
177
+ *
178
+ * Usage:
179
+ * html`${cache(showA ? html`<A-component></A-component>` : html`<B-component></B-component>`)}`
180
+ *
181
+ * The cache key is the template's `strings` (the literal parts of the tagged
182
+ * template), so distinct `html\`...\`` sites are cached separately. Non-template
183
+ * values are passed through unchanged (no caching benefit, no harm).
184
+ *
185
+ * @param value A `TemplateResult` (or any node value) to cache by template.
186
+ */
187
+ export declare const cache: (value: unknown) => CacheResult;
188
+ /**
189
+ * Renders one of two templates based on a condition. `when` is a pure function
190
+ * that picks a branch and returns its result — it has no engine coupling, the
191
+ * chosen value flows through the renderer like any other node value.
192
+ *
193
+ * Usage:
194
+ * html`${when(isOn, () => html`<p>On</p>`, () => html`<p>Off</p>`)}`
195
+ *
196
+ * The case functions receive the condition so they can use it without a second
197
+ * binding. If the false case is omitted, `undefined` is returned (renders nothing).
198
+ *
199
+ * @param condition Truthy/falsy value selecting the branch.
200
+ * @param trueCase Called with `condition` when it is truthy.
201
+ * @param falseCase Optional. Called with `condition` when it is falsy.
202
+ */
203
+ export declare const when: (condition: unknown, trueCase: (c: unknown) => unknown, falseCase?: (c: unknown) => unknown) => unknown;
204
+ /**
205
+ * Selects a template by matching a value against an ordered list of cases,
206
+ * like a `switch`. The first case whose lookup `===` the value wins; otherwise
207
+ * the optional default case is used (or nothing is rendered).
208
+ *
209
+ * Usage:
210
+ * html`${choose(status, [
211
+ * ['idle', () => html`<i>Idle</i>`],
212
+ * ['loading', () => html`<b>Loading…</b>`],
213
+ * ], () => html`<span>Unknown</span>`)}`
214
+ *
215
+ * Each case function receives the matched value and its case index.
216
+ *
217
+ * @param value The value to match.
218
+ * @param cases Ordered `[lookup, fn]` pairs. First `lookup === value` wins.
219
+ * @param defaultCase Optional fallback called when no case matches.
220
+ */
221
+ export declare const choose: <T>(value: T, cases: Array<[T, (v: T, i: number) => unknown]>, defaultCase?: () => unknown) => unknown;
222
+ /**
223
+ * Maps an iterable to renderable values (e.g. templates) and returns the array.
224
+ * It is a thin helper over `Array.from` so that plain objects/iterables can be
225
+ * rendered as a list without manually spreading into an array first. The
226
+ * renderer already handles arrays, so this needs no engine integration.
227
+ *
228
+ * Usage:
229
+ * html`<ul>${map(items, (item) => html`<li>${item.name}</li>`)}</ul>`
230
+ *
231
+ * @param iterable Anything `Array.from` accepts.
232
+ * @param identityFn Maps each item to a renderable value, receiving `(item, index)`.
233
+ */
234
+ export declare const map: <T>(iterable: Iterable<T> | ArrayLike<T>, identityFn: (item: T, index: number) => unknown) => unknown[];
235
+ /**
236
+ * Joins renderable values with a separator interleaved between each pair — the
237
+ * list equivalent of `Array.prototype.join`, but with values rather than
238
+ * strings so the separator can itself be a template (e.g. a `<li>` divider).
239
+ *
240
+ * Usage (string separator):
241
+ * html`${join(names, (n) => n, ', ')}` // "a, b, c"
242
+ * Usage (separator template):
243
+ * html`<ul>${join(items, (i) => html`<li>${i}</li>`, () => html`<li class="sep">•</li>`)}</ul>`
244
+ *
245
+ * @param iterable Anything `Array.from` accepts.
246
+ * @param valueFn Maps each item to a renderable value.
247
+ * @param joiner Either a static renderable value or `(index) => value`. The
248
+ * index is the position of the item BEFORE the separator.
249
+ */
250
+ export declare const join: <T>(iterable: Iterable<T> | ArrayLike<T>, valueFn: (item: T, index: number) => unknown, joiner: unknown | ((index: number) => unknown)) => unknown[];
251
+ /**
252
+ * Generates an increasing (or decreasing) sequence of numbers as an array,
253
+ * suitable for rendering a fixed number of items. Overloads:
254
+ * range(end) -> [0, 1, …, end-1] (half-open, step +1)
255
+ * range(start, end) -> [start, …, end-1] (step +1)
256
+ * range(start, end, step) -> [start, start+step, …] (stops before `end`)
257
+ *
258
+ * Usage:
259
+ * html`<ul>${range(0, 5).map((n) => html`<li>${n}</li>`)}</ul>`
260
+ *
261
+ * A negative `step` walks downward (e.g. `range(5, 0, -1)` → `[5,4,3,2,1]`).
262
+ */
263
+ export declare const range: (startOrEnd: number, end?: number, step?: number) => number[];
42
264
  export type RefCallback = (el: Element | undefined) => void;
package/dist/index.js CHANGED
@@ -1,10 +1,10 @@
1
- import { C as e, S as t, _ as n, a as r, b as i, c as a, d as o, f as s, g as c, h as l, i as u, l as d, m as f, n as p, o as m, p as h, r as g, s as _, t as v, u as y, v as b, x, y as S } from "./cossack-html-QsGiJk7a.js";
1
+ import { A as e, B as t, C as n, D as r, E as i, F as a, I as o, L as s, M as c, N as l, O as u, P as d, R as f, S as p, T as m, V as h, _ as g, a as _, b as v, c as y, d as b, f as x, g as S, h as C, i as w, j as T, k as E, l as D, m as O, n as k, o as A, p as j, r as M, s as N, t as P, u as F, v as I, w as L, x as R, y as z, z as B } from "./cossack-html-D6DeVgtW.js";
2
2
  //#region src/context.ts
3
- function C(e) {
3
+ function V(e) {
4
4
  return {
5
5
  id: Math.random().toString(36).slice(2),
6
6
  defaultValue: e
7
7
  };
8
8
  }
9
9
  //#endregion
10
- export { S as CossackElement, o as KeyResult, s as LiveResult, h as RepeatResult, v as TemplateResult, p as UnsafeHTMLResult, f as classMap, g as component, C as createContext, u as escapeHtml, r as html, i as instanceStack, e as isComponentResult, m as isTemplateResult, _ as isUnsafeHTML, l as key, c as live, x as popCurrentInstance, t as pushCurrentInstance, a as render, d as renderToString, n as repeat, b as styleMap, y as unsafeHTML };
10
+ export { x as BindResult, j as CacheResult, s as CossackElement, O as GuardResult, C as IfDefinedResult, S as KeyResult, g as LiveResult, I as PreventDefaultResult, z as RepeatResult, P as TemplateResult, k as UnsafeHTMLResult, v as bind, R as cache, p as choose, n as classMap, M as component, V as createContext, w as escapeHtml, L as guard, _ as html, A as hydrate, m as ifDefined, f as instanceStack, h as isComponentResult, N as isTemplateResult, y as isUnsafeHTML, i as join, r as key, u as live, E as map, B as popCurrentInstance, e as preventDefault, t as pushCurrentInstance, T as range, D as render, F as renderToString, c as repeat, l as resolveField, d as setField, a as styleMap, b as unsafeHTML, o as when };
package/dist/server.js CHANGED
@@ -1,4 +1,4 @@
1
- import { i as e, l as t } from "./cossack-html-QsGiJk7a.js";
1
+ import { i as e, u as t } from "./cossack-html-D6DeVgtW.js";
2
2
  //#region src/minify-html.ts
3
3
  var n = /<(script|style|pre|textarea)\b[^>]*>[\s\S]*?<\/\1>/gi, r = RegExp(`<(${[.../* @__PURE__ */ new Set([
4
4
  "area",
@@ -30,7 +30,7 @@ function s(e, t) {
30
30
  return e.replace(/\x00PRESERVE_(\d+)\x00/g, (e, n) => t[parseInt(n, 10)]);
31
31
  }
32
32
  function c(e) {
33
- let { html: t, blocks: n } = o(e), c = t.replace(/<!--(?!\[if\s)[\s\S]*?-->/g, "");
33
+ let { html: t, blocks: n } = o(e), c = t.replace(/<!--(?!\[if\s|\/?CRP|CSA-[SE])[\s\S]*?-->/g, "");
34
34
  return c = c.replace(i, ""), c = c.replace(/\s+/g, " "), c = c.replace(/>\s+</g, "><"), c = c.replace(a, (e, t, n) => `${t}=${n}`), c = c.replace(r, (e, t, n) => `<${t}${n ? n.trimEnd() : ""}>`), c = s(c, n), c = c.replace(/>\s+</g, "><"), c = c.trim(), c;
35
35
  }
36
36
  //#endregion
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cossackframework/renderer",
3
- "version": "0.6.0",
3
+ "version": "0.7.1",
4
4
  "type": "module",
5
5
  "description": "Light DOM rendering engine for the Cossack Framework",
6
6
  "license": "MIT",
@@ -10,6 +10,7 @@
10
10
  "directory": "packages/renderer"
11
11
  },
12
12
  "main": "./dist/index.js",
13
+ "module": "./dist/index.js",
13
14
  "types": "./dist/index.d.ts",
14
15
  "files": [
15
16
  "dist"
@@ -29,13 +30,12 @@
29
30
  },
30
31
  "devDependencies": {
31
32
  "oxlint": "^1.41.0",
32
- "vite": "^8.1.0",
33
- "vite-plugin-dts": "^3.9.1",
34
- "vite-tsconfig-paths": "^5.1.4",
35
- "vitest": "^4.1.9"
33
+ "typescript": "^7.0.2",
34
+ "vite": "^8.1.4",
35
+ "vitest": "^4.1.10"
36
36
  },
37
37
  "scripts": {
38
- "build": "vite build",
38
+ "build": "vite build && tsc -p tsconfig.declarations.json",
39
39
  "test": "vitest",
40
40
  "lint": "oxlint"
41
41
  }
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};