@adaas/are-html 0.0.2 → 0.0.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 +4 -4
- package/dist/browser/index.d.mts +88 -5
- package/dist/browser/index.mjs +542 -176
- package/dist/browser/index.mjs.map +1 -1
- package/dist/node/attributes/AreBinding.attribute.js +17 -4
- package/dist/node/attributes/AreBinding.attribute.js.map +1 -1
- package/dist/node/attributes/AreBinding.attribute.mjs +10 -3
- package/dist/node/attributes/AreBinding.attribute.mjs.map +1 -1
- package/dist/node/attributes/AreDirective.attribute.js +17 -4
- package/dist/node/attributes/AreDirective.attribute.js.map +1 -1
- package/dist/node/attributes/AreDirective.attribute.mjs +10 -3
- package/dist/node/attributes/AreDirective.attribute.mjs.map +1 -1
- package/dist/node/attributes/AreEvent.attribute.js +17 -4
- package/dist/node/attributes/AreEvent.attribute.js.map +1 -1
- package/dist/node/attributes/AreEvent.attribute.mjs +10 -3
- package/dist/node/attributes/AreEvent.attribute.mjs.map +1 -1
- package/dist/node/attributes/AreStatic.attribute.js +17 -4
- package/dist/node/attributes/AreStatic.attribute.js.map +1 -1
- package/dist/node/attributes/AreStatic.attribute.mjs +10 -3
- package/dist/node/attributes/AreStatic.attribute.mjs.map +1 -1
- package/dist/node/directives/AreDirectiveFor.directive.d.mts +8 -0
- package/dist/node/directives/AreDirectiveFor.directive.d.ts +8 -0
- package/dist/node/directives/AreDirectiveFor.directive.js +78 -33
- package/dist/node/directives/AreDirectiveFor.directive.js.map +1 -1
- package/dist/node/directives/AreDirectiveFor.directive.mjs +78 -33
- package/dist/node/directives/AreDirectiveFor.directive.mjs.map +1 -1
- package/dist/node/directives/AreDirectiveIf.directive.d.mts +18 -0
- package/dist/node/directives/AreDirectiveIf.directive.d.ts +18 -0
- package/dist/node/directives/AreDirectiveIf.directive.js +10 -3
- package/dist/node/directives/AreDirectiveIf.directive.js.map +1 -1
- package/dist/node/directives/AreDirectiveIf.directive.mjs +10 -3
- package/dist/node/directives/AreDirectiveIf.directive.mjs.map +1 -1
- package/dist/node/engine/AreHTML.compiler.d.mts +2 -2
- package/dist/node/engine/AreHTML.compiler.d.ts +2 -2
- package/dist/node/engine/AreHTML.compiler.js +57 -29
- package/dist/node/engine/AreHTML.compiler.js.map +1 -1
- package/dist/node/engine/AreHTML.compiler.mjs +58 -30
- package/dist/node/engine/AreHTML.compiler.mjs.map +1 -1
- package/dist/node/engine/AreHTML.constants.d.mts +53 -1
- package/dist/node/engine/AreHTML.constants.d.ts +53 -1
- package/dist/node/engine/AreHTML.constants.js +100 -0
- package/dist/node/engine/AreHTML.constants.js.map +1 -1
- package/dist/node/engine/AreHTML.constants.mjs +93 -0
- package/dist/node/engine/AreHTML.constants.mjs.map +1 -1
- package/dist/node/engine/AreHTML.context.d.mts +6 -2
- package/dist/node/engine/AreHTML.context.d.ts +6 -2
- package/dist/node/engine/AreHTML.context.js +42 -7
- package/dist/node/engine/AreHTML.context.js.map +1 -1
- package/dist/node/engine/AreHTML.context.mjs +35 -6
- package/dist/node/engine/AreHTML.context.mjs.map +1 -1
- package/dist/node/engine/AreHTML.engine.js +10 -7
- package/dist/node/engine/AreHTML.engine.js.map +1 -1
- package/dist/node/engine/AreHTML.engine.mjs +10 -7
- package/dist/node/engine/AreHTML.engine.mjs.map +1 -1
- package/dist/node/engine/AreHTML.interpreter.js +155 -43
- package/dist/node/engine/AreHTML.interpreter.js.map +1 -1
- package/dist/node/engine/AreHTML.interpreter.mjs +155 -43
- package/dist/node/engine/AreHTML.interpreter.mjs.map +1 -1
- package/dist/node/engine/AreHTML.lifecycle.js +17 -12
- package/dist/node/engine/AreHTML.lifecycle.js.map +1 -1
- package/dist/node/engine/AreHTML.lifecycle.mjs +9 -2
- package/dist/node/engine/AreHTML.lifecycle.mjs.map +1 -1
- package/dist/node/engine/AreHTML.tokenizer.js +14 -9
- package/dist/node/engine/AreHTML.tokenizer.js.map +1 -1
- package/dist/node/engine/AreHTML.tokenizer.mjs +10 -3
- package/dist/node/engine/AreHTML.tokenizer.mjs.map +1 -1
- package/dist/node/engine/AreHTML.transformer.js +13 -8
- package/dist/node/engine/AreHTML.transformer.js.map +1 -1
- package/dist/node/engine/AreHTML.transformer.mjs +9 -2
- package/dist/node/engine/AreHTML.transformer.mjs.map +1 -1
- package/dist/node/index.d.mts +2 -1
- package/dist/node/index.d.ts +2 -1
- package/dist/node/index.js +3 -3
- package/dist/node/index.mjs +1 -1
- package/dist/node/instructions/AddAttribute.instruction.js +3 -4
- package/dist/node/instructions/AddAttribute.instruction.js.map +1 -1
- package/dist/node/instructions/AddAttribute.instruction.mjs +3 -4
- package/dist/node/instructions/AddAttribute.instruction.mjs.map +1 -1
- package/dist/node/instructions/AddComment.instruction.js +3 -4
- package/dist/node/instructions/AddComment.instruction.js.map +1 -1
- package/dist/node/instructions/AddComment.instruction.mjs +3 -4
- package/dist/node/instructions/AddComment.instruction.mjs.map +1 -1
- package/dist/node/instructions/AddElement.instruction.js +3 -4
- package/dist/node/instructions/AddElement.instruction.js.map +1 -1
- package/dist/node/instructions/AddElement.instruction.mjs +3 -4
- package/dist/node/instructions/AddElement.instruction.mjs.map +1 -1
- package/dist/node/instructions/AddInterpolation.instruction.js +3 -4
- package/dist/node/instructions/AddInterpolation.instruction.js.map +1 -1
- package/dist/node/instructions/AddInterpolation.instruction.mjs +3 -4
- package/dist/node/instructions/AddInterpolation.instruction.mjs.map +1 -1
- package/dist/node/instructions/AddListener.instruction.js +3 -4
- package/dist/node/instructions/AddListener.instruction.js.map +1 -1
- package/dist/node/instructions/AddListener.instruction.mjs +3 -4
- package/dist/node/instructions/AddListener.instruction.mjs.map +1 -1
- package/dist/node/instructions/AddStyle.instruction.js +3 -4
- package/dist/node/instructions/AddStyle.instruction.js.map +1 -1
- package/dist/node/instructions/AddStyle.instruction.mjs +3 -4
- package/dist/node/instructions/AddStyle.instruction.mjs.map +1 -1
- package/dist/node/instructions/AddText.instruction.js +3 -4
- package/dist/node/instructions/AddText.instruction.js.map +1 -1
- package/dist/node/instructions/AddText.instruction.mjs +3 -4
- package/dist/node/instructions/AddText.instruction.mjs.map +1 -1
- package/dist/node/lib/AreDirective/AreDirective.component.js +5 -0
- package/dist/node/lib/AreDirective/AreDirective.component.js.map +1 -1
- package/dist/node/lib/AreDirective/AreDirective.component.mjs +5 -0
- package/dist/node/lib/AreDirective/AreDirective.component.mjs.map +1 -1
- package/dist/node/lib/AreHTMLAttribute/AreHTML.attribute.js +17 -4
- package/dist/node/lib/AreHTMLAttribute/AreHTML.attribute.js.map +1 -1
- package/dist/node/lib/AreHTMLAttribute/AreHTML.attribute.mjs +10 -3
- package/dist/node/lib/AreHTMLAttribute/AreHTML.attribute.mjs.map +1 -1
- package/dist/node/lib/AreHTMLNode/AreHTMLNode.js +3 -4
- package/dist/node/lib/AreHTMLNode/AreHTMLNode.js.map +1 -1
- package/dist/node/lib/AreHTMLNode/AreHTMLNode.mjs +3 -4
- package/dist/node/lib/AreHTMLNode/AreHTMLNode.mjs.map +1 -1
- package/dist/node/lib/AreRoot/AreRoot.component.js +3 -4
- package/dist/node/lib/AreRoot/AreRoot.component.js.map +1 -1
- package/dist/node/lib/AreRoot/AreRoot.component.mjs +3 -4
- package/dist/node/lib/AreRoot/AreRoot.component.mjs.map +1 -1
- package/dist/node/lib/{AreWatcher/AreWatcher.component.d.mts → AreRouteWatcher/AreRouteWatcher.component.d.mts} +2 -2
- package/dist/node/lib/{AreWatcher/AreWatcher.component.d.ts → AreRouteWatcher/AreRouteWatcher.component.d.ts} +2 -2
- package/dist/node/lib/{AreWatcher/AreWatcher.component.js → AreRouteWatcher/AreRouteWatcher.component.js} +9 -10
- package/dist/node/lib/AreRouteWatcher/AreRouteWatcher.component.js.map +1 -0
- package/dist/node/lib/{AreWatcher/AreWatcher.component.mjs → AreRouteWatcher/AreRouteWatcher.component.mjs} +10 -11
- package/dist/node/lib/AreRouteWatcher/AreRouteWatcher.component.mjs.map +1 -0
- package/dist/node/lib/AreStyle/AreStyle.context.js +17 -4
- package/dist/node/lib/AreStyle/AreStyle.context.js.map +1 -1
- package/dist/node/lib/AreStyle/AreStyle.context.mjs +10 -3
- package/dist/node/lib/AreStyle/AreStyle.context.mjs.map +1 -1
- package/dist/node/nodes/AreComment.js +17 -4
- package/dist/node/nodes/AreComment.js.map +1 -1
- package/dist/node/nodes/AreComment.mjs +10 -3
- package/dist/node/nodes/AreComment.mjs.map +1 -1
- package/dist/node/nodes/AreComponent.js +3 -4
- package/dist/node/nodes/AreComponent.js.map +1 -1
- package/dist/node/nodes/AreComponent.mjs +3 -4
- package/dist/node/nodes/AreComponent.mjs.map +1 -1
- package/dist/node/nodes/AreInterpolation.js +17 -4
- package/dist/node/nodes/AreInterpolation.js.map +1 -1
- package/dist/node/nodes/AreInterpolation.mjs +10 -3
- package/dist/node/nodes/AreInterpolation.mjs.map +1 -1
- package/dist/node/nodes/AreRoot.js +3 -4
- package/dist/node/nodes/AreRoot.js.map +1 -1
- package/dist/node/nodes/AreRoot.mjs +3 -4
- package/dist/node/nodes/AreRoot.mjs.map +1 -1
- package/dist/node/nodes/AreText.js +17 -4
- package/dist/node/nodes/AreText.js.map +1 -1
- package/dist/node/nodes/AreText.mjs +10 -3
- package/dist/node/nodes/AreText.mjs.map +1 -1
- package/dist/node/signals/AreRoute.signal.js +18 -5
- package/dist/node/signals/AreRoute.signal.js.map +1 -1
- package/dist/node/signals/AreRoute.signal.mjs +10 -3
- package/dist/node/signals/AreRoute.signal.mjs.map +1 -1
- package/docs/SYNTAX.md +714 -0
- package/docs/arehtml.monaco.json +235 -0
- package/docs/arehtml.monaco.ts +119 -0
- package/examples/dashboard/dist/index.html +1 -1
- package/examples/dashboard/dist/mpioi5ab-8c3oa9.js +13674 -0
- package/examples/jumpstart/dist/index.html +1 -1
- package/examples/{dashboard/dist/mnzfypsd-6zjt7w.js → jumpstart/dist/mor90p6y-0plg7g.js} +1869 -1926
- package/examples/jumpstart/dist/{mnpl1g4i-nobz9g.js → mor90p7p-1898bz.js} +2797 -2282
- package/examples/jumpstart/src/components/List.component.ts +14 -13
- package/examples/jumpstart/src/concept.ts +5 -4
- package/jest.config.ts +1 -1
- package/package.json +10 -6
- package/src/attributes/AreBinding.attribute.ts +5 -0
- package/src/attributes/AreDirective.attribute.ts +5 -0
- package/src/attributes/AreEvent.attribute.ts +5 -0
- package/src/attributes/AreStatic.attribute.ts +5 -0
- package/src/directives/AreDirectiveFor.directive.ts +97 -60
- package/src/directives/AreDirectiveIf.directive.ts +37 -15
- package/src/engine/AreHTML.compiler.ts +64 -36
- package/src/engine/AreHTML.constants.ts +144 -0
- package/src/engine/AreHTML.context.ts +33 -4
- package/src/engine/AreHTML.engine.ts +12 -7
- package/src/engine/AreHTML.interpreter.ts +195 -68
- package/src/engine/AreHTML.lifecycle.ts +5 -0
- package/src/engine/AreHTML.tokenizer.ts +6 -1
- package/src/engine/AreHTML.transformer.ts +5 -0
- package/src/index.ts +2 -2
- package/src/instructions/AddAttribute.instruction.ts +3 -4
- package/src/instructions/AddComment.instruction.ts +3 -4
- package/src/instructions/AddElement.instruction.ts +3 -4
- package/src/instructions/AddInterpolation.instruction.ts +3 -4
- package/src/instructions/AddListener.instruction.ts +3 -4
- package/src/instructions/AddStyle.instruction.ts +3 -4
- package/src/instructions/AddText.instruction.ts +3 -4
- package/src/lib/AreDirective/AreDirective.component.ts +5 -0
- package/src/lib/AreHTMLAttribute/AreHTML.attribute.ts +5 -0
- package/src/lib/AreHTMLNode/AreHTMLNode.ts +3 -4
- package/src/lib/AreRoot/AreRoot.component.ts +3 -4
- package/src/lib/{AreWatcher/AreWatcher.component.ts → AreRouteWatcher/AreRouteWatcher.component.ts} +5 -6
- package/src/lib/AreStyle/AreStyle.context.ts +5 -0
- package/src/nodes/AreComment.ts +5 -0
- package/src/nodes/AreComponent.ts +3 -4
- package/src/nodes/AreInterpolation.ts +5 -0
- package/src/nodes/AreRoot.ts +3 -4
- package/src/nodes/AreText.ts +5 -0
- package/src/signals/AreRoute.signal.ts +5 -0
- package/dist/node/lib/AreWatcher/AreWatcher.component.js.map +0 -1
- package/dist/node/lib/AreWatcher/AreWatcher.component.mjs.map +0 -1
package/docs/SYNTAX.md
ADDED
|
@@ -0,0 +1,714 @@
|
|
|
1
|
+
# `@adaas/are-html` — Template Syntax Reference
|
|
2
|
+
|
|
3
|
+
A complete reference for the template syntax understood by the **ARE‑HTML**
|
|
4
|
+
engine. This document is the source of truth for building syntax highlighting,
|
|
5
|
+
linting, and editor tooling (Monaco / VS Code TextMate / Tree-sitter / etc.).
|
|
6
|
+
|
|
7
|
+
> Conventions in this document
|
|
8
|
+
>
|
|
9
|
+
> - `<x>` denotes a placeholder (you write something there).
|
|
10
|
+
> - Code blocks tagged ` ```html ` are template fragments.
|
|
11
|
+
> - Code blocks tagged ` ```ts ` are companion JavaScript/TypeScript code.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## 1. Big picture
|
|
16
|
+
|
|
17
|
+
ARE‑HTML templates look like HTML, but element attributes can carry one of
|
|
18
|
+
**four sigils** that alter their meaning:
|
|
19
|
+
|
|
20
|
+
| Sigil | Attribute kind | Example | Purpose |
|
|
21
|
+
| :---: | ---------------- | --------------------------- | ------------------------------------ |
|
|
22
|
+
| _none_ | **Static** | `class="menu-item"` | Plain HTML attribute |
|
|
23
|
+
| `:` | **Binding** | `:value="user.name"` | Reactive expression → attribute/prop |
|
|
24
|
+
| `@` | **Event** | `@click="$save"` | DOM event listener |
|
|
25
|
+
| `$` | **Directive** | `$if="visible"` | Structural / behavioral directive |
|
|
26
|
+
|
|
27
|
+
Inside text, `{{ … }}` is a **reactive interpolation**.
|
|
28
|
+
|
|
29
|
+
### 1.1 Lexical grammar (per-attribute)
|
|
30
|
+
|
|
31
|
+
Tokenizer regex used by the engine:
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
ATTR ::= ([$:@]?[\w.-]+) ( = "..." | = '...' | = bare )?
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Effectively:
|
|
38
|
+
|
|
39
|
+
| Pattern | Token kind |
|
|
40
|
+
| --------------------------- | ----------- |
|
|
41
|
+
| `$<name>` | Directive |
|
|
42
|
+
| `:<name>` | Binding |
|
|
43
|
+
| `@<name>(.<modifier>)*` | Event |
|
|
44
|
+
| `<name>` (`[\w.-]+`) | Static attr |
|
|
45
|
+
|
|
46
|
+
Attribute **values** are always JS-like expressions when carried by `:`, `@`,
|
|
47
|
+
`$` or `{{ … }}`. Expressions are evaluated in a sandbox over the component's
|
|
48
|
+
`AreStore`, so any property you `store.set(…)` is in scope as a top-level
|
|
49
|
+
identifier.
|
|
50
|
+
|
|
51
|
+
### 1.2 File structure of a component
|
|
52
|
+
|
|
53
|
+
A component is a TypeScript class extending `Are`. Templates live in the
|
|
54
|
+
decorated `template()` method:
|
|
55
|
+
|
|
56
|
+
```ts
|
|
57
|
+
import { A_Caller, A_Inject } from "@adaas/a-concept";
|
|
58
|
+
import { Are, AreNode, AreStore } from "@adaas/are";
|
|
59
|
+
|
|
60
|
+
export class HelloComponent extends Are {
|
|
61
|
+
|
|
62
|
+
@Are.Template
|
|
63
|
+
async template(@A_Inject(A_Caller) node: AreNode) {
|
|
64
|
+
node.setContent(`
|
|
65
|
+
<div class="hello">
|
|
66
|
+
Hello, {{name}}!
|
|
67
|
+
<button @click="$greet">Greet</button>
|
|
68
|
+
</div>
|
|
69
|
+
`);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
@Are.Data
|
|
73
|
+
async data(@A_Inject(AreStore) store: AreStore) {
|
|
74
|
+
store.set({ name: "World" });
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
@Are.EventHandler
|
|
78
|
+
async greet(@A_Inject(AreStore) store: AreStore) {
|
|
79
|
+
store.set("name", "ARE");
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## 2. Interpolations — `{{ expression }}`
|
|
87
|
+
|
|
88
|
+
Reactive text expressions inside text content (NOT inside attribute values).
|
|
89
|
+
|
|
90
|
+
```html
|
|
91
|
+
<span>Hello, {{ user.name }}!</span>
|
|
92
|
+
<span>Total: {{ items.length }}</span>
|
|
93
|
+
<span>{{ count > 0 ? 'has' : 'none' }}</span>
|
|
94
|
+
<span>{{ a + b * 2 }}</span>
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Rules:
|
|
98
|
+
|
|
99
|
+
1. Whitespace inside `{{ }}` is ignored.
|
|
100
|
+
2. The expression is JS-like; any property in the component's store is a
|
|
101
|
+
top-level identifier.
|
|
102
|
+
3. Result is coerced to a DOM string:
|
|
103
|
+
- `null` / `undefined` → `""` (empty string, not the literal text).
|
|
104
|
+
- `object` → `JSON.stringify(value)`.
|
|
105
|
+
- everything else → `String(value)`.
|
|
106
|
+
4. Re-evaluated automatically when any dependency in the store changes.
|
|
107
|
+
5. **Not allowed in attributes.** Use `:attr="expr"` instead.
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## 3. Static attributes (no sigil)
|
|
112
|
+
|
|
113
|
+
Plain HTML attributes. Value is a literal string.
|
|
114
|
+
|
|
115
|
+
```html
|
|
116
|
+
<div class="card primary" id="main" data-role="container"></div>
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## 4. Bindings — `:<attribute>`
|
|
122
|
+
|
|
123
|
+
Reactive attribute / property bindings. The right-hand side is an **expression**.
|
|
124
|
+
|
|
125
|
+
```html
|
|
126
|
+
<input :value="user.email" :disabled="loading">
|
|
127
|
+
<a :href="'/users/' + user.id">Profile</a>
|
|
128
|
+
<img :src="avatar || '/default.png'" :alt="user.name">
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### 4.1 Property name matching
|
|
132
|
+
|
|
133
|
+
The framework looks up the right destination key on the child component
|
|
134
|
+
using both **kebab-case** and **camelCase**:
|
|
135
|
+
|
|
136
|
+
```html
|
|
137
|
+
<my-card :user-name="user.name"></my-card>
|
|
138
|
+
<!-- writes either `user-name` or `userName` into the child store, whichever exists -->
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### 4.2 Boolean attributes
|
|
142
|
+
|
|
143
|
+
For boolean HTML attributes (`disabled`, `checked`, `readonly`, `required`,
|
|
144
|
+
`hidden`, `multiple`, `selected`, `open`, `autofocus`, `autoplay`, `controls`,
|
|
145
|
+
`loop`, `muted`, `default`, `defer`, `async`, `nomodule`, `novalidate`,
|
|
146
|
+
`reversed`, `ismap`, `formnovalidate`, `playsinline`, `itemscope`, `truespeed`,
|
|
147
|
+
`typemustmatch`, `inert`, `allowfullscreen`):
|
|
148
|
+
|
|
149
|
+
```html
|
|
150
|
+
<button :disabled="loading"></button>
|
|
151
|
+
<input :readonly="locked">
|
|
152
|
+
<input :checked="agreed" type="checkbox">
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
- Truthy → attribute is present (and corresponding IDL property set to `true`).
|
|
156
|
+
- Falsy → attribute is removed (IDL property `false`).
|
|
157
|
+
|
|
158
|
+
### 4.3 IDL form properties
|
|
159
|
+
|
|
160
|
+
For form inputs, the engine uses the **IDL property** (not `setAttribute`)
|
|
161
|
+
when binding:
|
|
162
|
+
|
|
163
|
+
| Element / attribute | Set as IDL property |
|
|
164
|
+
| ------------------------------------- | ------------------- |
|
|
165
|
+
| `<input :value="…">` | `input.value` |
|
|
166
|
+
| `<input :checked="…">` | `input.checked` |
|
|
167
|
+
| `<select :value="…">` | `select.value` |
|
|
168
|
+
| `<textarea :value="…">` | `textarea.value` |
|
|
169
|
+
| `<option :selected="…">` | `option.selected` |
|
|
170
|
+
|
|
171
|
+
This means `:value` round-trips correctly even after the user types into the
|
|
172
|
+
input.
|
|
173
|
+
|
|
174
|
+
### 4.4 `:class` (string, array, or object)
|
|
175
|
+
|
|
176
|
+
```html
|
|
177
|
+
<!-- string -->
|
|
178
|
+
<div :class="active ? 'on' : 'off'">…</div>
|
|
179
|
+
|
|
180
|
+
<!-- array -->
|
|
181
|
+
<div :class="['btn', kind, isActive && 'btn-active']">…</div>
|
|
182
|
+
|
|
183
|
+
<!-- object: keys whose value is truthy are added -->
|
|
184
|
+
<div :class="{ 'btn': true, 'btn-primary': primary, 'btn-disabled': loading }">…</div>
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
`:class` is **merged** with any static `class="…"` on the same element — both
|
|
188
|
+
are preserved.
|
|
189
|
+
|
|
190
|
+
### 4.5 `:style` (string or object)
|
|
191
|
+
|
|
192
|
+
```html
|
|
193
|
+
<!-- string (raw CSS) -->
|
|
194
|
+
<div :style="'color:' + color + ';font-size:14px'">…</div>
|
|
195
|
+
|
|
196
|
+
<!-- object: camelCase or kebab-case keys; numbers get 'px' for length-like keys -->
|
|
197
|
+
<div :style="{ color: textColor, fontSize: '14px', marginTop: 8 }">…</div>
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### 4.6 Other attributes
|
|
201
|
+
|
|
202
|
+
For everything else, the value is set with `setAttribute`. If the expression
|
|
203
|
+
evaluates to `false`, `null`, or `undefined`, the attribute is **removed**.
|
|
204
|
+
|
|
205
|
+
```html
|
|
206
|
+
<a :title="hover">…</a>
|
|
207
|
+
<img :alt="caption">
|
|
208
|
+
<div :data-id="row.id">…</div>
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
---
|
|
212
|
+
|
|
213
|
+
## 5. Events — `@<event>[.<modifier>…]`
|
|
214
|
+
|
|
215
|
+
Bind a DOM event to an `@Are.EventHandler` method on the component, or to an
|
|
216
|
+
inline expression.
|
|
217
|
+
|
|
218
|
+
### 5.1 Reference an event-handler method
|
|
219
|
+
|
|
220
|
+
When the value starts with `$<name>` it refers to an `@Are.EventHandler`
|
|
221
|
+
method named `<name>`:
|
|
222
|
+
|
|
223
|
+
```html
|
|
224
|
+
<button @click="$save">Save</button>
|
|
225
|
+
<button @click="$save(item, $event)">Save with args</button>
|
|
226
|
+
<form @submit="$onSubmit($event)">…</form>
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
- Always available in the handler argument scope: `$event` (the DOM event).
|
|
230
|
+
- If no arguments are passed, the DOM event is appended automatically.
|
|
231
|
+
- Multiple handlers per call are allowed: `@click="$a(); $b($event)"`.
|
|
232
|
+
|
|
233
|
+
### 5.2 Inline expressions
|
|
234
|
+
|
|
235
|
+
Any expression is allowed; it runs each time the event fires:
|
|
236
|
+
|
|
237
|
+
```html
|
|
238
|
+
<button @click="count = count + 1">+1</button>
|
|
239
|
+
<a @click="open = !open">Toggle</a>
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
### 5.3 Event modifiers
|
|
243
|
+
|
|
244
|
+
Append one or more `.<modifier>` to the event name:
|
|
245
|
+
|
|
246
|
+
| Modifier | Effect |
|
|
247
|
+
| --------- | ----------------------------------------------------------- |
|
|
248
|
+
| `.stop` | `event.stopPropagation()` before handler runs |
|
|
249
|
+
| `.prevent`| `event.preventDefault()` before handler runs |
|
|
250
|
+
| `.self` | Only fire when `event.target === element` |
|
|
251
|
+
| `.capture`| `addEventListener(…, { capture: true })` |
|
|
252
|
+
| `.once` | `addEventListener(…, { once: true })` |
|
|
253
|
+
| `.passive`| `addEventListener(…, { passive: true })` |
|
|
254
|
+
|
|
255
|
+
Modifiers can be combined in any order:
|
|
256
|
+
|
|
257
|
+
```html
|
|
258
|
+
<a @click.stop.prevent="$open">Open</a>
|
|
259
|
+
<form @submit.prevent="$submit">…</form>
|
|
260
|
+
<div @scroll.passive="$onScroll">…</div>
|
|
261
|
+
<div @click.self="$onSelfOnly">…</div>
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
### 5.4 Key modifiers (keyboard events)
|
|
265
|
+
|
|
266
|
+
For `keydown` / `keyup` / `keypress`, the following keys can be used as
|
|
267
|
+
modifiers — handler only fires when the matching key is pressed:
|
|
268
|
+
|
|
269
|
+
| Modifier | Matches `event.key` |
|
|
270
|
+
| -------------------- | ------------------------------------ |
|
|
271
|
+
| `.enter` | `Enter` |
|
|
272
|
+
| `.esc` / `.escape` | `Escape` |
|
|
273
|
+
| `.tab` | `Tab` |
|
|
274
|
+
| `.space` | `' '` (Space) |
|
|
275
|
+
| `.delete` / `.backspace` | `Delete` / `Backspace` |
|
|
276
|
+
| `.up` | `ArrowUp` |
|
|
277
|
+
| `.down` | `ArrowDown` |
|
|
278
|
+
| `.left` | `ArrowLeft` |
|
|
279
|
+
| `.right` | `ArrowRight` |
|
|
280
|
+
|
|
281
|
+
Combined with system-key modifiers:
|
|
282
|
+
|
|
283
|
+
| Modifier | Matches |
|
|
284
|
+
| --------- | ------- |
|
|
285
|
+
| `.ctrl` | `event.ctrlKey` must be true |
|
|
286
|
+
| `.alt` | `event.altKey` must be true |
|
|
287
|
+
| `.shift` | `event.shiftKey` must be true |
|
|
288
|
+
| `.meta` | `event.metaKey` must be true |
|
|
289
|
+
|
|
290
|
+
Examples:
|
|
291
|
+
|
|
292
|
+
```html
|
|
293
|
+
<input @keydown.enter="$submit">
|
|
294
|
+
<input @keydown.esc="$cancel">
|
|
295
|
+
<input @keydown.ctrl.enter="$saveDraft">
|
|
296
|
+
<div @keydown.up.prevent="$moveUp">
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
### 5.5 The `$event` argument
|
|
300
|
+
|
|
301
|
+
Always available as the variable `$event` inside the handler call:
|
|
302
|
+
|
|
303
|
+
```html
|
|
304
|
+
<button @click="$save($event, row)">Save</button>
|
|
305
|
+
<input @input="$onInput($event.target.value)">
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
---
|
|
309
|
+
|
|
310
|
+
## 6. Directives — `$<name>`
|
|
311
|
+
|
|
312
|
+
Structural / behavioral attributes. They have **higher priority** than other
|
|
313
|
+
attributes on the same element.
|
|
314
|
+
|
|
315
|
+
### 6.1 `$if="<expression>"`
|
|
316
|
+
|
|
317
|
+
Conditionally render an element subtree.
|
|
318
|
+
|
|
319
|
+
```html
|
|
320
|
+
<div $if="user.isAdmin">Admin only content</div>
|
|
321
|
+
<span $if="badge > 0" class="menu-badge">{{badge}}</span>
|
|
322
|
+
<p $if="!loading && error">{{error}}</p>
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
- Re-evaluated reactively.
|
|
326
|
+
- Truthy ↔ falsy transitions mount / unmount the subtree.
|
|
327
|
+
- Identical truthiness across updates is a **no-op** (no remount).
|
|
328
|
+
|
|
329
|
+
> ⚠️ Do **not** combine `$if` and `$for` on the **same** element. Wrap one in a
|
|
330
|
+
> parent:
|
|
331
|
+
>
|
|
332
|
+
> ```html
|
|
333
|
+
> <!-- ✅ Right -->
|
|
334
|
+
> <div $if="active === 'list'">
|
|
335
|
+
> <li $for="item in items">{{item.name}}</li>
|
|
336
|
+
> </div>
|
|
337
|
+
> ```
|
|
338
|
+
|
|
339
|
+
### 6.2 `$for="<binding> in <source> [track <key>]"`
|
|
340
|
+
|
|
341
|
+
Render a node once per item. The binding is one of:
|
|
342
|
+
|
|
343
|
+
```html
|
|
344
|
+
<li $for="item in items">…</li>
|
|
345
|
+
<li $for="item, i in items">…</li>
|
|
346
|
+
<li $for="(item, i) in items">…</li>
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
- `item` — current value
|
|
350
|
+
- `i` — current index (defaults to `index` when omitted)
|
|
351
|
+
- `items` — any expression returning an array
|
|
352
|
+
|
|
353
|
+
#### Stable identity: `track <expr>`
|
|
354
|
+
|
|
355
|
+
For correct reuse across reorders / removals, supply a `track` clause whose
|
|
356
|
+
expression yields a unique key per item. Dotted paths are supported:
|
|
357
|
+
|
|
358
|
+
```html
|
|
359
|
+
<li $for="row in rows track row.id">{{row.name}}</li>
|
|
360
|
+
<li $for="(item, i) in items track item.uid">{{item.title}}</li>
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
When `track` is omitted the engine falls back to identity by **reference**
|
|
364
|
+
(`===`). Use `track` whenever items can be re-created (e.g. after a
|
|
365
|
+
`store.set('items', [...next])`).
|
|
366
|
+
|
|
367
|
+
#### Filtering helper
|
|
368
|
+
|
|
369
|
+
Inline filtering is supported via a `filter(<source>, <key>)` helper:
|
|
370
|
+
|
|
371
|
+
```html
|
|
372
|
+
<li $for="item in filter(items, 'active') track item.id">{{item.name}}</li>
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
Inside `filter`, items are kept where the named property is truthy.
|
|
376
|
+
|
|
377
|
+
### 6.3 `$<custom>`
|
|
378
|
+
|
|
379
|
+
Any custom directive registered with the engine appears with the `$` prefix:
|
|
380
|
+
|
|
381
|
+
```html
|
|
382
|
+
<input $autofocus>
|
|
383
|
+
<div $tooltip="'Click to edit'">…</div>
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
Custom directives implement `transform`, `compile`, and `update` lifecycles
|
|
387
|
+
on a `AreDirective` subclass. See `src/directives/AreDirectiveFor.directive.ts`
|
|
388
|
+
for a reference implementation.
|
|
389
|
+
|
|
390
|
+
---
|
|
391
|
+
|
|
392
|
+
## 7. Built-in scope keywords
|
|
393
|
+
|
|
394
|
+
These identifiers always exist when an expression is evaluated:
|
|
395
|
+
|
|
396
|
+
| Identifier | Where | Meaning |
|
|
397
|
+
| ----------- | ------------- | ---------------------------------------- |
|
|
398
|
+
| `$event` | `@event=…` | The DOM event object |
|
|
399
|
+
| `item` / `<your-key>` | `$for=…` | The current iteration value |
|
|
400
|
+
| `index` / `<your-index>` | `$for=…` | The current iteration index |
|
|
401
|
+
| store keys | everywhere | Anything `store.set(<key>, …)` registers |
|
|
402
|
+
|
|
403
|
+
Component instance methods decorated with `@Are.EventHandler` are exposed
|
|
404
|
+
under the `$` prefix in event expressions: `@click="$save"` calls `save()`.
|
|
405
|
+
|
|
406
|
+
---
|
|
407
|
+
|
|
408
|
+
## 8. Putting it together — full example
|
|
409
|
+
|
|
410
|
+
```html
|
|
411
|
+
<div class="dashboard" :class="{ loading: loading }">
|
|
412
|
+
|
|
413
|
+
<h1>Hello, {{ user.name }}!</h1>
|
|
414
|
+
|
|
415
|
+
<button :disabled="loading"
|
|
416
|
+
@click.prevent="$refresh">
|
|
417
|
+
Refresh
|
|
418
|
+
</button>
|
|
419
|
+
|
|
420
|
+
<input :value="filter"
|
|
421
|
+
@input="$onFilter($event.target.value)"
|
|
422
|
+
@keydown.enter="$apply"
|
|
423
|
+
@keydown.esc="$reset">
|
|
424
|
+
|
|
425
|
+
<div $if="error" class="error">{{ error }}</div>
|
|
426
|
+
|
|
427
|
+
<ul $if="!loading">
|
|
428
|
+
<li $for="user in filter(users, 'active') track user.id"
|
|
429
|
+
:class="{ selected: user.id === selectedId }"
|
|
430
|
+
@click="$select(user)">
|
|
431
|
+
|
|
432
|
+
<span class="name">{{ user.name }}</span>
|
|
433
|
+
<span $if="user.unread > 0" class="badge">{{ user.unread }}</span>
|
|
434
|
+
<button @click.stop="$remove(user)">×</button>
|
|
435
|
+
</li>
|
|
436
|
+
</ul>
|
|
437
|
+
</div>
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
---
|
|
441
|
+
|
|
442
|
+
## 9. Cheat-sheet (one-liner per case)
|
|
443
|
+
|
|
444
|
+
```html
|
|
445
|
+
<!-- 1. Static attribute -->
|
|
446
|
+
<div class="card" id="x" data-x="1"></div>
|
|
447
|
+
|
|
448
|
+
<!-- 2. Interpolation in text -->
|
|
449
|
+
<span>{{ value }}</span>
|
|
450
|
+
|
|
451
|
+
<!-- 3. Reactive attribute binding -->
|
|
452
|
+
<a :href="url"></a>
|
|
453
|
+
|
|
454
|
+
<!-- 4. Boolean attribute -->
|
|
455
|
+
<button :disabled="loading"></button>
|
|
456
|
+
|
|
457
|
+
<!-- 5. IDL form property -->
|
|
458
|
+
<input :value="email" :checked="agreed" type="checkbox">
|
|
459
|
+
|
|
460
|
+
<!-- 6. :class merge — string / array / object -->
|
|
461
|
+
<div :class="active ? 'on' : 'off'"></div>
|
|
462
|
+
<div :class="['btn', kind]"></div>
|
|
463
|
+
<div :class="{ on: active, big: large }"></div>
|
|
464
|
+
|
|
465
|
+
<!-- 7. :style — string or object -->
|
|
466
|
+
<div :style="'color:' + color"></div>
|
|
467
|
+
<div :style="{ color, fontSize: 14 }"></div>
|
|
468
|
+
|
|
469
|
+
<!-- 8. Custom prop binding (kebab/camel both work) -->
|
|
470
|
+
<my-card :user-name="user.name"></my-card>
|
|
471
|
+
|
|
472
|
+
<!-- 9. Event — handler reference -->
|
|
473
|
+
<button @click="$save"></button>
|
|
474
|
+
|
|
475
|
+
<!-- 10. Event — handler call with args -->
|
|
476
|
+
<button @click="$save(item, $event)"></button>
|
|
477
|
+
|
|
478
|
+
<!-- 11. Event — inline expression -->
|
|
479
|
+
<button @click="count = count + 1"></button>
|
|
480
|
+
|
|
481
|
+
<!-- 12. Event modifiers -->
|
|
482
|
+
<a @click.stop.prevent="$open"></a>
|
|
483
|
+
<form @submit.prevent="$submit"></form>
|
|
484
|
+
<div @scroll.passive="$onScroll"></div>
|
|
485
|
+
<div @click.self="$onSelf"></div>
|
|
486
|
+
<a @click.once="$track"></a>
|
|
487
|
+
<div @click.capture="$capture"></div>
|
|
488
|
+
|
|
489
|
+
<!-- 13. Key modifiers -->
|
|
490
|
+
<input @keydown.enter="$submit">
|
|
491
|
+
<input @keydown.esc="$cancel">
|
|
492
|
+
<input @keydown.ctrl.enter="$saveDraft">
|
|
493
|
+
|
|
494
|
+
<!-- 14. $if -->
|
|
495
|
+
<div $if="visible"></div>
|
|
496
|
+
|
|
497
|
+
<!-- 15. $for, with index, with parens -->
|
|
498
|
+
<li $for="item in items">{{item}}</li>
|
|
499
|
+
<li $for="item, i in items">{{i}}: {{item}}</li>
|
|
500
|
+
<li $for="(item, i) in items">{{i}}: {{item}}</li>
|
|
501
|
+
|
|
502
|
+
<!-- 16. $for with track -->
|
|
503
|
+
<li $for="row in rows track row.id">{{row.name}}</li>
|
|
504
|
+
|
|
505
|
+
<!-- 17. $for with inline filter -->
|
|
506
|
+
<li $for="u in filter(users, 'active') track u.id">{{u.name}}</li>
|
|
507
|
+
|
|
508
|
+
<!-- 18. $if + $for (must be on different elements!) -->
|
|
509
|
+
<div $if="show">
|
|
510
|
+
<li $for="x in xs track x.id">{{x.name}}</li>
|
|
511
|
+
</div>
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
---
|
|
515
|
+
|
|
516
|
+
## 10. Tooling reference (for Monaco / TextMate / Tree-sitter)
|
|
517
|
+
|
|
518
|
+
This section enumerates **every token kind** in a machine-friendly form so you
|
|
519
|
+
can wire up syntax highlighting.
|
|
520
|
+
|
|
521
|
+
### 10.1 Token kinds
|
|
522
|
+
|
|
523
|
+
| Kind | Pattern (regex, JS flavor) | Notes |
|
|
524
|
+
| ------------------- | --------------------------------------------------------------- | ----- |
|
|
525
|
+
| `tag.open` | `<[a-zA-Z][\w-]*` | Opening tag start |
|
|
526
|
+
| `tag.close` | `</[a-zA-Z][\w-]*\s*>` | Closing tag |
|
|
527
|
+
| `tag.selfClose` | `/>` | End of self-closing tag |
|
|
528
|
+
| `tag.end` | `>` | End of opening tag |
|
|
529
|
+
| `attr.static` | `\b[\w-]+(?=\s*=\|\s\|>\|/)` | Plain attribute name |
|
|
530
|
+
| `attr.binding` | `:[\w-]+` | `:value`, `:class`, `:user-name`, … |
|
|
531
|
+
| `attr.event` | `@[\w-]+(\.[\w-]+)*` | `@click`, `@keydown.enter`, `@click.stop.prevent` |
|
|
532
|
+
| `attr.directive` | `\$[\w-]+` | `$if`, `$for`, `$tooltip`, … |
|
|
533
|
+
| `attr.assign` | `=` | Attribute assignment |
|
|
534
|
+
| `attr.value.dq` | `"[^"]*"` | Double-quoted value (expression for `:`,`@`,`$`) |
|
|
535
|
+
| `attr.value.sq` | `'[^']*'` | Single-quoted value |
|
|
536
|
+
| `attr.value.bare` | `[^\s>/"'=]+` | Unquoted value |
|
|
537
|
+
| `interp.delimiter` | `\{\{` … `\}\}` | Interpolation start/end |
|
|
538
|
+
| `interp.expression` | content between `{{` and `}}` | JS-like expression |
|
|
539
|
+
| `comment.html` | `<!--` … `-->` | HTML comment |
|
|
540
|
+
| `text` | anything else | Plain text |
|
|
541
|
+
|
|
542
|
+
### 10.2 Reserved attribute names
|
|
543
|
+
|
|
544
|
+
Highlight these as **directive keywords** when prefixed with `$`:
|
|
545
|
+
|
|
546
|
+
```
|
|
547
|
+
if for else else-if show hide model ref slot key
|
|
548
|
+
```
|
|
549
|
+
|
|
550
|
+
> Currently shipped: `$if`, `$for`. The rest are reserved for future built-ins.
|
|
551
|
+
|
|
552
|
+
### 10.3 Reserved event modifiers
|
|
553
|
+
|
|
554
|
+
Highlight these as **modifier keywords** when following a `.` after an event
|
|
555
|
+
name:
|
|
556
|
+
|
|
557
|
+
```
|
|
558
|
+
stop prevent self capture once passive
|
|
559
|
+
enter esc escape tab space delete backspace
|
|
560
|
+
up down left right
|
|
561
|
+
ctrl alt shift meta
|
|
562
|
+
```
|
|
563
|
+
|
|
564
|
+
### 10.4 Inside expression values
|
|
565
|
+
|
|
566
|
+
Inside the value of `:`, `@`, `$`, and `{{ … }}`, the content is a
|
|
567
|
+
**JS-like expression**:
|
|
568
|
+
|
|
569
|
+
- Identifiers: `[A-Za-z_$][\w$]*`
|
|
570
|
+
- Member access: `\.`, `?.`
|
|
571
|
+
- Numbers: `\d+(\.\d+)?`
|
|
572
|
+
- Strings: `'…'` and `"…"` with escapes
|
|
573
|
+
- Operators: `+ - * / % == != === !== < > <= >= && || ?? ! ?:`
|
|
574
|
+
- Punctuation: `( ) [ ] , ; { }`
|
|
575
|
+
- Reserved literals: `true`, `false`, `null`, `undefined`
|
|
576
|
+
|
|
577
|
+
Special identifiers worth highlighting:
|
|
578
|
+
|
|
579
|
+
| Identifier | Where | As |
|
|
580
|
+
| ---------- | -------------------- | ------------------------------- |
|
|
581
|
+
| `$event` | event expressions | `variable.special` |
|
|
582
|
+
| `$<name>` | event expressions | `function.handler` (calls method) |
|
|
583
|
+
| `track` | `$for` value | `keyword.control` (only after `in <expr>`) |
|
|
584
|
+
| `in` | `$for` value | `keyword.control` |
|
|
585
|
+
| `filter` | `$for` value | `support.function` |
|
|
586
|
+
|
|
587
|
+
### 10.5 Suggested Monaco scope mapping (Monarch)
|
|
588
|
+
|
|
589
|
+
Drop-in starter for a Monaco Monarch language definition:
|
|
590
|
+
|
|
591
|
+
```js
|
|
592
|
+
export const areHtmlLanguage = {
|
|
593
|
+
defaultToken: '',
|
|
594
|
+
tokenPostfix: '.arehtml',
|
|
595
|
+
|
|
596
|
+
keywords: ['if', 'for', 'else', 'else-if', 'show', 'hide', 'model', 'ref', 'slot', 'key'],
|
|
597
|
+
|
|
598
|
+
eventModifiers: [
|
|
599
|
+
'stop','prevent','self','capture','once','passive',
|
|
600
|
+
'enter','esc','escape','tab','space','delete','backspace',
|
|
601
|
+
'up','down','left','right',
|
|
602
|
+
'ctrl','alt','shift','meta'
|
|
603
|
+
],
|
|
604
|
+
|
|
605
|
+
tokenizer: {
|
|
606
|
+
root: [
|
|
607
|
+
[/<!--/, 'comment', '@comment'],
|
|
608
|
+
[/\{\{/, { token: 'delimiter.interpolation', next: '@interp' }],
|
|
609
|
+
[/<\/[a-zA-Z][\w-]*\s*>/, 'tag'],
|
|
610
|
+
[/<[a-zA-Z][\w-]*/, { token: 'tag', next: '@tagAttrs' }],
|
|
611
|
+
[/[^<{]+/, ''],
|
|
612
|
+
],
|
|
613
|
+
|
|
614
|
+
comment: [
|
|
615
|
+
[/-->/, 'comment', '@pop'],
|
|
616
|
+
[/[^-]+/, 'comment'],
|
|
617
|
+
[/./, 'comment'],
|
|
618
|
+
],
|
|
619
|
+
|
|
620
|
+
interp: [
|
|
621
|
+
[/\}\}/, { token: 'delimiter.interpolation', next: '@pop' }],
|
|
622
|
+
{ include: '@expr' },
|
|
623
|
+
],
|
|
624
|
+
|
|
625
|
+
tagAttrs: [
|
|
626
|
+
[/\s+/, ''],
|
|
627
|
+
[/\/?>/, { token: 'tag', next: '@pop' }],
|
|
628
|
+
|
|
629
|
+
// directive
|
|
630
|
+
[/\$[\w-]+/, {
|
|
631
|
+
cases: {
|
|
632
|
+
'$if|$for': 'keyword.directive.builtin',
|
|
633
|
+
'@default': 'keyword.directive'
|
|
634
|
+
}
|
|
635
|
+
}],
|
|
636
|
+
|
|
637
|
+
// binding
|
|
638
|
+
[/:[\w-]+/, 'attribute.name.binding'],
|
|
639
|
+
|
|
640
|
+
// event with optional modifiers
|
|
641
|
+
[/@[\w-]+(?:\.[\w-]+)*/, {
|
|
642
|
+
cases: {
|
|
643
|
+
'@default': 'attribute.name.event'
|
|
644
|
+
}
|
|
645
|
+
}],
|
|
646
|
+
|
|
647
|
+
// static attribute
|
|
648
|
+
[/[\w-]+/, 'attribute.name'],
|
|
649
|
+
|
|
650
|
+
[/=/, 'delimiter'],
|
|
651
|
+
|
|
652
|
+
// expression-bearing values (anything quoted after :, @, $)
|
|
653
|
+
[/"/, { token: 'string.quote', next: '@dqValue' }],
|
|
654
|
+
[/'/, { token: 'string.quote', next: '@sqValue' }],
|
|
655
|
+
[/[^\s>/"'=]+/, 'string'],
|
|
656
|
+
],
|
|
657
|
+
|
|
658
|
+
dqValue: [
|
|
659
|
+
[/"/, { token: 'string.quote', next: '@pop' }],
|
|
660
|
+
{ include: '@expr' },
|
|
661
|
+
],
|
|
662
|
+
sqValue: [
|
|
663
|
+
[/'/, { token: 'string.quote', next: '@pop' }],
|
|
664
|
+
{ include: '@expr' },
|
|
665
|
+
],
|
|
666
|
+
|
|
667
|
+
expr: [
|
|
668
|
+
[/\$event\b/, 'variable.predefined'],
|
|
669
|
+
[/\$[A-Za-z_]\w*/, 'function.handler'],
|
|
670
|
+
[/\b(true|false|null|undefined)\b/, 'constant.language'],
|
|
671
|
+
[/\b(in|track|filter)\b/, 'keyword.control'],
|
|
672
|
+
[/[A-Za-z_]\w*/, 'identifier'],
|
|
673
|
+
[/\d+(\.\d+)?/, 'number'],
|
|
674
|
+
[/[+\-*/%<>=!&|?:.,;()\[\]{}]+/, 'operator'],
|
|
675
|
+
[/\s+/, ''],
|
|
676
|
+
],
|
|
677
|
+
}
|
|
678
|
+
};
|
|
679
|
+
```
|
|
680
|
+
|
|
681
|
+
### 10.6 Suggested TextMate-style scope names
|
|
682
|
+
|
|
683
|
+
For VS Code grammars, map ARE‑HTML tokens to these scopes:
|
|
684
|
+
|
|
685
|
+
| Token | TextMate scope |
|
|
686
|
+
| -------------------- | -------------------------------------------------- |
|
|
687
|
+
| `attr.binding` | `entity.other.attribute-name.binding.arehtml` |
|
|
688
|
+
| `attr.event` | `entity.other.attribute-name.event.arehtml` |
|
|
689
|
+
| `attr.directive` | `entity.other.attribute-name.directive.arehtml` |
|
|
690
|
+
| `event.modifier` | `keyword.other.modifier.event.arehtml` |
|
|
691
|
+
| `directive.builtin` | `keyword.control.directive.arehtml` |
|
|
692
|
+
| `interp.delimiter` | `punctuation.section.embedded.begin/end.arehtml` |
|
|
693
|
+
| `interp.expression` | `meta.embedded.expression.arehtml` |
|
|
694
|
+
| `$event` | `variable.language.event.arehtml` |
|
|
695
|
+
| `$<handler>` | `entity.name.function.handler.arehtml` |
|
|
696
|
+
| `track` / `in` / `filter` | `keyword.control.loop.arehtml` |
|
|
697
|
+
|
|
698
|
+
---
|
|
699
|
+
|
|
700
|
+
## 11. Notes for tooling authors
|
|
701
|
+
|
|
702
|
+
1. Treat any attribute starting with `:`, `@`, or `$` as **expression-bearing**:
|
|
703
|
+
the value should be tokenized as JS, not as a literal string.
|
|
704
|
+
2. The set of valid event modifiers is closed (see §5.3, §5.4). Anything else
|
|
705
|
+
after `@event.` is a **key name** (the framework matches `event.key === '<mod>'`).
|
|
706
|
+
3. The set of currently-shipped directives is `$if`, `$for`. All other `$<name>`
|
|
707
|
+
should still be highlighted (custom directives), but not flagged as errors.
|
|
708
|
+
4. Within `$for` values, the keywords `in`, `track`, and the helper `filter`
|
|
709
|
+
are reserved. The grammar is:
|
|
710
|
+
`(<key> | (<key>) | <key>, <index> | (<key>, <index>)) in <expr> ( track <expr> )?`.
|
|
711
|
+
5. `:class` and `:style` accept string / array / object — the grammar inside is
|
|
712
|
+
plain JS; only the host attribute name is special.
|
|
713
|
+
6. Boolean attributes and IDL form properties (see §4.2 and §4.3) are
|
|
714
|
+
**runtime** behaviors only — there is no syntactic difference for the editor.
|