@nisli/core 0.46.0 → 0.47.4

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
@@ -16,7 +16,7 @@ import { signal, component, html } from '@nisli/core';
16
16
  const Counter = component('x-counter', () => {
17
17
  const count = signal(0);
18
18
  return html`
19
- <button @click=${() => count.set(count() + 1)}>
19
+ <button @click=${() => count.value++}>
20
20
  Count: ${count}
21
21
  </button>
22
22
  `;
@@ -34,6 +34,7 @@ const Counter = component('x-counter', () => {
34
34
  - **Lifecycle** — `onMount`, `onCleanup`, `useHostEvent`
35
35
  - **Refs** — Direct element access via `ref()`
36
36
  - **Control Flow** — `when()` for toggles, `each()` for keyed list rendering
37
+ - **Static Rendering** — DOM-free HTML string rendering via `@nisli/core/static`
37
38
 
38
39
  ## API
39
40
 
@@ -45,7 +46,7 @@ effect(() => { ... }) // Side effect that tracks dependencies
45
46
 
46
47
  // Components
47
48
  component('tag-name', (props, host) => html`...`)
48
- component<Props>('tag-name', { props: [...] }, (props, host) => html`...`)
49
+ component<Props>('tag-name', (props, host) => html`...`)
49
50
 
50
51
  // Templates — signals are implicit, no .value needed
51
52
  html`<div>${count}</div>`
@@ -69,11 +70,11 @@ const { data, loading, error } = query(
69
70
  // Lifecycle
70
71
  onMount(() => { ... })
71
72
  onCleanup(() => { ... })
72
- useHostEvent('click', handler)
73
+ useHostEvent(host, 'click', handler)
73
74
 
74
75
  // Refs
75
76
  const el = ref<HTMLDivElement>()
76
- html`<div ${el}>...</div>`
77
+ html`<div ref="${el}">...</div>`
77
78
 
78
79
  // Events
79
80
  class Nav extends Emitter<{ select: { id: string } }> {}
@@ -81,6 +82,21 @@ inject(Nav).emit('select', { id })
81
82
  inject(Nav).on('select', ({ id }) => { ... })
82
83
  ```
83
84
 
85
+ ## Static Rendering
86
+
87
+ Use the static entry point when you want HTML strings for build-time pages,
88
+ feeds, emails, or other DOM-free output.
89
+
90
+ ```typescript
91
+ import { staticHtml, raw, renderToString } from '@nisli/core/static';
92
+
93
+ const page = staticHtml`<article>${raw(markdownHtml)}</article>`;
94
+ const output = renderToString(page);
95
+ ```
96
+
97
+ `staticHtml` is intentionally separate from browser `html`. Browser `html`
98
+ returns mountable DOM bindings; `staticHtml` returns escaped static HTML strings.
99
+
84
100
  ## Size
85
101
 
86
102
  ~2,600 lines of TypeScript. Zero dependencies.
package/dist/ref.d.ts CHANGED
@@ -10,11 +10,11 @@
10
10
  *
11
11
  * onMount(() => inputRef.current?.focus());
12
12
  *
13
- * return html`<input ${inputRef} @keydown.enter=${onSubmit} />`;
13
+ * return html`<input ref="${inputRef}" @keydown.enter=${onSubmit} />`;
14
14
  * ```
15
15
  *
16
- * The template engine detects `Ref` objects in expression positions
17
- * (not inside attribute values) and assigns the nearest parent element.
16
+ * The template engine detects `Ref` objects in `ref="${...}"`
17
+ * attribute bindings and assigns the referenced element.
18
18
  */
19
19
  export declare const REF_BRAND: unique symbol;
20
20
  export interface Ref<T extends Element = Element> {
package/dist/ref.js CHANGED
@@ -10,11 +10,11 @@
10
10
  *
11
11
  * onMount(() => inputRef.current?.focus());
12
12
  *
13
- * return html`<input ${inputRef} @keydown.enter=${onSubmit} />`;
13
+ * return html`<input ref="${inputRef}" @keydown.enter=${onSubmit} />`;
14
14
  * ```
15
15
  *
16
- * The template engine detects `Ref` objects in expression positions
17
- * (not inside attribute values) and assigns the nearest parent element.
16
+ * The template engine detects `Ref` objects in `ref="${...}"`
17
+ * attribute bindings and assigns the referenced element.
18
18
  */
19
19
  // ── Brand for ref detection in templates ─────────────────────────────
20
20
  export const REF_BRAND = Symbol.for('backlog.ref');
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Static rendering public API.
3
+ */
4
+ export { staticHtml, raw, renderToString, type StaticResult, type RawHtml, } from './render.js';
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Static rendering public API.
3
+ */
4
+ export { staticHtml, raw, renderToString, } from './render.js';
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/static/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EACL,UAAU,EACV,GAAG,EACH,cAAc,GAGf,MAAM,aAAa,CAAC"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Static HTML renderer for Nisli-style templates.
3
+ *
4
+ * This module is intentionally DOM-free. It renders tagged templates to HTML
5
+ * strings for build-time/static use cases, not live browser bindings.
6
+ */
7
+ export interface StaticResult {
8
+ toString(): string;
9
+ __staticResult: true;
10
+ }
11
+ export interface RawHtml {
12
+ value: string;
13
+ __raw: true;
14
+ }
15
+ export declare function staticHtml(strings: TemplateStringsArray, ...values: unknown[]): StaticResult;
16
+ export declare function raw(value: string): RawHtml;
17
+ export declare function renderToString(value: unknown): string;
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Static HTML renderer for Nisli-style templates.
3
+ *
4
+ * This module is intentionally DOM-free. It renders tagged templates to HTML
5
+ * strings for build-time/static use cases, not live browser bindings.
6
+ */
7
+ const ESCAPE_MAP = {
8
+ '&': '&amp;',
9
+ '<': '&lt;',
10
+ '>': '&gt;',
11
+ '"': '&quot;',
12
+ "'": '&#39;',
13
+ };
14
+ function escapeHtml(value) {
15
+ return value.replace(/[&<>"']/g, ch => ESCAPE_MAP[ch] ?? ch);
16
+ }
17
+ function isStaticResult(value) {
18
+ return (typeof value === 'object' &&
19
+ value !== null &&
20
+ '__staticResult' in value &&
21
+ value.__staticResult === true &&
22
+ typeof value.toString === 'function');
23
+ }
24
+ function isRawHtml(value) {
25
+ return (typeof value === 'object' &&
26
+ value !== null &&
27
+ '__raw' in value &&
28
+ value.__raw === true &&
29
+ typeof value.value === 'string');
30
+ }
31
+ export function staticHtml(strings, ...values) {
32
+ const result = [];
33
+ for (let i = 0; i < strings.length; i++) {
34
+ result.push(strings[i] ?? '');
35
+ if (i < values.length) {
36
+ result.push(renderToString(values[i]));
37
+ }
38
+ }
39
+ const output = result.join('');
40
+ return {
41
+ __staticResult: true,
42
+ toString: () => output,
43
+ };
44
+ }
45
+ export function raw(value) {
46
+ return {
47
+ __raw: true,
48
+ value,
49
+ };
50
+ }
51
+ export function renderToString(value) {
52
+ if (value == null || value === false || value === true) {
53
+ return '';
54
+ }
55
+ if (isStaticResult(value)) {
56
+ return value.toString();
57
+ }
58
+ if (isRawHtml(value)) {
59
+ return value.value;
60
+ }
61
+ if (Array.isArray(value)) {
62
+ return value.map(renderToString).join('');
63
+ }
64
+ return escapeHtml(String(value));
65
+ }
66
+ //# sourceMappingURL=render.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.js","sourceRoot":"","sources":["../../src/static/render.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAYH,MAAM,UAAU,GAA2B;IACzC,GAAG,EAAE,OAAO;IACZ,GAAG,EAAE,MAAM;IACX,GAAG,EAAE,MAAM;IACX,GAAG,EAAE,QAAQ;IACb,GAAG,EAAE,OAAO;CACb,CAAC;AAEF,SAAS,UAAU,CAAC,KAAa;IAC/B,OAAO,KAAK,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,EAAE,CAAC,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC;AAC/D,CAAC;AAED,SAAS,cAAc,CAAC,KAAc;IACpC,OAAO,CACL,OAAO,KAAK,KAAK,QAAQ;QACzB,KAAK,KAAK,IAAI;QACd,gBAAgB,IAAI,KAAK;QACxB,KAAsB,CAAC,cAAc,KAAK,IAAI;QAC/C,OAAQ,KAAsB,CAAC,QAAQ,KAAK,UAAU,CACvD,CAAC;AACJ,CAAC;AAED,SAAS,SAAS,CAAC,KAAc;IAC/B,OAAO,CACL,OAAO,KAAK,KAAK,QAAQ;QACzB,KAAK,KAAK,IAAI;QACd,OAAO,IAAI,KAAK;QACf,KAAiB,CAAC,KAAK,KAAK,IAAI;QACjC,OAAQ,KAAiB,CAAC,KAAK,KAAK,QAAQ,CAC7C,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,OAA6B,EAAE,GAAG,MAAiB;IAC5E,MAAM,MAAM,GAAa,EAAE,CAAC;IAE5B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACxC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;QAE9B,IAAI,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC;YACtB,MAAM,CAAC,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACzC,CAAC;IACH,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAE/B,OAAO;QACL,cAAc,EAAE,IAAa;QAC7B,QAAQ,EAAE,GAAG,EAAE,CAAC,MAAM;KACvB,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,GAAG,CAAC,KAAa;IAC/B,OAAO;QACL,KAAK,EAAE,IAAa;QACpB,KAAK;KACN,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,KAAc;IAC3C,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,KAAK,KAAK,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QACvD,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,IAAI,cAAc,CAAC,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO,KAAK,CAAC,QAAQ,EAAE,CAAC;IAC1B,CAAC;IAED,IAAI,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC;QACrB,OAAO,KAAK,CAAC,KAAK,CAAC;IACrB,CAAC;IAED,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC5C,CAAC;IAED,OAAO,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;AACnC,CAAC"}
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "@nisli/core",
3
- "version": "0.46.0",
3
+ "version": "0.47.4",
4
4
  "description": "A reactive web component framework. Signals, templates, dependency injection — no build step, no virtual DOM, no dependencies.",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "repository": {
8
8
  "type": "git",
9
- "url": "https://github.com/gkoreli/backlog-mcp",
10
- "directory": "packages/framework"
9
+ "url": "https://github.com/gkoreli/nisli.git",
10
+ "directory": "packages/core"
11
11
  },
12
12
  "keywords": [
13
13
  "web-components",
@@ -21,6 +21,10 @@
21
21
  ".": {
22
22
  "types": "./dist/index.d.ts",
23
23
  "default": "./dist/index.js"
24
+ },
25
+ "./static": {
26
+ "types": "./dist/static/index.d.ts",
27
+ "default": "./dist/static/index.js"
24
28
  }
25
29
  },
26
30
  "files": [