@adia-ai/web-components 0.5.10 → 0.5.11
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/USAGE.md +29 -0
- package/core/template.js +19 -0
- package/core/template.test.js +97 -0
- package/package.json +1 -1
package/USAGE.md
CHANGED
|
@@ -1057,6 +1057,35 @@ const tpl = html`
|
|
|
1057
1057
|
|
|
1058
1058
|
Same rule applies to nested `<style>` and `<script>` blocks inside `html\`...\`` — anything that contains a literal backtick must be escaped (`\\\``) or rephrased.
|
|
1059
1059
|
|
|
1060
|
+
### §250 — Template parser — invariants + unsupported syntaxes (FEEDBACK-27)
|
|
1061
|
+
|
|
1062
|
+
`html\`\`` is intentionally smaller than `lit-html`. The parser recognizes exactly four placeholder shapes; anything else fails (some loudly, some silently — §250 graduates every documented failure to load-time `console.warn`).
|
|
1063
|
+
|
|
1064
|
+
| Syntax | Behavior | Notes |
|
|
1065
|
+
|---|---|---|
|
|
1066
|
+
| `attr=${val}` (full attribute interpolation) | ✅ Supported. Writes the stringified value to the attribute. | The whole attribute value must be the placeholder — `attr="prefix-${val}"` is **NOT** supported (silent garbage pre-v0.5.3; load-time warn v0.5.3 §152+). |
|
|
1067
|
+
| `.prop=${val}` (property binding) | ✅ Supported. Assigns to the JS property, NOT the DOM attribute. | This is the canonical pattern for boolean state + object values. Custom elements reflect declared `static properties` to DOM attributes automatically — set `.disabled = true` and the host reflects `[disabled]`. |
|
|
1068
|
+
| `@event=${handler}` (event listener) | ✅ Supported. Calls `addEventListener(event, handler)`. | `handler` can be a function or a signal-wrapped function. |
|
|
1069
|
+
| `?attr=${bool}` (Lit-style boolean attribute) | ❌ **NOT supported (load-time `console.warn` v0.5.11 §250).** | Pre-§250: silently registered a literal `?attr` attribute (browser ignored it). v0.5.11 §250 emits a `console.warn` at scan time + degrades to no-op. Use `.attr=${bool}` instead. |
|
|
1070
|
+
|
|
1071
|
+
**Migration for `?attr=${bool}` patterns:**
|
|
1072
|
+
|
|
1073
|
+
```js
|
|
1074
|
+
// ❌ Lit-style boolean attribute — silently inert pre-v0.5.11; warns + no-ops post-§250
|
|
1075
|
+
html`<button-ui ?disabled=${isLoading}>Save</button-ui>`;
|
|
1076
|
+
|
|
1077
|
+
// ✅ Property binding — the primitive reflects the property to [disabled] for you
|
|
1078
|
+
html`<button-ui .disabled=${isLoading}>Save</button-ui>`;
|
|
1079
|
+
```
|
|
1080
|
+
|
|
1081
|
+
Why AdiaUI doesn't implement `?attr=`: custom elements declare their reflective property/attribute mapping via `static properties = { disabled: { type: Boolean, reflect: true } }`. The runtime handles the property-to-attribute reflection automatically. Adding a `?attr=` shape would create two valid syntaxes for the same effect — discoverability tax without behavioral benefit.
|
|
1082
|
+
|
|
1083
|
+
**Other documented parser invariants** (closed in earlier cycles):
|
|
1084
|
+
|
|
1085
|
+
- HTML comments containing apostrophes (`<!-- foo's bar -->`) — fixed at parser level in v0.5.3 §155. Native; no consumer action needed.
|
|
1086
|
+
- HTML comments containing quoted attributes (`<!-- attr="value" -->`) — same fix (v0.5.3 §155).
|
|
1087
|
+
- Backticks inside HTML comments — see §221i above (JS-spec footgun; not a parser bug).
|
|
1088
|
+
|
|
1060
1089
|
### §221j — Typography token cheatsheet
|
|
1061
1090
|
|
|
1062
1091
|
Quick-reference for component-CSS authoring. Cross-reference [`styles/typography.css`](./styles/typography.css):
|
package/core/template.js
CHANGED
|
@@ -175,6 +175,25 @@ function scan(fragment, count) {
|
|
|
175
175
|
} else if (name[0] === '.') {
|
|
176
176
|
n.removeAttribute(name);
|
|
177
177
|
parts[i] = { t: 'p', n, name: name.slice(1), c: undefined, _fx: null };
|
|
178
|
+
} else if (name[0] === '?') {
|
|
179
|
+
// §250 (v0.5.11, FEEDBACK-27): Lit-style boolean attribute syntax
|
|
180
|
+
// (`?attr=${bool}`) is NOT supported. Without this branch, the
|
|
181
|
+
// attribute name registers verbatim including the `?` — silent
|
|
182
|
+
// inert binding. Strip the bogus attribute, warn loudly + degrade
|
|
183
|
+
// to a no-op text-node part so nothing reaches the DOM.
|
|
184
|
+
// Consumers should use `.attr=${bool}` (property binding); AdiaUI
|
|
185
|
+
// primitives reflect properties to DOM attributes automatically.
|
|
186
|
+
// Parallel structural fix to v0.5.3 §152 partial-interpolation warn.
|
|
187
|
+
n.removeAttribute(name);
|
|
188
|
+
// eslint-disable-next-line no-console
|
|
189
|
+
console.warn(
|
|
190
|
+
`[template] Lit-style boolean attribute "${name}=" is not supported.\n` +
|
|
191
|
+
` Element: <${n.tagName.toLowerCase()}>\n` +
|
|
192
|
+
` Use .${name.slice(1)}=\${value} (property binding) instead — ` +
|
|
193
|
+
`the primitive reflects the property to the DOM attribute for you.\n` +
|
|
194
|
+
` See USAGE.md § Template parser — invariants + unsupported syntaxes.`
|
|
195
|
+
);
|
|
196
|
+
parts[i] = { t: 'n', n: document.createTextNode(''), c: undefined, _fx: null };
|
|
178
197
|
} else {
|
|
179
198
|
parts[i] = { t: 'a', n, name, c: undefined, _fx: null };
|
|
180
199
|
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* core/template.js — focused tests for the §250 (v0.5.11) `?attr=${bool}`
|
|
3
|
+
* silent-failure-trap close. Surfaced by FEEDBACK-27 against v0.5.10 source.
|
|
4
|
+
*
|
|
5
|
+
* Pre-§250: `?disabled=${true}` registered a literal `?disabled` DOM
|
|
6
|
+
* attribute with no console warning. Consumer ergonomics gap — Lit-familiar
|
|
7
|
+
* developers reach for the most ergonomic syntax + ship inert bindings.
|
|
8
|
+
*
|
|
9
|
+
* Post-§250: scan-time `console.warn` fires + the bogus part is degraded to
|
|
10
|
+
* a no-op text-node, so nothing reaches the DOM. Migration path is
|
|
11
|
+
* `.attr=${bool}` (property binding) — primitives reflect properties to DOM
|
|
12
|
+
* attributes automatically.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
16
|
+
import { html, stamp } from './template.js';
|
|
17
|
+
|
|
18
|
+
describe('html template — §250 (v0.5.11) ?attr=${bool} silent-failure trap', () => {
|
|
19
|
+
let container;
|
|
20
|
+
let warnSpy;
|
|
21
|
+
|
|
22
|
+
beforeEach(() => {
|
|
23
|
+
container = document.createElement('div');
|
|
24
|
+
document.body.appendChild(container);
|
|
25
|
+
warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
afterEach(() => {
|
|
29
|
+
container.remove();
|
|
30
|
+
warnSpy.mockRestore();
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('fires console.warn at scan time when ?attr=${bool} is encountered', () => {
|
|
34
|
+
const tpl = html`<div ?disabled=${true}></div>`;
|
|
35
|
+
stamp(tpl, container);
|
|
36
|
+
|
|
37
|
+
expect(warnSpy).toHaveBeenCalledTimes(1);
|
|
38
|
+
const msg = warnSpy.mock.calls[0][0];
|
|
39
|
+
expect(msg).toMatch(/Lit-style boolean attribute/);
|
|
40
|
+
expect(msg).toMatch(/\?disabled=/);
|
|
41
|
+
expect(msg).toMatch(/\.disabled=/); // migration path mentioned
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('does NOT register a literal "?attr" attribute on the DOM node', () => {
|
|
45
|
+
const tpl = html`<div ?disabled=${true} data-test="probe"></div>`;
|
|
46
|
+
stamp(tpl, container);
|
|
47
|
+
|
|
48
|
+
const node = container.querySelector('[data-test="probe"]');
|
|
49
|
+
expect(node).not.toBeNull();
|
|
50
|
+
expect(node.getAttribute('?disabled')).toBeNull();
|
|
51
|
+
expect(node.hasAttribute('?disabled')).toBe(false);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('does NOT set the matching DOM attribute either (no Lit-attr semantics)', () => {
|
|
55
|
+
// AdiaUI intentionally does not implement `?attr=${bool}` semantics.
|
|
56
|
+
// The warn + degrade-to-no-op path means truthy `?disabled` does NOT
|
|
57
|
+
// add `[disabled]` to the DOM. Consumer must use `.disabled=` for that.
|
|
58
|
+
const tpl = html`<div ?disabled=${true} data-test="probe"></div>`;
|
|
59
|
+
stamp(tpl, container);
|
|
60
|
+
|
|
61
|
+
const node = container.querySelector('[data-test="probe"]');
|
|
62
|
+
expect(node.hasAttribute('disabled')).toBe(false);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('fires the warn even when the bound value is falsy', () => {
|
|
66
|
+
// Lit's `?attr=${false}` removes the attribute. AdiaUI's contract is
|
|
67
|
+
// "this syntax is not supported, period" — warn fires regardless of
|
|
68
|
+
// truthiness to maximize visibility of the authoring error.
|
|
69
|
+
const tpl = html`<div ?disabled=${false}></div>`;
|
|
70
|
+
stamp(tpl, container);
|
|
71
|
+
|
|
72
|
+
expect(warnSpy).toHaveBeenCalledTimes(1);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('does not warn on canonical .prop=${val} (no false positive)', () => {
|
|
76
|
+
// Make sure the new `?`-prefix branch doesn't accidentally fire on
|
|
77
|
+
// adjacent canonical syntaxes.
|
|
78
|
+
const tpl = html`<div .className=${'x'}></div>`;
|
|
79
|
+
stamp(tpl, container);
|
|
80
|
+
|
|
81
|
+
expect(warnSpy).not.toHaveBeenCalled();
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('does not warn on canonical @event=${handler} (no false positive)', () => {
|
|
85
|
+
const tpl = html`<div @click=${() => {}}></div>`;
|
|
86
|
+
stamp(tpl, container);
|
|
87
|
+
|
|
88
|
+
expect(warnSpy).not.toHaveBeenCalled();
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('does not warn on canonical attr=${value} (no false positive)', () => {
|
|
92
|
+
const tpl = html`<div data-id=${'42'}></div>`;
|
|
93
|
+
stamp(tpl, container);
|
|
94
|
+
|
|
95
|
+
expect(warnSpy).not.toHaveBeenCalled();
|
|
96
|
+
});
|
|
97
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adia-ai/web-components",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.11",
|
|
4
4
|
"description": "AdiaUI web components — vanilla custom elements. A2UI runtime (renderer, registry, streams, wiring) lives in @adia-ai/a2ui-runtime.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"types": "./index.d.ts",
|