marko 6.3.32 → 6.3.34

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.
Files changed (63) hide show
  1. package/cheatsheet.md +38 -31
  2. package/dist/common/errors.d.ts +1 -0
  3. package/dist/debug/dom/catch.feat.js +1 -1
  4. package/dist/debug/dom/catch.feat.mjs +1 -1
  5. package/dist/debug/dom/controllable-input.feat.js +1 -1
  6. package/dist/debug/dom/controllable-input.feat.mjs +1 -1
  7. package/dist/debug/dom/controllable-open.feat.js +1 -1
  8. package/dist/debug/dom/controllable-open.feat.mjs +1 -1
  9. package/dist/debug/dom/controllable-select.feat.js +1 -1
  10. package/dist/debug/dom/controllable-select.feat.mjs +1 -1
  11. package/dist/debug/dom/controllable-textarea.feat.js +1 -1
  12. package/dist/debug/dom/controllable-textarea.feat.mjs +1 -1
  13. package/dist/debug/dom/controllable.feat.js +1 -1
  14. package/dist/debug/dom/controllable.feat.mjs +1 -1
  15. package/dist/debug/{dom-CiFITSPN.js → dom-Bj58jt8y.js} +55 -30
  16. package/dist/debug/{dom-BJ93mcSe.mjs → dom-klf8Ec_o.mjs} +55 -30
  17. package/dist/debug/dom.js +3 -1
  18. package/dist/debug/dom.mjs +3 -1
  19. package/dist/debug/html.js +154 -86
  20. package/dist/debug/html.mjs +155 -84
  21. package/dist/dom/catch.feat.js +1 -1
  22. package/dist/dom/catch.feat.mjs +1 -1
  23. package/dist/dom/control-flow.d.ts +3 -1
  24. package/dist/dom/controllable-input.feat.js +1 -1
  25. package/dist/dom/controllable-input.feat.mjs +1 -1
  26. package/dist/dom/controllable-open.feat.js +1 -1
  27. package/dist/dom/controllable-open.feat.mjs +1 -1
  28. package/dist/dom/controllable-select.feat.js +1 -1
  29. package/dist/dom/controllable-select.feat.mjs +1 -1
  30. package/dist/dom/controllable-textarea.feat.js +1 -1
  31. package/dist/dom/controllable-textarea.feat.mjs +1 -1
  32. package/dist/dom/controllable.feat.js +1 -1
  33. package/dist/dom/controllable.feat.mjs +1 -1
  34. package/dist/{dom-Cj4HQ7T_.mjs → dom-6hBvZW7X.mjs} +27 -21
  35. package/dist/{dom-CI-06TZb.js → dom-B-XpL2_H.js} +27 -21
  36. package/dist/dom.js +3 -1
  37. package/dist/dom.mjs +3 -1
  38. package/dist/html/compat.d.ts +6 -2
  39. package/dist/html/writer.d.ts +3 -0
  40. package/dist/html.d.ts +1 -1
  41. package/dist/html.js +104 -65
  42. package/dist/html.mjs +104 -65
  43. package/dist/translator/core/client.d.ts +1 -2
  44. package/dist/translator/core/server.d.ts +1 -2
  45. package/dist/translator/core/static.d.ts +1 -2
  46. package/dist/translator/index.js +430 -294
  47. package/dist/translator/util/branch-tag.d.ts +5 -0
  48. package/dist/translator/util/constants/binding-type.d.ts +1 -0
  49. package/dist/translator/util/references.d.ts +8 -1
  50. package/dist/translator/util/serialize-reasons.d.ts +4 -4
  51. package/dist/translator/util/signals.d.ts +4 -3
  52. package/dist/translator/util/statement-tag.d.ts +2 -0
  53. package/dist/translator/visitors/program/index.d.ts +0 -1
  54. package/index.d.ts +19 -1
  55. package/package.json +5 -4
  56. package/tags/html-comment.d.marko +2 -0
  57. package/tags/html-script.d.marko +2 -0
  58. package/tags/html-style.d.marko +2 -0
  59. package/tags/let.d.marko +2 -2
  60. package/tags/show.d.marko +6 -0
  61. package/tags/try.d.marko +1 -1
  62. package/tags-html.d.ts +6 -1
  63. /package/dist/translator/util/{get-accessor-char.d.ts → get-accessor-enums.d.ts} +0 -0
package/cheatsheet.md CHANGED
@@ -1,24 +1,25 @@
1
1
  # Marko 6 cheat sheet
2
2
 
3
- Marko 6 = HTML superset. NOT JSX, NOT old Marko 4/5. `.marko` files are components; the filename is the tag name.
3
+ Marko 6 = HTML superset, not JSX and not Marko 4/5 syntax. `.marko` files are components; the filename is the tag name.
4
4
 
5
5
  ## Golden rules
6
6
 
7
- 1. Text interpolation: `${expr}` inside tag bodies. A bare line at the template root parses as a TAG (concise mode): `Welcome aboard` fails to compile, but `p is a tag` compiles SILENTLY to `<p is a tag></p>` any line starting with a real tag name loses its words to attributes. Wrap text in an element (`<p>Welcome aboard</p>`) or prefix the line with `--` and a space (`-- Welcome ${name}`). Attributes take raw JS after `=` with NO braces/quotes: `<div title=user.name data-n=1 + 1>`.
8
- 2. A top-level `>` in an attribute value **ENDS THE TAG**: the value truncates there, the rest of the line becomes body text, and it usually still compiles clean. `<button disabled=count>=8 onClick() {…}>More</button>` is `disabled=count` plus the TEXT `=8 onClick() {…}>`, so the handler never binds; spaces don't help (`disabled=a > b` closes too). Parenthesize the value `disabled=(count >= 8)`, `hidden=(a > b)`, and for a TS type argument `<let/s=(new Set<string>())>`. Do NOT move the type onto the tag variable instead: `<let/s:Set<string>=new Set()>` compiles but fails type-check with TS2322 (the annotation does not flow into the initializer). A `>` nested inside `(…)`/`{…}`/`[…]` is safe (`class={ big: n > 1 }`, `of=list.filter(x => x > 1)`), but an arrow BODY is not nested: `<const/f=(a, b) => a > b>` truncates to `(a, b) => a`. `<` never closes a tag (`disabled=count<=1` is fine).
7
+ 1. Text interpolation: `${expr}` inside tag bodies. A bare line at the template root parses as a tag (concise mode): `Welcome aboard` fails to compile, but `p is a tag` compiles **silently** to `<p is a tag></p>`, since any line starting with a real tag name loses its words to attributes. Wrap text in an element (`<p>Welcome aboard</p>`) or prefix the line with `--` and a space (`-- Welcome ${name}`). Attributes take raw JS after `=` with no braces or quotes: `<div title=user.name data-n=1 + 1>`.
8
+ 2. A top-level `>` hugging its operand in an attribute value **ends the tag** silently: `<button disabled=count>=8 onClick() {…}>More</button>` is `disabled=count` plus the text `=8 onClick() {…}>`, so the handler never binds. Space a `>=` (`disabled=count >= 8`), and parenthesize a bare `>` comparison (`hidden=(a > b)`) and a TS type argument `<let/s=(new Set<string>())>`. Do not move the type onto the tag variable instead: `<let/s:Set<string>=new Set()>` compiles but fails type-check with TS2322 (the annotation does not flow into the initializer). A `>` nested inside `(…)`/`{…}`/`[…]` is safe (`class={ big: n > 1 }`). `<` never closes a tag (`disabled=count<=1` is fine).
9
9
  3. State: `<let/name=initial>` (slash then var name!). Update by plain assignment in an event handler: `count++`, `text = "hi"`. No setState, no hooks.
10
- 4. Derived values: `<const/total=items.length * price>` auto-recomputes. Never use an effect to derive state. Updates batch: mid-handler a reassigned `<let>` reads current but its derived `<const>` reads stale recompute from the `<let>`.
11
- 5. NEVER mutate state in place. `items.push(x)` will NOT update the UI. Always reassign:
10
+ 4. Derived values: `<const/total=items.length * price>` auto-recomputes. Never use an effect to derive state. `<let>` is deliberately different: its value is an _initial_ value, so `<let/draft=input.text>` seeds from a reactive value and then de-syncs; that de-sync is the point of an editable copy. So pick by intent: recomputes → `<const>`, seeds then diverges → `<let>`. A `<let>` you never assign is just a frozen `<const>`. Updates batch: mid-handler a reassigned `<let>` reads current but its derived `<const>` reads stale, so recompute from the `<let>`.
11
+ 5. Never mutate state in place: `items.push(x)` does not update the UI. Always reassign:
12
12
  - add: `items = items.concat(x)`
13
13
  - remove: `items = items.toSpliced(i, 1)`
14
14
  - update: `items = items.toSpliced(i, 1, { ...item, done: true })`
15
15
  - object: `user = { ...user, name }`
16
- 6. Events: method shorthand `onClick() { ... }` or `onClick=fn`. Handler gets the DOM event: `onSubmit(e) { e.preventDefault(); save() }`. Don't sync input values through `onInput`/`onChange` that's what the change handlers below are for.
17
- 7. Native inputs are UNCONTROLLED by default: `value=` only sets the initial value. Adding the matching `*Change` handler is what makes them controlled `valueChange` on `<input>`/`<textarea>`/`<select>`, `checkedChange` on checkboxes/radios, `openChange` on `<details>`/`<dialog>`. `value:=text` is the shorthand for `value=text valueChange(v) { text = v }`. (`<textarea value:=text/>` value attribute, not body.)
18
- 8. Transform in the handler when needed number inputs give STRINGS: `<input type="number" value=n valueChange(v) { n = +v }>`, or `value:parseFloat:=n`.
19
- 9. Radio/checkbox groups: `checkedValue:=picked` on each input (shared var, distinct `value=`) the match is checked; array var for multi-checkbox. Dropdown: `<select value:=picked>`.
16
+ 6. Events: method shorthand `onClick() { ... }` or `onClick=fn`. Handlers receive `(event, element)`; delegation means the element is the second parameter, not `event.currentTarget`: `onSubmit(e) { e.preventDefault(); save() }`, `onClick(e, el) { el.focus() }`. Don't sync input values through `onInput`/`onChange`; that's what the change handlers below are for.
17
+ 7. Native inputs are uncontrolled by default: `value=` only sets the initial value. Adding the matching `*Change` handler is what makes them controlled: `valueChange` on `<input>`/`<textarea>`/`<select>`, `checkedChange` on checkboxes/radios, `openChange` on `<details>`/`<dialog>`. `value:=text` is the shorthand for `value=text valueChange(v) { text = v }`. (`<textarea value:=text/>`: value attribute, not body.)
18
+ 8. Transform in the handler when needed; number inputs give strings: `<input type="number" value=n valueChange(v) { n = +v }>`, or `value:parseFloat:=n`.
19
+ 9. Radio/checkbox groups: `checkedValue:=picked` on each input (shared var, distinct `value=`); the match is checked; array var for multi-checkbox. Dropdown: `<select value:=picked>`.
20
+ 10. Module-level values and helpers need `static`: `static const LIMIT = 10`, `static function fmt(n) {…}`. Without it `function fmt(n) {` parses as a tag: ``Unable to find entry point for custom tag `<function>` ``, an error that never says `static`. Prefer it to `<const>` for anything that never changes: `<const/LIMIT=10>` emits a per-instance signal plus a `$setup` call.
20
21
 
21
- ## Canonical component (copy this shape)
22
+ ## Canonical component
22
23
 
23
24
  ```marko
24
25
  <let/items=[]>
@@ -57,12 +58,12 @@ Marko 6 = HTML superset. NOT JSX, NOT old Marko 4/5. `.marko` files are componen
57
58
  ## Control flow
58
59
 
59
60
  ```marko
60
- <if=cond> A </if>
61
+ <if=(count > 10)> A </if> // parenthesize comparisons; a bare `>` ends the tag (rule 2)
61
62
  <else if=other> B </else>
62
63
  <else> C </else>
63
64
 
64
65
  <for|item, index| of=list by="id"> ${item.name} </for> // by keys the loop (no key= attr!)
65
- <for|city| of=cities by=(city) => city> ${city} </for> // primitives: by takes a FUNCTION the loop param is not in scope in by=, so by=city is an undefined variable
66
+ <for|city| of=cities by=(city) => city> ${city} </for> // primitives: by takes a function; the loop param is not in scope in by=, so by=city is an undefined variable
66
67
  <for|i| from=0 until=5> ${i} </for> // 0..4
67
68
 
68
69
  <show=open> stays mounted, keeps state (form drafts) when hidden </show>
@@ -87,13 +88,13 @@ import { getUser } from "../data.js";
87
88
 
88
89
  `@placeholder`/`@catch` go on `<try>`, never on `<await>`. On the server this streams (placeholder flushes first, content follows). It works in the browser too: hand `<await>` a new promise (e.g. a `<const>` derived from state) and it shows the placeholder again, then the new result. `@catch` can't recover in place: redirect (a `<script>` setting `location`), or re-render the `<try>` by bumping a key on a wrapping `<for>`.
89
90
 
90
- Don't fetch while rendering: start data loads early, pass the PROMISE through the template, and `<await>` it where the data is rendered. Fetching inside each component that renders the data serializes the requests (waterfalls). Under @marko/run, load in the route handler `return next({ user: getUser() })`, no await and render with `<await|user|=$global.data.user>`.
91
+ Don't fetch while rendering: start data loads early, pass the promise through the template, and `<await>` it where the data is rendered. Fetching inside each component that renders the data serializes the requests (waterfalls). Under @marko/run, load in the route handler (`return next({ user: getUser() })`, no await) and render with `<await|user|=$global.data.user>`.
91
92
 
92
93
  ## Components
93
94
 
94
95
  - File `src/tags/product-card.marko` is auto-discovered as `<product-card>` from any template (no import needed). Attributes arrive as `input`: `${input.title}`.
95
- - Body content renders where the child places `<${input.content}/>`, and the child can hand it values `<${input.content}(x, y)/>` in the child, `<my-tag|count, total|>${count}</my-tag>` in the parent. Placement is the child's: put it inside a `<for>` and the body appears once per item, each with its own values (`<for|...args| to=input.to><${input.content}(...args)/></>`). Those values exist ONLY inside that body.
96
- - `<return=value>` publishes ONE value into the PARENT's scope, named by a tag variable; from there it is an ordinary value in that template. A native tag variable's value is itself a function returning the element, so `<div/el>` is read as `el()`. So: body parameters when the value belongs to the nested markup, including one set per item where the child loops; a tag variable when the parent needs one value outside the body. A tag var on a child that never returns is `undefined`.
96
+ - Body content renders where the child places `<${input.content}/>`, and the child can hand it values: `<${input.content}(x, y)/>` in the child, `<my-tag|count, total|>${count}</my-tag>` in the parent. Placement is the child's: put it inside a `<for>` and the body appears once per item, each with its own values (`<for|...args| to=input.to><${input.content}(...args)/></>`). Those values exist only inside that body.
97
+ - `<return=value>` publishes one value into the parent's scope, named by a tag variable; from there it is an ordinary value in that template. A native tag variable's value is itself a function returning the element, so `<div/el>` is read as `el()`. So: body parameters when the value belongs to the nested markup, including one set per item where the child loops; a tag variable when the parent needs one value outside the body. A tag var on a child that never returns is `undefined`.
97
98
 
98
99
  ```marko
99
100
  /* src/tags/toggle-section.marko */
@@ -124,30 +125,32 @@ Don't fetch while rendering: start data loads early, pass the PROMISE through th
124
125
  </div>
125
126
  ```
126
127
 
127
- - Repeated attr tags (many `<@tab ...>`) arrive as the SINGULAR prop `input.tab`, which is iterable but NOT an array: `input.tab[i]` and `input.tab.length` are undefined. To index or count, spread first: `<const/tabs=[...input.tab ?? []]>` then `tabs[active]`/`tabs.length`. Looping directly is fine: `<for|tab| of=input.tab>`.
128
+ - Repeated attr tags (many `<@tab ...>`) arrive as the singular prop `input.tab`, which is iterable but not an array: `input.tab[i]` and `input.tab.length` are undefined. To index or count, spread first: `<const/tabs=[...input.tab ?? []]>` then `tabs[active]`/`tabs.length`. Looping directly is fine: `<for|tab| of=input.tab>`.
128
129
  - Conditional attrs: `false`/`null` attrs are omitted from HTML. `aria-selected` etc. want strings: `aria-selected=(i === active && "true")`.
129
130
  - `class=` / `style=` accept strings, objects, arrays: `class=["btn", { active }]`, `style={ color }` (single braces). `style=` keys are kebab-case CSS names (`{ "background-color": c }`), not camelCase.
130
- - `<id/x>` mints a collision-free id for label/input wiring (`<label for=x>`/`<input id=x>`) don't hardcode ids in reusable tags; `<id/x=input.id>` reuses a caller's.
131
+ - `<id/x>` mints a collision-free id for label/input wiring (`<label for=x>`/`<input id=x>`); don't hardcode ids in reusable tags; `<id/x=input.id>` reuses a caller's.
131
132
 
132
133
  ## Sharing data (`$global`)
133
134
 
134
- - Read request-scoped `$global` from any template, no threading: `${$global.messages.title}`. Otherwise prop-drill through `input` there is no provider/consumer context API.
135
+ - Read request-scoped `$global` from any template, no threading: `${$global.messages.title}`. Otherwise prop-drill through `input`; there is no provider/consumer context API.
135
136
  - Populate at the render call: `template.render({ $global: { messages } })`. Under @marko/run a middleware's `return next({ messages })` merges into `$global.data`.
136
- - `$global` is NOT serialized to the client by default. Mark any key the BROWSER itself evaluates an event handler, a `<script>`, markup the browser (re)creates, a `<const>` that recomputes from state: `$global.serializedGlobals = { messages: true }` at the render call (under @marko/run, `context.serializedGlobals.data = true`; it ships `params`/`url` already). What the server already rendered needs no opt-in.
137
+ - `$global` is not serialized to the client by default. Mark any key the browser itself evaluates, e.g. an event handler, a `<script>`, markup the browser (re)creates, or a `<const>` that recomputes from state: `$global.serializedGlobals = { messages: true }` at the render call (under @marko/run, `context.serializedGlobals.data = true`; it ships `params`/`url` already). What the server already rendered needs no opt-in.
137
138
 
138
- ## Client-side effects (rare — prefer state/const)
139
+ ## Client-side effects
140
+
141
+ Rare; prefer state and `<const>`.
139
142
 
140
143
  ```marko
141
144
  <div/el/>
142
145
  <script>
143
146
  // Browser-only. Runs after mount and re-runs when referenced state changes.
144
- el().focus(); // NATIVE element refs are getter FUNCTIONS
147
+ el().focus(); // native element refs are getter functions
145
148
  const id = setInterval(tick, 1000);
146
149
  $signal.onabort = () => clearInterval(id); // cleanup
147
150
  </script>
148
151
  ```
149
152
 
150
- `<style>` = real CSS, extracted & global; `<style/styles>` scopes it (CSS modules) `.card {...}` then `class=styles.card`, or `<style/{card}>` then `class=card`. Don't hand-namespace globals. `<script>` = reactive effect, NOT an HTML script tag.
153
+ `<style>` = real CSS, extracted & global; `<style/styles>` scopes it (CSS modules): `.card {...}` then `class=styles.card`, or `<style/{card}>` then `class=card`. Don't hand-namespace globals. `<script>` = reactive effect, not an HTML script tag.
151
154
 
152
155
  Imperative libs (charts, maps) needing mount/update/destroy: use `<lifecycle>`, not a hand-wired `<script>`. `this` persists across all three; return an object from `onMount` to stash the instance:
153
156
 
@@ -171,7 +174,7 @@ import PriceChart from "<price-chart>" with { load: "visible#chart" }
171
174
 
172
175
  ## TypeScript
173
176
 
174
- `export interface Input` types `input` generic as `Input<T>`, body content as `Marko.Body<[params]>`, repeated attr tags as `Marko.AttrTag<T>`.
177
+ `export interface Input` types `input`; generic as `Input<T>`, body content as `Marko.Body<[params]>`, repeated attr tags as `Marko.AttrTag<T>`.
175
178
 
176
179
  ```marko
177
180
  export interface Input<T> {
@@ -183,18 +186,22 @@ export interface Input<T> {
183
186
  <${input.then}(input.value)/>
184
187
  ```
185
188
 
186
- `tsc` silently SKIPS `.marko` a type-broken template still exits 0. Check with `mtc` (`@marko/type-check`).
189
+ `tsc` silently skips `.marko`, so a type-broken template still exits 0. Check with `mtc` (`@marko/type-check`); in TS mode an undeclared `Input` is `{}` by design, so declare one before reading `input`.
190
+
191
+ ## DON'T
187
192
 
188
- ## DON'T (these are errors or silently wrong)
193
+ Each left-hand habit is an error or silently wrong.
189
194
 
190
195
  | Wrong (React/Vue/Marko5 habit) | Right |
191
196
  | ----------------------------------------------------------- | ------------------------------------------------------------------------------------ |
192
- | `disabled=n>=8` / `hidden=a > b` (top-level `>` in a value) | `disabled=(n >= 8)` a bare `>` closes the tag; the rest of the line becomes text |
193
- | `<let/s=new Set<string>()>` (type argument in a value) | `<let/s=(new Set<string>())>` a tag-var annotation fails type-check |
197
+ | `disabled=n>=8` (hugging `>` in a value) | `disabled=n >= 8` or `disabled=(n >= 8)`; a hugging `>` silently closes the tag |
198
+ | `<let/s=new Set<string>()>` (type argument in a value) | `<let/s=(new Set<string>())>`; a tag-var annotation fails type-check |
194
199
  | `{expr}` in markup, `className`, `key=`, `style={{...}}` | `${expr}`, `class`, `by=` on `<for>`, `style={...}` |
195
200
  | `onClick={() => ...}` / `@click` / `on-click("name")` | `onClick() { ... }` |
196
201
  | `const [x, setX] = useState()` / `state` / `class {}` block | `<let/x=0>` then `x = 1` |
197
202
  | `$ const y = x * 2;` (scriptlets are removed) | `<const/y=x * 2>` |
203
+ | `<let/n=a + b>` for a value that should recompute | `<const/n=a + b>`; `<let>` seeds an initial value, then de-syncs by design |
204
+ | `function fmt(n) {…}` / `const LIMIT = 10` at module level | `static function fmt(n) {…}` / `static const LIMIT = 10` |
198
205
  | `<let x=0>` | `<let/x=0>` |
199
206
  | `<if(cond)>` | `<if=cond>` |
200
207
  | `items.push(x)` | `items = items.concat(x)` |
@@ -203,8 +210,8 @@ export interface Input<T> {
203
210
  | `el.focus()` on a ref | `el().focus()` inside `<script>`/handler |
204
211
  | `input.tab[0]` / `input.tab.length` | `[...input.tab ?? []]` first (attr tags are iterables, not arrays) |
205
212
  | bare text on its own line at template root | wrap in an element (`<p>...`), or prefix the line with `--` and a space |
206
- | `by=item` using the loop variable | `by="propName"` or `by=(item) => key` `by=` is evaluated outside the loop |
207
- | `onInput(e) { q = e.target.value }` to sync an input | `value:=q` the change handler owns the value |
213
+ | `by=item` using the loop variable | `by="propName"` or `by=(item) => key`; `by=` is evaluated outside the loop |
214
+ | `onInput(e) { q = e.target.value }` to sync an input | `value:=q`; the change handler owns the value |
208
215
  | fetching inside the component that renders the data | start the promise early (route handler / top of template), pass it down to `<await>` |
209
216
  | `style={ backgroundColor: c }` (camelCase keys) | `style={ "background-color": c }` (kebab-case) |
210
217
  | `this.querySelector` / `this.getRootNode()` in `<script>` | element ref getter: `<div/el>` then `el()` (there is no `this`) |
@@ -213,6 +220,6 @@ export interface Input<T> {
213
220
  | hand-rolled `IntersectionObserver` to defer a widget's JS | `import W from "<w>" with { load: "visible#sel" }` |
214
221
  | imperative lib wired through `<script>` mount + cleanup | `<lifecycle onMount/onUpdate/onDestroy>` (keeps `this` across all three) |
215
222
  | `createContext`/provider to share data | `input` (prop drilling) or request-scoped `$global` |
216
- | `$global.x` in client-reactive code, not allow-listed | `$global.serializedGlobals = { x: true }` first otherwise the read is `undefined` |
223
+ | `$global.x` in client-reactive code, not allow-listed | `$global.serializedGlobals = { x: true }` first; otherwise the read is `undefined` |
217
224
  | hand-namespaced global classes (`.my-card-title`) | `<style/styles>` + `class=styles.card` (scoped CSS modules) |
218
- | `tsc --noEmit` to type check templates | `mtc` `tsc` skips `.marko` files and exits 0 |
225
+ | `tsc --noEmit` to type check templates | `mtc`; `tsc` skips `.marko` files and exits 0 |
@@ -2,6 +2,7 @@ export declare function assertValidAttrValue(name: string, value: unknown): void
2
2
  export declare function assertValidTextValue(value: unknown): void;
3
3
  export declare function assertValidLoopKey(key: unknown, seenKeys?: Set<unknown>): void;
4
4
  export declare function assertValidList(value: unknown): void;
5
+ export declare function assertValidRangeStart(name: string, value: unknown): void;
5
6
  export declare function assertValidRangeBound(name: string, value: unknown): void;
6
7
  export declare function assertValidAttrName(name: string): void;
7
8
  export declare function _el_read_error(): void;
@@ -1,5 +1,5 @@
1
1
  "use strict";
2
- const require_control_flow = require("../dom-CiFITSPN.js");
2
+ const require_control_flow = require("../dom-Bj58jt8y.js");
3
3
  //#region src/dom/catch.feat.ts
4
4
  const handlePendingTry = (fn, scope, branch) => {
5
5
  while (branch) {
@@ -1,4 +1,4 @@
1
- import { Cn as PendingRenders, Jt as caughtError, Sn as PendingEffects, Xt as placeholderShown, Yt as installCatch, gn as ClosestBranch, h as renderCatch, mn as Scope, pn as Pending, xn as ParentBranch } from "../dom-BJ93mcSe.mjs";
1
+ import { Cn as PendingRenders, Jt as caughtError, Sn as PendingEffects, Xt as placeholderShown, Yt as installCatch, gn as ClosestBranch, h as renderCatch, mn as Scope, pn as Pending, xn as ParentBranch } from "../dom-klf8Ec_o.mjs";
2
2
  //#region src/dom/catch.feat.ts
3
3
  const handlePendingTry = (fn, scope, branch) => {
4
4
  while (branch) {
@@ -1,5 +1,5 @@
1
1
  "use strict";
2
- const require_control_flow = require("../dom-CiFITSPN.js");
2
+ const require_control_flow = require("../dom-Bj58jt8y.js");
3
3
  //#region src/dom/controllable-input.feat.ts
4
4
  require_control_flow.controllableScripts[0] = require_control_flow._attr_input_checked_script;
5
5
  require_control_flow.controllableScripts[1] = require_control_flow._attr_input_checkedValue_script;
@@ -1,4 +1,4 @@
1
- import { R as controllableScripts, S as _attr_input_checkedValue_script, k as _attr_input_value_script, w as _attr_input_checked_script } from "../dom-BJ93mcSe.mjs";
1
+ import { R as controllableScripts, S as _attr_input_checkedValue_script, k as _attr_input_value_script, w as _attr_input_checked_script } from "../dom-klf8Ec_o.mjs";
2
2
  //#region src/dom/controllable-input.feat.ts
3
3
  controllableScripts[0] = _attr_input_checked_script;
4
4
  controllableScripts[1] = _attr_input_checkedValue_script;
@@ -1,5 +1,5 @@
1
1
  "use strict";
2
- const require_control_flow = require("../dom-CiFITSPN.js");
2
+ const require_control_flow = require("../dom-Bj58jt8y.js");
3
3
  //#region src/dom/controllable-open.feat.ts
4
4
  require_control_flow.controllableScripts[4] = require_control_flow._attr_details_or_dialog_open_script;
5
5
  //#endregion
@@ -1,4 +1,4 @@
1
- import { R as controllableScripts, v as _attr_details_or_dialog_open_script } from "../dom-BJ93mcSe.mjs";
1
+ import { R as controllableScripts, v as _attr_details_or_dialog_open_script } from "../dom-klf8Ec_o.mjs";
2
2
  //#region src/dom/controllable-open.feat.ts
3
3
  controllableScripts[4] = _attr_details_or_dialog_open_script;
4
4
  //#endregion
@@ -1,5 +1,5 @@
1
1
  "use strict";
2
- const require_control_flow = require("../dom-CiFITSPN.js");
2
+ const require_control_flow = require("../dom-Bj58jt8y.js");
3
3
  //#region src/dom/controllable-select.feat.ts
4
4
  require_control_flow.controllableScripts[3] = require_control_flow._attr_select_value_script;
5
5
  //#endregion
@@ -1,4 +1,4 @@
1
- import { M as _attr_select_value_script, R as controllableScripts } from "../dom-BJ93mcSe.mjs";
1
+ import { M as _attr_select_value_script, R as controllableScripts } from "../dom-klf8Ec_o.mjs";
2
2
  //#region src/dom/controllable-select.feat.ts
3
3
  controllableScripts[3] = _attr_select_value_script;
4
4
  //#endregion
@@ -1,5 +1,5 @@
1
1
  "use strict";
2
- const require_control_flow = require("../dom-CiFITSPN.js");
2
+ const require_control_flow = require("../dom-Bj58jt8y.js");
3
3
  //#region src/dom/controllable-textarea.feat.ts
4
4
  require_control_flow.controllableScripts[2] = require_control_flow._attr_input_value_script;
5
5
  //#endregion
@@ -1,4 +1,4 @@
1
- import { R as controllableScripts, k as _attr_input_value_script } from "../dom-BJ93mcSe.mjs";
1
+ import { R as controllableScripts, k as _attr_input_value_script } from "../dom-klf8Ec_o.mjs";
2
2
  //#region src/dom/controllable-textarea.feat.ts
3
3
  controllableScripts[2] = _attr_input_value_script;
4
4
  //#endregion
@@ -1,5 +1,5 @@
1
1
  "use strict";
2
- const require_control_flow = require("../dom-CiFITSPN.js");
2
+ const require_control_flow = require("../dom-Bj58jt8y.js");
3
3
  require("./controllable-input.feat.js");
4
4
  require("./controllable-open.feat.js");
5
5
  require("./controllable-select.feat.js");
@@ -1,4 +1,4 @@
1
- import { F as _controllable_select, I as _controllable_textarea, L as controllableRenders, N as _controllable_input, P as _controllable_open } from "../dom-BJ93mcSe.mjs";
1
+ import { F as _controllable_select, I as _controllable_textarea, L as controllableRenders, N as _controllable_input, P as _controllable_open } from "../dom-klf8Ec_o.mjs";
2
2
  import "./controllable-input.feat.mjs";
3
3
  import "./controllable-open.feat.mjs";
4
4
  import "./controllable-select.feat.mjs";
@@ -113,6 +113,7 @@ function stringifyStyleObject(name, value) {
113
113
  warnedStyleKeys.add(name);
114
114
  console.warn(`\`${name}\` is not a CSS property name; \`style\` object keys are written verbatim, so it renders as invalid CSS. Use \`${name.replace(/[A-Z]/g, (m) => "-" + m.toLowerCase()).replace(/^ms-/, "-ms-")}\`.`);
115
115
  }
116
+ if (value !== value || typeof value === "bigint" && !value) console.warn(`The \`${name}\` style value \`${value !== value ? "NaN" : "0n"}\` drops the declaration; convert it to a string or number to render it.`);
116
117
  return value || value === 0 ? escapeStyleAttr(name) + ":" + escapeStyleAttr(value + "") : "";
117
118
  }
118
119
  const unsafeStyleAttrReg = /[\\;]/g;
@@ -189,6 +190,13 @@ function assertValidAttrValue(name, value) {
189
190
  function assertValidTextValue(value) {
190
191
  const unrenderable = describeUnrenderable(value);
191
192
  if (unrenderable) throw new Error(`Text content cannot be ${unrenderable}.`);
193
+ if (isSilentlyDropped(value)) console.warn(`Text content of \`${describeDropped(value)}\` renders as nothing; convert it to a string or number to render it.`);
194
+ }
195
+ function isSilentlyDropped(value) {
196
+ return value !== value || typeof value === "bigint" && !value;
197
+ }
198
+ function describeDropped(value) {
199
+ return value !== value ? "NaN" : "0n";
192
200
  }
193
201
  function describeUnrenderable(value) {
194
202
  if (typeof value === "symbol") return "a symbol";
@@ -212,6 +220,9 @@ function assertValidLoopKey(key, seenKeys) {
212
220
  function assertValidList(value) {
213
221
  if (value && typeof value[Symbol.iterator] !== "function") throw new Error(`A \`<for>\` tag's \`of\` attribute must be an iterable, such as an array, but received ${describeForValue(value)}.`);
214
222
  }
223
+ function assertValidRangeStart(name, value) {
224
+ if (value && (typeof value !== "number" || !isFinite(value))) throw new Error(`A \`<for>\` tag's \`${name}\` attribute must be a finite number, but received ${describeForValue(value)}.`);
225
+ }
215
226
  function assertValidRangeBound(name, value) {
216
227
  if (!isFinite(value)) throw new Error(`A \`<for>\` tag's \`${name}\` attribute must be a finite number, but received ${describeForValue(value)}.`);
217
228
  }
@@ -278,12 +289,16 @@ function forOf(list, cb) {
278
289
  }
279
290
  function forTo(to, from, step, cb) {
280
291
  assertValidRangeBound("to", to);
292
+ assertValidRangeStart("from", from);
293
+ assertValidRangeStart("step", step);
281
294
  const start = from || 0;
282
295
  const delta = step || 1;
283
296
  for (let steps = (to - start) / delta, i = 0; i <= steps; i++) cb(start + i * delta);
284
297
  }
285
298
  function forUntil(until, from, step, cb) {
286
299
  assertValidRangeBound("until", until);
300
+ assertValidRangeStart("from", from);
301
+ assertValidRangeStart("step", step);
287
302
  const start = from || 0;
288
303
  const delta = step || 1;
289
304
  for (let steps = (until - start) / delta, i = 0; i < steps; i++) cb(start + i * delta);
@@ -441,10 +456,10 @@ function push(opt, item) {
441
456
  //#region src/dom/event.ts
442
457
  function _on(element, type, handler) {
443
458
  assertHandlerIsFunction("on" + type[0].toUpperCase() + type.slice(1), handler);
444
- if (element["$" + type] === void 0) delegate(type, handleDelegated);
445
- element["$" + type] = handler || null;
459
+ if (element[1 + type] === void 0) delegate(type, handleDelegated);
460
+ element[1 + type] = handler || null;
446
461
  }
447
- const delegate = (type, handler) => handler[type] ||= (document.addEventListener(type, handler, true), 1);
462
+ const delegate = (type, handler) => handler[1 + type] ||= (document.addEventListener(type, handler, true), 1);
448
463
  function handleDelegated(ev) {
449
464
  let target = !rendering && ev.target;
450
465
  Object.defineProperty(ev, "currentTarget", {
@@ -455,7 +470,7 @@ function handleDelegated(ev) {
455
470
  }
456
471
  });
457
472
  while (target) {
458
- target["$" + ev.type]?.(ev, target);
473
+ target[1 + ev.type]?.(ev, target);
459
474
  target = ev.bubbles && !ev.cancelBubble && target.parentNode;
460
475
  }
461
476
  delete ev.currentTarget;
@@ -720,7 +735,7 @@ function _var(scope, childAccessor, signal) {
720
735
  }
721
736
  const _return = (scope, value) => scope[TagVariable]?.(value);
722
737
  function _return_change(scope, changeHandler) {
723
- if (changeHandler) scope[TagVariableChange] = changeHandler;
738
+ scope[TagVariableChange] = changeHandler || void 0;
724
739
  }
725
740
  const _var_change = (scope, value, name = "This") => {
726
741
  if (typeof scope["#TagVariableChange"] !== "function") throw new TypeError(`${name} is a readonly tag variable.`);
@@ -1056,7 +1071,7 @@ function _content(id, template, walks, setup, params, dynamicScopesAccessor) {
1056
1071
  setup = setup ? setup._ || setup : void 0;
1057
1072
  params ||= void 0;
1058
1073
  const clone = template ? (branch, ns) => {
1059
- ((cloneCache[ns] ||= {})[template] ||= createCloneableHTML(template, ns))(branch, walks);
1074
+ ((cloneCache[ns] ||= {})[1 + template] ||= createCloneableHTML(template, ns))(branch, walks);
1060
1075
  } : (branch) => {
1061
1076
  walk(branch[StartNode] = branch[EndNode] = new Text(), walks, branch);
1062
1077
  };
@@ -1176,7 +1191,7 @@ function _attrs_partial(scope, nodeAccessor, nextAttrs, skip, controllable) {
1176
1191
  const partial = {};
1177
1192
  for (let i = el.attributes.length; i--;) {
1178
1193
  const { name } = el.attributes.item(i);
1179
- if (!skip[name] && !(nextAttrs && name in nextAttrs)) el.removeAttribute(name);
1194
+ if (!skip[name] && !(nextAttrs && (name in nextAttrs || hasAttrAlias(el, name, nextAttrs)))) el.removeAttribute(name);
1180
1195
  }
1181
1196
  for (const name in nextAttrs) {
1182
1197
  const key = isEventHandler(name) ? `on-${getEventHandlerName(name)}` : name;
@@ -1290,17 +1305,16 @@ function toInsertNode(startNode, endNode) {
1290
1305
  }
1291
1306
  //#endregion
1292
1307
  //#region src/dom/resolve-cursor-position.ts
1293
- const R = /[^\p{L}\p{N}]/gu;
1308
+ const R = /[\p{L}\p{N}]/gu;
1294
1309
  function resolveCursorPosition(inputType, initialPosition, initialValue, updatedValue) {
1295
1310
  if ((initialPosition || initialPosition === 0) && (initialPosition !== initialValue.length || /kw/.test(inputType))) {
1296
1311
  const before = initialValue.slice(0, initialPosition);
1297
1312
  const after = initialValue.slice(initialPosition);
1298
1313
  if (updatedValue.startsWith(before)) return initialPosition;
1299
1314
  if (updatedValue.endsWith(after)) return updatedValue.length - after.length;
1300
- let count = before.replace(R, "").length;
1301
- let pos = 0;
1302
- while (count && updatedValue[pos]) if (updatedValue[pos++].replace(R, "")) count--;
1303
- return pos;
1315
+ let count = before.match(R)?.length;
1316
+ while (count && R.test(updatedValue)) count--;
1317
+ return count ? updatedValue.length : R.lastIndex;
1304
1318
  }
1305
1319
  return -1;
1306
1320
  }
@@ -1644,12 +1658,12 @@ function _await_promise(nodeAccessor, params) {
1644
1658
  if (!scope[promiseAccessor]) {
1645
1659
  if (awaitBranch) awaitBranch[PendingRenders] ||= [];
1646
1660
  if (tryPlaceholder) awaitCounter = addAwaitCounter(scope, tryPlaceholder);
1647
- else if (!awaitCounter.i++) requestAnimationFrame(() => awaitCounter.i && runEffects(prepareEffects(() => queueRender(scope, () => {
1661
+ else scheduleAwaitFrame(awaitCounter, scope, () => {
1648
1662
  if (!awaitBranch["#DetachedAwait"]) {
1649
- awaitBranch["#StartNode"].parentNode.insertBefore(scope[nodeAccessor], awaitBranch["#StartNode"]);
1663
+ awaitBranch[StartNode].parentNode.insertBefore(scope[nodeAccessor], awaitBranch[StartNode]);
1650
1664
  tempDetachBranch(tryBranch);
1651
1665
  }
1652
- }, -1))));
1666
+ });
1653
1667
  }
1654
1668
  const thisPromise = scope[promiseAccessor] = promise.then((data) => {
1655
1669
  if (thisPromise === scope[promiseAccessor]) {
@@ -1714,12 +1728,15 @@ function addAwaitCounter(scope, tryBranch = findBranchWithKey(scope, Placeholder
1714
1728
  let awaitCounter = tryBranch[AwaitCounter];
1715
1729
  if (!awaitCounter?.i) awaitCounter = createAwaitCounter(tryBranch, () => dismissPlaceholder(tryBranch));
1716
1730
  placeholderShown.add(pendingEffects);
1717
- if (!awaitCounter.i++) requestAnimationFrame(() => awaitCounter.i && runEffects(prepareEffects(() => queueRender(tryBranch, () => {
1718
- insertBranchBefore(tryBranch["#PlaceholderBranch"] = createAndSetupBranch(tryBranch["$global"], tryBranch["#PlaceholderContent"], tryBranch["_"], tryBranch["#StartNode"].parentNode), tryBranch["#StartNode"].parentNode, tryBranch["#StartNode"]);
1731
+ scheduleAwaitFrame(awaitCounter, tryBranch, () => {
1732
+ insertBranchBefore(tryBranch[PlaceholderBranch] = createAndSetupBranch(tryBranch[Global], tryBranch[PlaceholderContent], tryBranch["_"], tryBranch[StartNode].parentNode), tryBranch[StartNode].parentNode, tryBranch[StartNode]);
1719
1733
  tempDetachBranch(tryBranch);
1720
- }, -1))));
1734
+ });
1721
1735
  return awaitCounter;
1722
1736
  }
1737
+ function scheduleAwaitFrame(awaitCounter, scope, render) {
1738
+ if (!awaitCounter.i++) requestAnimationFrame(() => awaitCounter.i && runEffects(prepareEffects(() => queueRender(scope, render, -1))));
1739
+ }
1723
1740
  function createAwaitCounter(tryBranch, done) {
1724
1741
  const awaitCounter = tryBranch[AwaitCounter] = {
1725
1742
  i: 0,
@@ -1831,8 +1848,8 @@ let _dynamic_tag = /*@__PURE__*/ withBranches((nodeAccessor, getContent, getTagV
1831
1848
  if (typeof normalizedRenderer === "string") {
1832
1849
  if (getContent) {
1833
1850
  const content = getContent(scope);
1834
- setConditionalRenderer(scope[childScopeAccessor], `#${normalizedRenderer}/0`, content, createAndSetupBranch);
1835
- if (content["accessor"]) subscribeToScopeSet(content[Owner], content[Accessor], scope[childScopeAccessor][`BranchScopes:#${normalizedRenderer}/0`]);
1851
+ setConditionalRenderer(scope[childScopeAccessor], `#${normalizedRenderer.toLowerCase()}/0`, content, createAndSetupBranch);
1852
+ if (content["accessor"]) subscribeToScopeSet(content[Owner], content[Accessor], scope[childScopeAccessor][`BranchScopes:#${normalizedRenderer.toLowerCase()}/0`]);
1836
1853
  }
1837
1854
  } else if (normalizedRenderer?.["accessor"]) subscribeToScopeSet(normalizedRenderer[Owner], normalizedRenderer[Accessor], scope[childScopeAccessor]);
1838
1855
  }
@@ -1840,7 +1857,7 @@ let _dynamic_tag = /*@__PURE__*/ withBranches((nodeAccessor, getContent, getTagV
1840
1857
  const childScope = scope[childScopeAccessor];
1841
1858
  const args = getInput?.();
1842
1859
  if (typeof normalizedRenderer === "string") {
1843
- const nodeAccessor = `#${normalizedRenderer}/0`;
1860
+ const nodeAccessor = `#${normalizedRenderer.toLowerCase()}/0`;
1844
1861
  (getContent ? _attrs : _attrs_content)(childScope, nodeAccessor, (inputIsArgs ? args[0] : args) || {}, controllableRenders[childScope[nodeAccessor].tagName]);
1845
1862
  if (childScope["EventAttributes:" + nodeAccessor] || childScope["ControlledHandler:" + nodeAccessor]) queueEffect(childScope, dynamicTagScript);
1846
1863
  } else {
@@ -1868,11 +1885,9 @@ const _dynamic_tag_content = /*@__PURE__*/ withBranches((nodeAccessor) => {
1868
1885
  if (renderer) for (const accessor in renderer[LocalClosures]) renderer[LocalClosures][accessor](scope[childScopeAccessor], renderer[LocalClosureValues][accessor]);
1869
1886
  };
1870
1887
  });
1871
- function _resume_dynamic_tag() {
1872
- _resume(DYNAMIC_TAG_SCRIPT_REGISTER_ID, dynamicTagScript);
1873
- }
1888
+ const _resume_dynamic_tag = /*@__PURE__*/ withBranches(() => _resume(DYNAMIC_TAG_SCRIPT_REGISTER_ID, dynamicTagScript));
1874
1889
  function dynamicTagScript(branch) {
1875
- _attrs_script(branch, `#${branch[Renderer]}/0`);
1890
+ _attrs_script(branch, `#${branch[Renderer].toLowerCase()}/0`);
1876
1891
  }
1877
1892
  function setConditionalRenderer(scope, nodeAccessor, newRenderer, createBranch) {
1878
1893
  const referenceNode = scope[nodeAccessor];
@@ -1993,17 +2008,27 @@ const loop = /*@__PURE__*/ withBranches((forEach) => (nodeAccessor, template, wa
1993
2008
  }
1994
2009
  };
1995
2010
  });
1996
- const _for_of = /*@__PURE__*/ loop(([all, by = bySecondArg], cb) => {
2011
+ const _for_of = /*@__PURE__*/ loop(([all, by], cb) => {
2012
+ by ||= bySecondArg;
1997
2013
  if (typeof by === "string") forOf(all, (item, i) => cb(item[by], [item, i]));
1998
2014
  else forOf(all, (item, i) => cb(by(item, i), [item, i]));
1999
2015
  });
2000
- const _for_in = /*@__PURE__*/ loop(([obj, by = byFirstArg], cb) => forIn(obj, (key, value) => cb(by(key, value), [key, value])));
2001
- const _for_to = /*@__PURE__*/ loop(([to, from, step, by = byFirstArg], cb) => forTo(to, from, step, (v) => cb(by(v), [v])));
2002
- const _for_until = /*@__PURE__*/ loop(([until, from, step, by = byFirstArg], cb) => forUntil(until, from, step, (v) => cb(by(v), [v])));
2016
+ const _for_in = /*@__PURE__*/ loop(([obj, by], cb) => {
2017
+ by ||= byFirstArg;
2018
+ forIn(obj, (key, value) => cb(by(key, value), [key, value]));
2019
+ });
2020
+ const _for_to = /*@__PURE__*/ loop(([to, from, step, by], cb) => {
2021
+ by ||= byFirstArg;
2022
+ forTo(to, from, step, (v) => cb(by(v), [v]));
2023
+ });
2024
+ const _for_until = /*@__PURE__*/ loop(([until, from, step, by], cb) => {
2025
+ by ||= byFirstArg;
2026
+ forUntil(until, from, step, (v) => cb(by(v), [v]));
2027
+ });
2003
2028
  function createBranchWithTagNameOrRenderer($global, tagNameOrRenderer, parentScope, parentNode) {
2004
2029
  if (typeof tagNameOrRenderer === "string") assertValidTagName(tagNameOrRenderer);
2005
2030
  const branch = createBranch($global, tagNameOrRenderer, parentScope, parentNode);
2006
- if (typeof tagNameOrRenderer === "string") branch[`#${tagNameOrRenderer}/0`] = branch[StartNode] = branch[EndNode] = document.createElementNS(tagNameOrRenderer === "svg" ? "http://www.w3.org/2000/svg" : tagNameOrRenderer === "math" ? "http://www.w3.org/1998/Math/MathML" : parentNode.namespaceURI, tagNameOrRenderer);
2031
+ if (typeof tagNameOrRenderer === "string") branch[`#${tagNameOrRenderer.toLowerCase()}/0`] = branch[StartNode] = branch[EndNode] = document.createElementNS(tagNameOrRenderer === "svg" ? "http://www.w3.org/2000/svg" : tagNameOrRenderer === "math" ? "http://www.w3.org/1998/Math/MathML" : parentNode.namespaceURI, tagNameOrRenderer);
2007
2032
  else setupBranch(tagNameOrRenderer, branch);
2008
2033
  return branch;
2009
2034
  }