@marko/runtime-tags 6.3.2 → 6.3.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/cheatsheet.md +146 -0
- package/dist/common/accessor.d.ts +0 -1
- package/dist/common/accessor.debug.d.ts +0 -1
- package/dist/debug/dom.js +8 -0
- package/dist/debug/dom.mjs +8 -0
- package/dist/debug/html.js +39 -30
- package/dist/debug/html.mjs +39 -30
- package/dist/dom/compat.d.ts +1 -0
- package/dist/dom.js +8 -2
- package/dist/dom.mjs +8 -2
- package/dist/html/compat.d.ts +1 -1
- package/dist/html.js +33 -31
- package/dist/html.mjs +33 -31
- package/dist/translator/index.js +125 -54
- package/dist/translator/util/analyze-errors.d.ts +3 -0
- package/dist/translator/util/body-to-text-literal.d.ts +9 -1
- package/dist/translator/util/sections.d.ts +3 -0
- package/dist/translator/visitors/referenced-identifier.d.ts +7 -0
- package/dist/translator/visitors/tag/index.d.ts +5 -0
- package/package.json +6 -3
package/cheatsheet.md
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# Marko 6 cheat sheet
|
|
2
|
+
|
|
3
|
+
Marko 6 = HTML superset. NOT JSX, NOT old Marko 4/5. `.marko` files are components; the filename is the tag name.
|
|
4
|
+
|
|
5
|
+
## Golden rules
|
|
6
|
+
|
|
7
|
+
1. Text interpolation: `${expr}` inside tag bodies. A bare line like `Welcome aboard` at the root of the template parses as a TAG named `Welcome` (concise mode) and fails to compile. Wrap it in an element (`<p>Welcome aboard</p>`) or prefix the line with `-- ` to mark it as text (`-- Welcome ${name}` works at the top level). Attributes take raw JS after `=` with NO braces/quotes needed: `<div title=user.name data-n=1 + 1>` (parenthesize if the value contains `>`).
|
|
8
|
+
2. State: `<let/name=initial>` (slash then var name!). Update by plain assignment in an event handler: `count++`, `text = "hi"`. No setState, no hooks.
|
|
9
|
+
3. Derived values: `<const/total=items.length * price>` — auto-recomputes. Never use an effect to derive state.
|
|
10
|
+
4. NEVER mutate state in place. `items.push(x)` will NOT update the UI. Always reassign:
|
|
11
|
+
- add: `items = items.concat(x)`
|
|
12
|
+
- remove: `items = items.toSpliced(i, 1)`
|
|
13
|
+
- update: `items = items.toSpliced(i, 1, { ...item, done: true })`
|
|
14
|
+
- object: `user = { ...user, name }`
|
|
15
|
+
5. 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` listeners — that's what change handlers (next rule) are for, and they make the data's owner explicit.
|
|
16
|
+
6. 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.)
|
|
17
|
+
7. Write the change handler yourself when updates need transforming — number inputs give STRINGS: `<input type="number" value=n valueChange(v) { n = +v }>`.
|
|
18
|
+
|
|
19
|
+
## Canonical component (copy this shape)
|
|
20
|
+
|
|
21
|
+
```marko
|
|
22
|
+
<let/items=[]>
|
|
23
|
+
<let/draft="">
|
|
24
|
+
<const/remaining=items.filter(t => !t.done).length>
|
|
25
|
+
|
|
26
|
+
<input value:=draft placeholder="What next?">
|
|
27
|
+
<button onClick() {
|
|
28
|
+
const text = draft.trim();
|
|
29
|
+
if (text) {
|
|
30
|
+
items = items.concat({ id: items.length + 1, text, done: false });
|
|
31
|
+
draft = "";
|
|
32
|
+
}
|
|
33
|
+
}>Add</button>
|
|
34
|
+
|
|
35
|
+
<if=items.length>
|
|
36
|
+
<ul>
|
|
37
|
+
<for|item, i| of=items by="id">
|
|
38
|
+
<li class={ done: item.done }>
|
|
39
|
+
<input
|
|
40
|
+
type="checkbox"
|
|
41
|
+
checked=item.done
|
|
42
|
+
checkedChange(v) { items = items.toSpliced(i, 1, { ...item, done: v }) }
|
|
43
|
+
>
|
|
44
|
+
${item.text}
|
|
45
|
+
</li>
|
|
46
|
+
</for>
|
|
47
|
+
</ul>
|
|
48
|
+
<p>${remaining} left</p>
|
|
49
|
+
</if>
|
|
50
|
+
<else>
|
|
51
|
+
<p>Nothing yet</p>
|
|
52
|
+
</else>
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Control flow
|
|
56
|
+
|
|
57
|
+
```marko
|
|
58
|
+
<if=cond> A </if>
|
|
59
|
+
<else if=other> B </else>
|
|
60
|
+
<else> C </else>
|
|
61
|
+
|
|
62
|
+
<for|item, index| of=list by="id"> ${item.name} </for> // by keys the loop (no key= attr!)
|
|
63
|
+
<for|city| of=cities by=(city) => city> ${city} </for> // primitives: by takes a FUNCTION — by=city would be an undefined variable (the loop param is not in scope in by=)
|
|
64
|
+
<for|i| from=0 until=5> ${i} </for> // 0..4
|
|
65
|
+
|
|
66
|
+
<show=open> stays mounted, keeps state (form drafts) when hidden </show>
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
`<if>` destroys/rebuilds its content; `<show>` just hides it (use for toggles that must keep state).
|
|
70
|
+
|
|
71
|
+
## Async (`<await>`)
|
|
72
|
+
|
|
73
|
+
```marko
|
|
74
|
+
import { getUser } from "../data.js";
|
|
75
|
+
|
|
76
|
+
<try>
|
|
77
|
+
<await|user|=getUser()>
|
|
78
|
+
<h2>${user.name}</h2>
|
|
79
|
+
</await>
|
|
80
|
+
|
|
81
|
+
<@placeholder>Loading...</@placeholder>
|
|
82
|
+
<@catch|err|>${err.message}</@catch>
|
|
83
|
+
</try>
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
`@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.
|
|
87
|
+
|
|
88
|
+
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>`.
|
|
89
|
+
|
|
90
|
+
## Components
|
|
91
|
+
|
|
92
|
+
- File `src/tags/product-card.marko` is auto-discovered as `<product-card>` from any template (no import needed). Attributes arrive as `input`: `${input.title}`.
|
|
93
|
+
- Body content passed by the parent renders with `<${input.content}/>`.
|
|
94
|
+
- Named sections use attribute tags:
|
|
95
|
+
|
|
96
|
+
```marko
|
|
97
|
+
/* parent */
|
|
98
|
+
<my-card>
|
|
99
|
+
<@header>Hello</@header>
|
|
100
|
+
body text
|
|
101
|
+
</my-card>
|
|
102
|
+
|
|
103
|
+
/* src/tags/my-card.marko */
|
|
104
|
+
<div class="card">
|
|
105
|
+
<header><${input.header.content}/></header>
|
|
106
|
+
<${input.content}/>
|
|
107
|
+
</div>
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
- 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>`.
|
|
111
|
+
- Conditional attrs: `false`/`null` attrs are omitted from HTML. `aria-selected` etc. want strings: `aria-selected=(i === active && "true")`.
|
|
112
|
+
- `class=` / `style=` accept strings, objects, arrays: `class=["btn", { active }]`, `style={ color }` (single braces).
|
|
113
|
+
|
|
114
|
+
## Client-side effects (rare — prefer state/const)
|
|
115
|
+
|
|
116
|
+
```marko
|
|
117
|
+
<div/el/>
|
|
118
|
+
<script>
|
|
119
|
+
// Browser-only. Runs after mount and re-runs when referenced state changes.
|
|
120
|
+
el().focus(); // element refs are getter FUNCTIONS
|
|
121
|
+
const id = setInterval(tick, 1000);
|
|
122
|
+
$signal.onabort = () => clearInterval(id); // cleanup
|
|
123
|
+
</script>
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
`<style>` = real CSS, extracted & global. `<script>` = reactive effect, NOT an HTML script tag.
|
|
127
|
+
|
|
128
|
+
## DON'T (these are errors or silently wrong)
|
|
129
|
+
|
|
130
|
+
| Wrong (React/Vue/Marko5 habit) | Right |
|
|
131
|
+
| ----------------------------------------------------------- | ------------------------------------------------------------------------------------ |
|
|
132
|
+
| `{expr}` in markup, `className`, `key=`, `style={{...}}` | `${expr}`, `class`, `by=` on `<for>`, `style={...}` |
|
|
133
|
+
| `onClick={() => ...}` / `@click` / `on-click("name")` | `onClick() { ... }` |
|
|
134
|
+
| `const [x, setX] = useState()` / `state` / `class {}` block | `<let/x=0>` then `x = 1` |
|
|
135
|
+
| `$ const y = x * 2;` (scriptlets are removed) | `<const/y=x * 2>` |
|
|
136
|
+
| `<let x=0>` | `<let/x=0>` |
|
|
137
|
+
| `<if(cond)>` | `<if=cond>` |
|
|
138
|
+
| `items.push(x)` | `items = items.concat(x)` |
|
|
139
|
+
| `input.renderBody` | `input.content` |
|
|
140
|
+
| `<await>` with `@placeholder`/`@catch` | wrap in `<try>` |
|
|
141
|
+
| `el.focus()` on a ref | `el().focus()` inside `<script>`/handler |
|
|
142
|
+
| `input.tab[0]` / `input.tab.length` | `[...input.tab ?? []]` first (attr tags are iterables, not arrays) |
|
|
143
|
+
| bare text on its own line at template root | wrap in an element (`<p>...`), or prefix the line with `-- ` |
|
|
144
|
+
| `by=item` using the loop variable | `by="propName"` or `by=(item) => key` — `by=` is evaluated outside the loop |
|
|
145
|
+
| `onInput(e) { q = e.target.value }` to sync an input | `value:=q` — the change handler owns the value |
|
|
146
|
+
| fetching inside the component that renders the data | start the promise early (route handler / top of template), pass it down to `<await>` |
|
|
@@ -8,7 +8,6 @@ export declare enum AccessorPrefix {
|
|
|
8
8
|
ControlledValue = "ControlledValue:",
|
|
9
9
|
DynamicHTMLLastChild = "DynamicHTMLLastChild:",
|
|
10
10
|
EventAttributes = "EventAttributes:",
|
|
11
|
-
Getter = "Getter:",
|
|
12
11
|
KeyedScopes = "KeyedScopes:",
|
|
13
12
|
Lifecycle = "Lifecycle:",
|
|
14
13
|
Promise = "Promise:",
|
package/dist/debug/dom.js
CHANGED
|
@@ -1974,6 +1974,7 @@ const RENDER_BODY_ID = "$compat_renderBody";
|
|
|
1974
1974
|
//#endregion
|
|
1975
1975
|
//#region src/dom/compat.ts
|
|
1976
1976
|
const classIdToBranch = /* @__PURE__ */ new Map();
|
|
1977
|
+
let classEventResolver;
|
|
1977
1978
|
const scopesByRender = /* @__PURE__ */ new WeakMap();
|
|
1978
1979
|
const getRenderScopes = ($global) => {
|
|
1979
1980
|
const render = self[$global.runtimeId]?.[$global.renderId];
|
|
@@ -1988,9 +1989,16 @@ const compat = {
|
|
|
1988
1989
|
_resume(SET_SCOPE_REGISTER_ID, (scope) => {
|
|
1989
1990
|
getRenderScopes(scope["$global"])[scope["#Id"]] = scope;
|
|
1990
1991
|
if (scope.m5c) classIdToBranch.set(scope.m5c, scope);
|
|
1992
|
+
if (classEventResolver) for (const key in scope) {
|
|
1993
|
+
const resolved = classEventResolver(scope[key], scope);
|
|
1994
|
+
if (resolved !== scope[key]) scope[key] = resolved;
|
|
1995
|
+
}
|
|
1991
1996
|
});
|
|
1992
1997
|
_resume(RENDER_BODY_ID, warp10Noop);
|
|
1993
1998
|
},
|
|
1999
|
+
setClassEventResolver(fn) {
|
|
2000
|
+
classEventResolver = fn;
|
|
2001
|
+
},
|
|
1994
2002
|
getScope($global, scopeId) {
|
|
1995
2003
|
return getRenderScopes($global)?.[scopeId];
|
|
1996
2004
|
},
|
package/dist/debug/dom.mjs
CHANGED
|
@@ -1972,6 +1972,7 @@ const RENDER_BODY_ID = "$compat_renderBody";
|
|
|
1972
1972
|
//#endregion
|
|
1973
1973
|
//#region src/dom/compat.ts
|
|
1974
1974
|
const classIdToBranch = /* @__PURE__ */ new Map();
|
|
1975
|
+
let classEventResolver;
|
|
1975
1976
|
const scopesByRender = /* @__PURE__ */ new WeakMap();
|
|
1976
1977
|
const getRenderScopes = ($global) => {
|
|
1977
1978
|
const render = self[$global.runtimeId]?.[$global.renderId];
|
|
@@ -1986,9 +1987,16 @@ const compat = {
|
|
|
1986
1987
|
_resume(SET_SCOPE_REGISTER_ID, (scope) => {
|
|
1987
1988
|
getRenderScopes(scope["$global"])[scope["#Id"]] = scope;
|
|
1988
1989
|
if (scope.m5c) classIdToBranch.set(scope.m5c, scope);
|
|
1990
|
+
if (classEventResolver) for (const key in scope) {
|
|
1991
|
+
const resolved = classEventResolver(scope[key], scope);
|
|
1992
|
+
if (resolved !== scope[key]) scope[key] = resolved;
|
|
1993
|
+
}
|
|
1989
1994
|
});
|
|
1990
1995
|
_resume(RENDER_BODY_ID, warp10Noop);
|
|
1991
1996
|
},
|
|
1997
|
+
setClassEventResolver(fn) {
|
|
1998
|
+
classEventResolver = fn;
|
|
1999
|
+
},
|
|
1992
2000
|
getScope($global, scopeId) {
|
|
1993
2001
|
return getRenderScopes($global)?.[scopeId];
|
|
1994
2002
|
},
|
package/dist/debug/html.js
CHANGED
|
@@ -247,6 +247,8 @@ function _escape_comment(val) {
|
|
|
247
247
|
const K_SCOPE_ID = Symbol("Scope ID");
|
|
248
248
|
const kTouchedIterator = Symbol.for("marko.touchedIterator");
|
|
249
249
|
const { hasOwnProperty: hasOwnProperty$1 } = {};
|
|
250
|
+
const objectProto = Object.prototype;
|
|
251
|
+
const arrayProto = Array.prototype;
|
|
250
252
|
const Generator = (function* () {})().constructor;
|
|
251
253
|
const AsyncGenerator = (async function* () {})().constructor;
|
|
252
254
|
patchIteratorNext(Generator.prototype);
|
|
@@ -685,12 +687,6 @@ function writeProp(state, val, parent, accessor) {
|
|
|
685
687
|
}
|
|
686
688
|
}
|
|
687
689
|
function writeReferenceOr(state, write, val, parent, accessor) {
|
|
688
|
-
const scopeId = val[K_SCOPE_ID];
|
|
689
|
-
if (scopeId !== void 0) {
|
|
690
|
-
trackScope(state, val, scopeId);
|
|
691
|
-
state.buf.push("_(" + scopeId + ")");
|
|
692
|
-
return true;
|
|
693
|
-
}
|
|
694
690
|
let ref = state.refs.get(val);
|
|
695
691
|
if (ref) {
|
|
696
692
|
if (!trackChannel(state, ref)) {
|
|
@@ -811,6 +807,12 @@ function writeNull(state) {
|
|
|
811
807
|
}
|
|
812
808
|
function writeObject(state, val, parent, accessor) {
|
|
813
809
|
if (val === null) return writeNull(state);
|
|
810
|
+
const scopeId = val[K_SCOPE_ID];
|
|
811
|
+
if (scopeId !== void 0) {
|
|
812
|
+
trackScope(state, val, scopeId);
|
|
813
|
+
state.buf.push("_(" + scopeId + ")");
|
|
814
|
+
return true;
|
|
815
|
+
}
|
|
814
816
|
const wellKnownObject = KNOWN_OBJECTS.get(val);
|
|
815
817
|
if (wellKnownObject) {
|
|
816
818
|
state.buf.push(wellKnownObject);
|
|
@@ -819,7 +821,10 @@ function writeObject(state, val, parent, accessor) {
|
|
|
819
821
|
return writeReferenceOr(state, writeUnknownObject, val, parent, accessor);
|
|
820
822
|
}
|
|
821
823
|
function writeUnknownObject(state, val, ref) {
|
|
822
|
-
|
|
824
|
+
const proto = Object.getPrototypeOf(val);
|
|
825
|
+
if (proto === objectProto) return writePlainObject(state, val, ref);
|
|
826
|
+
if (proto === arrayProto) return writeArray(state, val, ref);
|
|
827
|
+
switch (proto?.constructor) {
|
|
823
828
|
case void 0: return writeNullObject(state, val, ref);
|
|
824
829
|
case Object: return writePlainObject(state, val, ref);
|
|
825
830
|
case Array: return writeArray(state, val, ref);
|
|
@@ -863,7 +868,7 @@ function writeUnknownObject(state, val, ref) {
|
|
|
863
868
|
}
|
|
864
869
|
function writePlainObject(state, val, ref) {
|
|
865
870
|
state.buf.push("{");
|
|
866
|
-
|
|
871
|
+
writeMaybeIterableProps(state, val, ref);
|
|
867
872
|
state.buf.push("}");
|
|
868
873
|
return true;
|
|
869
874
|
}
|
|
@@ -1212,7 +1217,7 @@ function writeAsyncGenerator(state, iter, ref) {
|
|
|
1212
1217
|
}
|
|
1213
1218
|
function writeNullObject(state, val, ref) {
|
|
1214
1219
|
state.buf.push("{");
|
|
1215
|
-
state.buf.push(
|
|
1220
|
+
state.buf.push(writeMaybeIterableProps(state, val, ref) + "__proto__:null}");
|
|
1216
1221
|
return true;
|
|
1217
1222
|
}
|
|
1218
1223
|
function writeObjectProps(state, val, ref) {
|
|
@@ -1223,6 +1228,10 @@ function writeObjectProps(state, val, ref) {
|
|
|
1223
1228
|
if (writeProp(state, val[key], ref, escapedKey)) sep = ",";
|
|
1224
1229
|
else state.buf.pop();
|
|
1225
1230
|
}
|
|
1231
|
+
return sep;
|
|
1232
|
+
}
|
|
1233
|
+
function writeMaybeIterableProps(state, val, ref) {
|
|
1234
|
+
let sep = writeObjectProps(state, val, ref);
|
|
1226
1235
|
if (hasSymbolIterator(val)) {
|
|
1227
1236
|
let yieldSelf = "";
|
|
1228
1237
|
const iterArr = [];
|
|
@@ -1309,14 +1318,19 @@ function isCircular(parent, ref) {
|
|
|
1309
1318
|
function toObjectKey(name) {
|
|
1310
1319
|
if (name === "") return "\"\"";
|
|
1311
1320
|
if (name === "__proto__") return "[\"__proto__\"]";
|
|
1312
|
-
const
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
}
|
|
1321
|
+
const len = name.length;
|
|
1322
|
+
const c0 = name.charCodeAt(0);
|
|
1323
|
+
if (c0 >= 48 && c0 <= 57) if (c0 === 48) {
|
|
1324
|
+
if (len !== 1) return quote(name, 1);
|
|
1325
|
+
} else for (let i = 1; i < len; i++) {
|
|
1326
|
+
const c = name.charCodeAt(i);
|
|
1327
|
+
if (c < 48 || c > 57) return quote(name, i);
|
|
1328
|
+
}
|
|
1329
|
+
else if (c0 >= 97 && c0 <= 122 || c0 >= 65 && c0 <= 90 || c0 === 95 || c0 === 36) for (let i = 1; i < len; i++) {
|
|
1330
|
+
const c = name.charCodeAt(i);
|
|
1331
|
+
if (!(c >= 97 && c <= 122 || c >= 65 && c <= 90 || c >= 48 && c <= 57 || c === 95 || c === 36)) return quote(name, i);
|
|
1332
|
+
}
|
|
1333
|
+
else return quote(name, 0);
|
|
1320
1334
|
return name;
|
|
1321
1335
|
}
|
|
1322
1336
|
function toAccess(accessor) {
|
|
@@ -1459,15 +1473,6 @@ function hasOnlyZeros(typedArray) {
|
|
|
1459
1473
|
for (let i = 0; i < typedArray.length; i++) if (typedArray[i] !== 0) return false;
|
|
1460
1474
|
return true;
|
|
1461
1475
|
}
|
|
1462
|
-
function isWordOrDigit(char) {
|
|
1463
|
-
return isWord(char) || isDigit(char);
|
|
1464
|
-
}
|
|
1465
|
-
function isDigit(char) {
|
|
1466
|
-
return char >= "0" && char <= "9";
|
|
1467
|
-
}
|
|
1468
|
-
function isWord(char) {
|
|
1469
|
-
return char >= "a" && char <= "z" || char >= "A" && char <= "Z" || char === "_" || char === "$";
|
|
1470
|
-
}
|
|
1471
1476
|
function patchIteratorNext(proto) {
|
|
1472
1477
|
if (proto.next[kTouchedIterator]) return;
|
|
1473
1478
|
const { next } = proto;
|
|
@@ -2900,6 +2905,7 @@ var ServerRendered = class {
|
|
|
2900
2905
|
pipe(stream) {
|
|
2901
2906
|
this.#read((html) => {
|
|
2902
2907
|
stream.write(html);
|
|
2908
|
+
stream.flush?.();
|
|
2903
2909
|
}, (err) => {
|
|
2904
2910
|
const socket = "socket" in stream && stream.socket;
|
|
2905
2911
|
if (socket && typeof socket.destroySoon === "function") socket.destroySoon();
|
|
@@ -2910,10 +2916,13 @@ var ServerRendered = class {
|
|
|
2910
2916
|
}
|
|
2911
2917
|
toReadable() {
|
|
2912
2918
|
let cancelled = false;
|
|
2919
|
+
let started = false;
|
|
2913
2920
|
let boundary;
|
|
2914
2921
|
const encoder = new TextEncoder();
|
|
2915
2922
|
return new ReadableStream({
|
|
2916
|
-
|
|
2923
|
+
pull: (ctrl) => {
|
|
2924
|
+
if (started) return;
|
|
2925
|
+
started = true;
|
|
2917
2926
|
boundary = this.#read((html) => {
|
|
2918
2927
|
ctrl.enqueue(encoder.encode(html));
|
|
2919
2928
|
}, (err) => {
|
|
@@ -2928,7 +2937,7 @@ var ServerRendered = class {
|
|
|
2928
2937
|
cancelled = true;
|
|
2929
2938
|
boundary?.abort(reason);
|
|
2930
2939
|
}
|
|
2931
|
-
});
|
|
2940
|
+
}, { highWaterMark: 0 });
|
|
2932
2941
|
}
|
|
2933
2942
|
then(onfulfilled, onrejected) {
|
|
2934
2943
|
return this.#promise().then(onfulfilled, onrejected);
|
|
@@ -3153,7 +3162,7 @@ const compat = {
|
|
|
3153
3162
|
if (boundary.flush() === 1) throw new Error("Cannot serialize promise across tags/class compat layer.");
|
|
3154
3163
|
return new Chunk(boundary, null, null, state).flushScript().scripts;
|
|
3155
3164
|
},
|
|
3156
|
-
render(renderer, willRerender, classAPIOut, component, input, completeChunks) {
|
|
3165
|
+
render(renderer, willRerender, classAPIOut, component, input, completeChunks, registerChildScope) {
|
|
3157
3166
|
const state = this.ensureState(classAPIOut.global);
|
|
3158
3167
|
const boundary = new Boundary(state);
|
|
3159
3168
|
let head = new Chunk(boundary, null, null, state);
|
|
@@ -3163,7 +3172,7 @@ const compat = {
|
|
|
3163
3172
|
for (const key in input) normalizedInput[key === "renderBody" ? "content" : key] = input[key];
|
|
3164
3173
|
}
|
|
3165
3174
|
head.render(() => {
|
|
3166
|
-
if (willRerender) {
|
|
3175
|
+
if (willRerender || registerChildScope) {
|
|
3167
3176
|
const scopeId = _peek_scope_id();
|
|
3168
3177
|
writeScope(scopeId, { m5c: component.id });
|
|
3169
3178
|
_script(scopeId, SET_SCOPE_REGISTER_ID);
|
package/dist/debug/html.mjs
CHANGED
|
@@ -245,6 +245,8 @@ function _escape_comment(val) {
|
|
|
245
245
|
const K_SCOPE_ID = Symbol("Scope ID");
|
|
246
246
|
const kTouchedIterator = Symbol.for("marko.touchedIterator");
|
|
247
247
|
const { hasOwnProperty: hasOwnProperty$1 } = {};
|
|
248
|
+
const objectProto = Object.prototype;
|
|
249
|
+
const arrayProto = Array.prototype;
|
|
248
250
|
const Generator = (function* () {})().constructor;
|
|
249
251
|
const AsyncGenerator = (async function* () {})().constructor;
|
|
250
252
|
patchIteratorNext(Generator.prototype);
|
|
@@ -683,12 +685,6 @@ function writeProp(state, val, parent, accessor) {
|
|
|
683
685
|
}
|
|
684
686
|
}
|
|
685
687
|
function writeReferenceOr(state, write, val, parent, accessor) {
|
|
686
|
-
const scopeId = val[K_SCOPE_ID];
|
|
687
|
-
if (scopeId !== void 0) {
|
|
688
|
-
trackScope(state, val, scopeId);
|
|
689
|
-
state.buf.push("_(" + scopeId + ")");
|
|
690
|
-
return true;
|
|
691
|
-
}
|
|
692
688
|
let ref = state.refs.get(val);
|
|
693
689
|
if (ref) {
|
|
694
690
|
if (!trackChannel(state, ref)) {
|
|
@@ -809,6 +805,12 @@ function writeNull(state) {
|
|
|
809
805
|
}
|
|
810
806
|
function writeObject(state, val, parent, accessor) {
|
|
811
807
|
if (val === null) return writeNull(state);
|
|
808
|
+
const scopeId = val[K_SCOPE_ID];
|
|
809
|
+
if (scopeId !== void 0) {
|
|
810
|
+
trackScope(state, val, scopeId);
|
|
811
|
+
state.buf.push("_(" + scopeId + ")");
|
|
812
|
+
return true;
|
|
813
|
+
}
|
|
812
814
|
const wellKnownObject = KNOWN_OBJECTS.get(val);
|
|
813
815
|
if (wellKnownObject) {
|
|
814
816
|
state.buf.push(wellKnownObject);
|
|
@@ -817,7 +819,10 @@ function writeObject(state, val, parent, accessor) {
|
|
|
817
819
|
return writeReferenceOr(state, writeUnknownObject, val, parent, accessor);
|
|
818
820
|
}
|
|
819
821
|
function writeUnknownObject(state, val, ref) {
|
|
820
|
-
|
|
822
|
+
const proto = Object.getPrototypeOf(val);
|
|
823
|
+
if (proto === objectProto) return writePlainObject(state, val, ref);
|
|
824
|
+
if (proto === arrayProto) return writeArray(state, val, ref);
|
|
825
|
+
switch (proto?.constructor) {
|
|
821
826
|
case void 0: return writeNullObject(state, val, ref);
|
|
822
827
|
case Object: return writePlainObject(state, val, ref);
|
|
823
828
|
case Array: return writeArray(state, val, ref);
|
|
@@ -861,7 +866,7 @@ function writeUnknownObject(state, val, ref) {
|
|
|
861
866
|
}
|
|
862
867
|
function writePlainObject(state, val, ref) {
|
|
863
868
|
state.buf.push("{");
|
|
864
|
-
|
|
869
|
+
writeMaybeIterableProps(state, val, ref);
|
|
865
870
|
state.buf.push("}");
|
|
866
871
|
return true;
|
|
867
872
|
}
|
|
@@ -1210,7 +1215,7 @@ function writeAsyncGenerator(state, iter, ref) {
|
|
|
1210
1215
|
}
|
|
1211
1216
|
function writeNullObject(state, val, ref) {
|
|
1212
1217
|
state.buf.push("{");
|
|
1213
|
-
state.buf.push(
|
|
1218
|
+
state.buf.push(writeMaybeIterableProps(state, val, ref) + "__proto__:null}");
|
|
1214
1219
|
return true;
|
|
1215
1220
|
}
|
|
1216
1221
|
function writeObjectProps(state, val, ref) {
|
|
@@ -1221,6 +1226,10 @@ function writeObjectProps(state, val, ref) {
|
|
|
1221
1226
|
if (writeProp(state, val[key], ref, escapedKey)) sep = ",";
|
|
1222
1227
|
else state.buf.pop();
|
|
1223
1228
|
}
|
|
1229
|
+
return sep;
|
|
1230
|
+
}
|
|
1231
|
+
function writeMaybeIterableProps(state, val, ref) {
|
|
1232
|
+
let sep = writeObjectProps(state, val, ref);
|
|
1224
1233
|
if (hasSymbolIterator(val)) {
|
|
1225
1234
|
let yieldSelf = "";
|
|
1226
1235
|
const iterArr = [];
|
|
@@ -1307,14 +1316,19 @@ function isCircular(parent, ref) {
|
|
|
1307
1316
|
function toObjectKey(name) {
|
|
1308
1317
|
if (name === "") return "\"\"";
|
|
1309
1318
|
if (name === "__proto__") return "[\"__proto__\"]";
|
|
1310
|
-
const
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
}
|
|
1319
|
+
const len = name.length;
|
|
1320
|
+
const c0 = name.charCodeAt(0);
|
|
1321
|
+
if (c0 >= 48 && c0 <= 57) if (c0 === 48) {
|
|
1322
|
+
if (len !== 1) return quote(name, 1);
|
|
1323
|
+
} else for (let i = 1; i < len; i++) {
|
|
1324
|
+
const c = name.charCodeAt(i);
|
|
1325
|
+
if (c < 48 || c > 57) return quote(name, i);
|
|
1326
|
+
}
|
|
1327
|
+
else if (c0 >= 97 && c0 <= 122 || c0 >= 65 && c0 <= 90 || c0 === 95 || c0 === 36) for (let i = 1; i < len; i++) {
|
|
1328
|
+
const c = name.charCodeAt(i);
|
|
1329
|
+
if (!(c >= 97 && c <= 122 || c >= 65 && c <= 90 || c >= 48 && c <= 57 || c === 95 || c === 36)) return quote(name, i);
|
|
1330
|
+
}
|
|
1331
|
+
else return quote(name, 0);
|
|
1318
1332
|
return name;
|
|
1319
1333
|
}
|
|
1320
1334
|
function toAccess(accessor) {
|
|
@@ -1457,15 +1471,6 @@ function hasOnlyZeros(typedArray) {
|
|
|
1457
1471
|
for (let i = 0; i < typedArray.length; i++) if (typedArray[i] !== 0) return false;
|
|
1458
1472
|
return true;
|
|
1459
1473
|
}
|
|
1460
|
-
function isWordOrDigit(char) {
|
|
1461
|
-
return isWord(char) || isDigit(char);
|
|
1462
|
-
}
|
|
1463
|
-
function isDigit(char) {
|
|
1464
|
-
return char >= "0" && char <= "9";
|
|
1465
|
-
}
|
|
1466
|
-
function isWord(char) {
|
|
1467
|
-
return char >= "a" && char <= "z" || char >= "A" && char <= "Z" || char === "_" || char === "$";
|
|
1468
|
-
}
|
|
1469
1474
|
function patchIteratorNext(proto) {
|
|
1470
1475
|
if (proto.next[kTouchedIterator]) return;
|
|
1471
1476
|
const { next } = proto;
|
|
@@ -2898,6 +2903,7 @@ var ServerRendered = class {
|
|
|
2898
2903
|
pipe(stream) {
|
|
2899
2904
|
this.#read((html) => {
|
|
2900
2905
|
stream.write(html);
|
|
2906
|
+
stream.flush?.();
|
|
2901
2907
|
}, (err) => {
|
|
2902
2908
|
const socket = "socket" in stream && stream.socket;
|
|
2903
2909
|
if (socket && typeof socket.destroySoon === "function") socket.destroySoon();
|
|
@@ -2908,10 +2914,13 @@ var ServerRendered = class {
|
|
|
2908
2914
|
}
|
|
2909
2915
|
toReadable() {
|
|
2910
2916
|
let cancelled = false;
|
|
2917
|
+
let started = false;
|
|
2911
2918
|
let boundary;
|
|
2912
2919
|
const encoder = new TextEncoder();
|
|
2913
2920
|
return new ReadableStream({
|
|
2914
|
-
|
|
2921
|
+
pull: (ctrl) => {
|
|
2922
|
+
if (started) return;
|
|
2923
|
+
started = true;
|
|
2915
2924
|
boundary = this.#read((html) => {
|
|
2916
2925
|
ctrl.enqueue(encoder.encode(html));
|
|
2917
2926
|
}, (err) => {
|
|
@@ -2926,7 +2935,7 @@ var ServerRendered = class {
|
|
|
2926
2935
|
cancelled = true;
|
|
2927
2936
|
boundary?.abort(reason);
|
|
2928
2937
|
}
|
|
2929
|
-
});
|
|
2938
|
+
}, { highWaterMark: 0 });
|
|
2930
2939
|
}
|
|
2931
2940
|
then(onfulfilled, onrejected) {
|
|
2932
2941
|
return this.#promise().then(onfulfilled, onrejected);
|
|
@@ -3151,7 +3160,7 @@ const compat = {
|
|
|
3151
3160
|
if (boundary.flush() === 1) throw new Error("Cannot serialize promise across tags/class compat layer.");
|
|
3152
3161
|
return new Chunk(boundary, null, null, state).flushScript().scripts;
|
|
3153
3162
|
},
|
|
3154
|
-
render(renderer, willRerender, classAPIOut, component, input, completeChunks) {
|
|
3163
|
+
render(renderer, willRerender, classAPIOut, component, input, completeChunks, registerChildScope) {
|
|
3155
3164
|
const state = this.ensureState(classAPIOut.global);
|
|
3156
3165
|
const boundary = new Boundary(state);
|
|
3157
3166
|
let head = new Chunk(boundary, null, null, state);
|
|
@@ -3161,7 +3170,7 @@ const compat = {
|
|
|
3161
3170
|
for (const key in input) normalizedInput[key === "renderBody" ? "content" : key] = input[key];
|
|
3162
3171
|
}
|
|
3163
3172
|
head.render(() => {
|
|
3164
|
-
if (willRerender) {
|
|
3173
|
+
if (willRerender || registerChildScope) {
|
|
3165
3174
|
const scopeId = _peek_scope_id();
|
|
3166
3175
|
writeScope(scopeId, { m5c: component.id });
|
|
3167
3176
|
_script(scopeId, SET_SCOPE_REGISTER_ID);
|
package/dist/dom/compat.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ export declare const compat: {
|
|
|
6
6
|
patchDynamicTag: typeof patchDynamicTag;
|
|
7
7
|
queueEffect: typeof queueEffect;
|
|
8
8
|
init(warp10Noop: any): void;
|
|
9
|
+
setClassEventResolver(fn: (value: unknown, scope: Scope) => unknown): void;
|
|
9
10
|
getScope($global: Record<string, unknown>, scopeId: unknown): Scope | undefined;
|
|
10
11
|
setRendererId(renderer: any, id: unknown): void;
|
|
11
12
|
isRenderer(renderer: any): any;
|
package/dist/dom.js
CHANGED
|
@@ -51,7 +51,7 @@ let empty = [], rest = Symbol(), toDelimitedString = function toDelimitedString(
|
|
|
51
51
|
typeof by == "string" ? forOf(all, (item, i) => cb(item[by], [item, i])) : forOf(all, (item, i) => cb(by(item, i), [item, i]));
|
|
52
52
|
}), _for_in = /* @__PURE__ */ loop(([obj, by = byFirstArg], cb) => forIn(obj, (key, value) => cb(by(key, value), [key, value]))), _for_to = /* @__PURE__ */ loop(([to, from, step, by = byFirstArg], cb) => forTo(to, from, step, (v) => cb(by(v), [v]))), _for_until = /* @__PURE__ */ loop(([until, from, step, by = byFirstArg], cb) => forUntil(until, from, step, (v) => cb(by(v), [v]))), rendering, runId = 2, caughtError = /* @__PURE__ */ new WeakSet(), placeholderShown = /* @__PURE__ */ new WeakSet(), pendingEffects = [], pendingRenders = [], runEffects = ((effects) => {
|
|
53
53
|
for (let i = 0; i < effects.length;) effects[i++](effects[i++]);
|
|
54
|
-
}), runRender = (render) => render.c(render.b, render.d), catchEnabled, classIdToBranch = /* @__PURE__ */ new Map(), scopesByRender = /* @__PURE__ */ new WeakMap(), getRenderScopes = ($global) => {
|
|
54
|
+
}), runRender = (render) => render.c(render.b, render.d), catchEnabled, classIdToBranch = /* @__PURE__ */ new Map(), classEventResolver, scopesByRender = /* @__PURE__ */ new WeakMap(), getRenderScopes = ($global) => {
|
|
55
55
|
let render = self[$global.runtimeId]?.[$global.renderId], scopes = render && scopesByRender.get(render);
|
|
56
56
|
return render && !scopes && scopesByRender.set(render, scopes = {}), scopes;
|
|
57
57
|
}, compat = {
|
|
@@ -59,9 +59,15 @@ let empty = [], rest = Symbol(), toDelimitedString = function toDelimitedString(
|
|
|
59
59
|
queueEffect,
|
|
60
60
|
init(warp10Noop) {
|
|
61
61
|
_resume("$C_s", (scope) => {
|
|
62
|
-
getRenderScopes(scope.$)[scope.L] = scope, scope.m5c && classIdToBranch.set(scope.m5c, scope)
|
|
62
|
+
if (getRenderScopes(scope.$)[scope.L] = scope, scope.m5c && classIdToBranch.set(scope.m5c, scope), classEventResolver) for (let key in scope) {
|
|
63
|
+
let resolved = classEventResolver(scope[key], scope);
|
|
64
|
+
resolved !== scope[key] && (scope[key] = resolved);
|
|
65
|
+
}
|
|
63
66
|
}), _resume("$C_b", warp10Noop);
|
|
64
67
|
},
|
|
68
|
+
setClassEventResolver(fn) {
|
|
69
|
+
classEventResolver = fn;
|
|
70
|
+
},
|
|
65
71
|
getScope($global, scopeId) {
|
|
66
72
|
return getRenderScopes($global)?.[scopeId];
|
|
67
73
|
},
|
package/dist/dom.mjs
CHANGED
|
@@ -51,7 +51,7 @@ let empty = [], rest = Symbol(), toDelimitedString = function toDelimitedString(
|
|
|
51
51
|
typeof by == "string" ? forOf(all, (item, i) => cb(item[by], [item, i])) : forOf(all, (item, i) => cb(by(item, i), [item, i]));
|
|
52
52
|
}), _for_in = /* @__PURE__ */ loop(([obj, by = byFirstArg], cb) => forIn(obj, (key, value) => cb(by(key, value), [key, value]))), _for_to = /* @__PURE__ */ loop(([to, from, step, by = byFirstArg], cb) => forTo(to, from, step, (v) => cb(by(v), [v]))), _for_until = /* @__PURE__ */ loop(([until, from, step, by = byFirstArg], cb) => forUntil(until, from, step, (v) => cb(by(v), [v]))), rendering, runId = 2, caughtError = /* @__PURE__ */ new WeakSet(), placeholderShown = /* @__PURE__ */ new WeakSet(), pendingEffects = [], pendingRenders = [], runEffects = ((effects) => {
|
|
53
53
|
for (let i = 0; i < effects.length;) effects[i++](effects[i++]);
|
|
54
|
-
}), runRender = (render) => render.c(render.b, render.d), catchEnabled, classIdToBranch = /* @__PURE__ */ new Map(), scopesByRender = /* @__PURE__ */ new WeakMap(), getRenderScopes = ($global) => {
|
|
54
|
+
}), runRender = (render) => render.c(render.b, render.d), catchEnabled, classIdToBranch = /* @__PURE__ */ new Map(), classEventResolver, scopesByRender = /* @__PURE__ */ new WeakMap(), getRenderScopes = ($global) => {
|
|
55
55
|
let render = self[$global.runtimeId]?.[$global.renderId], scopes = render && scopesByRender.get(render);
|
|
56
56
|
return render && !scopes && scopesByRender.set(render, scopes = {}), scopes;
|
|
57
57
|
}, compat = {
|
|
@@ -59,9 +59,15 @@ let empty = [], rest = Symbol(), toDelimitedString = function toDelimitedString(
|
|
|
59
59
|
queueEffect,
|
|
60
60
|
init(warp10Noop) {
|
|
61
61
|
_resume("$C_s", (scope) => {
|
|
62
|
-
getRenderScopes(scope.$)[scope.L] = scope, scope.m5c && classIdToBranch.set(scope.m5c, scope)
|
|
62
|
+
if (getRenderScopes(scope.$)[scope.L] = scope, scope.m5c && classIdToBranch.set(scope.m5c, scope), classEventResolver) for (let key in scope) {
|
|
63
|
+
let resolved = classEventResolver(scope[key], scope);
|
|
64
|
+
resolved !== scope[key] && (scope[key] = resolved);
|
|
65
|
+
}
|
|
63
66
|
}), _resume("$C_b", warp10Noop);
|
|
64
67
|
},
|
|
68
|
+
setClassEventResolver(fn) {
|
|
69
|
+
classEventResolver = fn;
|
|
70
|
+
},
|
|
65
71
|
getScope($global, scopeId) {
|
|
66
72
|
return getRenderScopes($global)?.[scopeId];
|
|
67
73
|
},
|
package/dist/html/compat.d.ts
CHANGED
|
@@ -16,7 +16,7 @@ export declare const compat: {
|
|
|
16
16
|
writeSetScopeForComponent(branchId: number, m5c: string, m5i: unknown): void;
|
|
17
17
|
toJSON(): (this: WeakKey) => [registryId: string, scopeId: unknown] | undefined;
|
|
18
18
|
flushScript($global: any): string;
|
|
19
|
-
render(renderer: ServerRenderer, willRerender: boolean, classAPIOut: any, component: any, input: any, completeChunks: Chunk[]): void;
|
|
19
|
+
render(renderer: ServerRenderer, willRerender: boolean, classAPIOut: any, component: any, input: any, completeChunks: Chunk[], registerChildScope?: boolean): void;
|
|
20
20
|
register: typeof register;
|
|
21
21
|
registerRenderBody(fn: any): void;
|
|
22
22
|
};
|