@marko/runtime-tags 6.3.1 → 6.3.3
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/debug/html.js +4 -4
- package/dist/debug/html.mjs +4 -4
- package/dist/html.js +3 -3
- package/dist/html.mjs +3 -3
- package/dist/translator/index.js +123 -52
- package/dist/translator/util/analyze-errors.d.ts +3 -0
- 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>` |
|
package/dist/debug/html.js
CHANGED
|
@@ -220,8 +220,8 @@ function _escape(val) {
|
|
|
220
220
|
assertValidTextValue(val);
|
|
221
221
|
return val ? escapeXMLStr(val + "") : val === 0 ? "0" : "";
|
|
222
222
|
}
|
|
223
|
-
const unsafeScriptReg =
|
|
224
|
-
const escapeScriptStr = (str) => unsafeScriptReg.test(str) ? str.replace(unsafeScriptReg, "\\x3C
|
|
223
|
+
const unsafeScriptReg = /<(\/?script|!--)/gi;
|
|
224
|
+
const escapeScriptStr = (str) => unsafeScriptReg.test(str) ? str.replace(unsafeScriptReg, "\\x3C$1") : str;
|
|
225
225
|
function _escape_script(val) {
|
|
226
226
|
assertValidTextValue(val);
|
|
227
227
|
return val ? escapeScriptStr(val + "") : val === 0 ? "0" : "";
|
|
@@ -648,7 +648,7 @@ function writeAssigned(state) {
|
|
|
648
648
|
} else state.buf.push("void 0");
|
|
649
649
|
const valueStartIndex = state.buf.push(toAccess(toObjectKey(mutation.property)) + "(");
|
|
650
650
|
if (mutation.value === void 0) {} else if (writeProp(state, mutation.value, null, "")) {
|
|
651
|
-
const valueRef = state.refs.get(mutation.value);
|
|
651
|
+
const valueRef = typeof mutation.value === "string" ? state.strs.get(mutation.value) : state.refs.get(mutation.value);
|
|
652
652
|
if (valueRef && !valueRef.id && valueRef.scopeId === void 0) {
|
|
653
653
|
valueRef.id = mutation.valueId || nextRefAccess(state);
|
|
654
654
|
state.buf[valueStartIndex] = valueRef.id + "=" + state.buf[valueStartIndex];
|
|
@@ -1161,7 +1161,7 @@ function writeReadableStream(state, val, ref) {
|
|
|
1161
1161
|
}
|
|
1162
1162
|
function writeGenerator(state, iter, ref) {
|
|
1163
1163
|
if (iter[kTouchedIterator]) {
|
|
1164
|
-
state.buf.push("(
|
|
1164
|
+
state.buf.push("(function*(){}())");
|
|
1165
1165
|
return true;
|
|
1166
1166
|
}
|
|
1167
1167
|
let sep = "";
|
package/dist/debug/html.mjs
CHANGED
|
@@ -218,8 +218,8 @@ function _escape(val) {
|
|
|
218
218
|
assertValidTextValue(val);
|
|
219
219
|
return val ? escapeXMLStr(val + "") : val === 0 ? "0" : "";
|
|
220
220
|
}
|
|
221
|
-
const unsafeScriptReg =
|
|
222
|
-
const escapeScriptStr = (str) => unsafeScriptReg.test(str) ? str.replace(unsafeScriptReg, "\\x3C
|
|
221
|
+
const unsafeScriptReg = /<(\/?script|!--)/gi;
|
|
222
|
+
const escapeScriptStr = (str) => unsafeScriptReg.test(str) ? str.replace(unsafeScriptReg, "\\x3C$1") : str;
|
|
223
223
|
function _escape_script(val) {
|
|
224
224
|
assertValidTextValue(val);
|
|
225
225
|
return val ? escapeScriptStr(val + "") : val === 0 ? "0" : "";
|
|
@@ -646,7 +646,7 @@ function writeAssigned(state) {
|
|
|
646
646
|
} else state.buf.push("void 0");
|
|
647
647
|
const valueStartIndex = state.buf.push(toAccess(toObjectKey(mutation.property)) + "(");
|
|
648
648
|
if (mutation.value === void 0) {} else if (writeProp(state, mutation.value, null, "")) {
|
|
649
|
-
const valueRef = state.refs.get(mutation.value);
|
|
649
|
+
const valueRef = typeof mutation.value === "string" ? state.strs.get(mutation.value) : state.refs.get(mutation.value);
|
|
650
650
|
if (valueRef && !valueRef.id && valueRef.scopeId === void 0) {
|
|
651
651
|
valueRef.id = mutation.valueId || nextRefAccess(state);
|
|
652
652
|
state.buf[valueStartIndex] = valueRef.id + "=" + state.buf[valueStartIndex];
|
|
@@ -1159,7 +1159,7 @@ function writeReadableStream(state, val, ref) {
|
|
|
1159
1159
|
}
|
|
1160
1160
|
function writeGenerator(state, iter, ref) {
|
|
1161
1161
|
if (iter[kTouchedIterator]) {
|
|
1162
|
-
state.buf.push("(
|
|
1162
|
+
state.buf.push("(function*(){}())");
|
|
1163
1163
|
return true;
|
|
1164
1164
|
}
|
|
1165
1165
|
let sep = "";
|
package/dist/html.js
CHANGED
|
@@ -4,7 +4,7 @@ let empty = [], rest = Symbol(), toDelimitedString = function toDelimitedString(
|
|
|
4
4
|
else if (Array.isArray(val)) for (let v of val) part = toDelimitedString(v, delimiter, stringify), part && (str += sep + part, sep = delimiter);
|
|
5
5
|
else for (let name in val) part = stringify(name, val[name]), part && (str += sep + part, sep = delimiter);
|
|
6
6
|
return str;
|
|
7
|
-
}, unsafeXMLReg = /[<&]/g, replaceUnsafeXML = (c) => c === "&" ? "&" : "<", escapeXMLStr = (str) => unsafeXMLReg.test(str) ? str.replace(unsafeXMLReg, replaceUnsafeXML) : str, unsafeScriptReg =
|
|
7
|
+
}, unsafeXMLReg = /[<&]/g, replaceUnsafeXML = (c) => c === "&" ? "&" : "<", escapeXMLStr = (str) => unsafeXMLReg.test(str) ? str.replace(unsafeXMLReg, replaceUnsafeXML) : str, unsafeScriptReg = /<(\/?script|!--)/gi, escapeScriptStr = (str) => unsafeScriptReg.test(str) ? str.replace(unsafeScriptReg, "\\x3C$1") : str, unsafeStyleReg = /<\/style/gi, escapeStyleStr = (str) => unsafeStyleReg.test(str) ? str.replace(unsafeStyleReg, "\\3C/style") : str, unsafeCommentReg = />/g, escapeCommentStr = (str) => unsafeCommentReg.test(str) ? str.replace(unsafeCommentReg, ">") : str, K_SCOPE_ID = Symbol("Scope ID"), kTouchedIterator = Symbol.for("marko.touchedIterator"), { hasOwnProperty: hasOwnProperty$1 } = {}, Generator = (function* () {})().constructor, AsyncGenerator = (async function* () {})().constructor, REGISTRY = /* @__PURE__ */ new WeakMap(), KNOWN_SYMBOLS = (() => {
|
|
8
8
|
let KNOWN_SYMBOLS = /* @__PURE__ */ new Map();
|
|
9
9
|
for (let name of Object.getOwnPropertyNames(Symbol)) {
|
|
10
10
|
let symbol = Symbol[name];
|
|
@@ -534,7 +534,7 @@ function writeAssigned(state) {
|
|
|
534
534
|
} else state.buf.push("void 0");
|
|
535
535
|
let valueStartIndex = state.buf.push(toAccess(toObjectKey(mutation.property)) + "(");
|
|
536
536
|
if (mutation.value !== void 0) if (writeProp(state, mutation.value, null, "")) {
|
|
537
|
-
let valueRef = state.refs.get(mutation.value);
|
|
537
|
+
let valueRef = typeof mutation.value == "string" ? state.strs.get(mutation.value) : state.refs.get(mutation.value);
|
|
538
538
|
valueRef && !valueRef.id && valueRef.scopeId === void 0 && (valueRef.id = mutation.valueId || nextRefAccess(state), state.buf[valueStartIndex] = valueRef.id + "=" + state.buf[valueStartIndex]);
|
|
539
539
|
} else state.buf.push("void 0");
|
|
540
540
|
state.buf.push(")");
|
|
@@ -819,7 +819,7 @@ function writeReadableStream(state, val, ref) {
|
|
|
819
819
|
return state.buf.push("new ReadableStream({start(c){(async(_,f,v,l,i,p=a=>l=new Promise((r,j)=>{f=_.r=r;_.j=j}),a=((_.f=v=>{f(v);a.push(p())}),[p()]))=>{for(i of a)v=await i,i==l?c.close():c.enqueue(v)})(" + iterId + "={}).catch(e=>c.error(e))}})"), reader.read().then(onFulfilled, onRejected), boundary.startAsync(), !0;
|
|
820
820
|
}
|
|
821
821
|
function writeGenerator(state, iter, ref) {
|
|
822
|
-
if (iter[kTouchedIterator]) return state.buf.push("(
|
|
822
|
+
if (iter[kTouchedIterator]) return state.buf.push("(function*(){}())"), !0;
|
|
823
823
|
let sep = "";
|
|
824
824
|
for (state.buf.push("(function*(){");;) {
|
|
825
825
|
let { value, done } = iter.next();
|
package/dist/html.mjs
CHANGED
|
@@ -4,7 +4,7 @@ let empty = [], rest = Symbol(), toDelimitedString = function toDelimitedString(
|
|
|
4
4
|
else if (Array.isArray(val)) for (let v of val) part = toDelimitedString(v, delimiter, stringify), part && (str += sep + part, sep = delimiter);
|
|
5
5
|
else for (let name in val) part = stringify(name, val[name]), part && (str += sep + part, sep = delimiter);
|
|
6
6
|
return str;
|
|
7
|
-
}, unsafeXMLReg = /[<&]/g, replaceUnsafeXML = (c) => c === "&" ? "&" : "<", escapeXMLStr = (str) => unsafeXMLReg.test(str) ? str.replace(unsafeXMLReg, replaceUnsafeXML) : str, unsafeScriptReg =
|
|
7
|
+
}, unsafeXMLReg = /[<&]/g, replaceUnsafeXML = (c) => c === "&" ? "&" : "<", escapeXMLStr = (str) => unsafeXMLReg.test(str) ? str.replace(unsafeXMLReg, replaceUnsafeXML) : str, unsafeScriptReg = /<(\/?script|!--)/gi, escapeScriptStr = (str) => unsafeScriptReg.test(str) ? str.replace(unsafeScriptReg, "\\x3C$1") : str, unsafeStyleReg = /<\/style/gi, escapeStyleStr = (str) => unsafeStyleReg.test(str) ? str.replace(unsafeStyleReg, "\\3C/style") : str, unsafeCommentReg = />/g, escapeCommentStr = (str) => unsafeCommentReg.test(str) ? str.replace(unsafeCommentReg, ">") : str, K_SCOPE_ID = Symbol("Scope ID"), kTouchedIterator = Symbol.for("marko.touchedIterator"), { hasOwnProperty: hasOwnProperty$1 } = {}, Generator = (function* () {})().constructor, AsyncGenerator = (async function* () {})().constructor, REGISTRY = /* @__PURE__ */ new WeakMap(), KNOWN_SYMBOLS = (() => {
|
|
8
8
|
let KNOWN_SYMBOLS = /* @__PURE__ */ new Map();
|
|
9
9
|
for (let name of Object.getOwnPropertyNames(Symbol)) {
|
|
10
10
|
let symbol = Symbol[name];
|
|
@@ -533,7 +533,7 @@ function writeAssigned(state) {
|
|
|
533
533
|
} else state.buf.push("void 0");
|
|
534
534
|
let valueStartIndex = state.buf.push(toAccess(toObjectKey(mutation.property)) + "(");
|
|
535
535
|
if (mutation.value !== void 0) if (writeProp(state, mutation.value, null, "")) {
|
|
536
|
-
let valueRef = state.refs.get(mutation.value);
|
|
536
|
+
let valueRef = typeof mutation.value == "string" ? state.strs.get(mutation.value) : state.refs.get(mutation.value);
|
|
537
537
|
valueRef && !valueRef.id && valueRef.scopeId === void 0 && (valueRef.id = mutation.valueId || nextRefAccess(state), state.buf[valueStartIndex] = valueRef.id + "=" + state.buf[valueStartIndex]);
|
|
538
538
|
} else state.buf.push("void 0");
|
|
539
539
|
state.buf.push(")");
|
|
@@ -818,7 +818,7 @@ function writeReadableStream(state, val, ref) {
|
|
|
818
818
|
return state.buf.push("new ReadableStream({start(c){(async(_,f,v,l,i,p=a=>l=new Promise((r,j)=>{f=_.r=r;_.j=j}),a=((_.f=v=>{f(v);a.push(p())}),[p()]))=>{for(i of a)v=await i,i==l?c.close():c.enqueue(v)})(" + iterId + "={}).catch(e=>c.error(e))}})"), reader.read().then(onFulfilled, onRejected), boundary.startAsync(), !0;
|
|
819
819
|
}
|
|
820
820
|
function writeGenerator(state, iter, ref) {
|
|
821
|
-
if (iter[kTouchedIterator]) return state.buf.push("(
|
|
821
|
+
if (iter[kTouchedIterator]) return state.buf.push("(function*(){}())"), !0;
|
|
822
822
|
let sep = "";
|
|
823
823
|
for (state.buf.push("(function*(){");;) {
|
|
824
824
|
let { value, done } = iter.next();
|
package/dist/translator/index.js
CHANGED
|
@@ -1249,6 +1249,7 @@ function startSection(path) {
|
|
|
1249
1249
|
upstreamExpression: void 0,
|
|
1250
1250
|
downstreamBinding: void 0,
|
|
1251
1251
|
hasAbortSignal: false,
|
|
1252
|
+
abortSignalExprs: 0,
|
|
1252
1253
|
readsOwner: false,
|
|
1253
1254
|
isBranch: false
|
|
1254
1255
|
};
|
|
@@ -1539,6 +1540,21 @@ function isStaticRoot(path) {
|
|
|
1539
1540
|
}
|
|
1540
1541
|
}
|
|
1541
1542
|
//#endregion
|
|
1543
|
+
//#region src/translator/util/analyze-errors.ts
|
|
1544
|
+
const [getHasAnalyzeErrors, setHasAnalyzeErrors] = createProgramState(() => false);
|
|
1545
|
+
function reportAnalyzeError(path, error) {
|
|
1546
|
+
if (!(error instanceof Error)) throw error;
|
|
1547
|
+
const { label = error.message, loc } = error;
|
|
1548
|
+
setHasAnalyzeErrors(true);
|
|
1549
|
+
(0, _marko_compiler_babel_utils.diagnosticError)(path, {
|
|
1550
|
+
label,
|
|
1551
|
+
loc: loc ?? path.node.loc ?? void 0
|
|
1552
|
+
});
|
|
1553
|
+
}
|
|
1554
|
+
function hasAnalyzeErrors() {
|
|
1555
|
+
return getHasAnalyzeErrors();
|
|
1556
|
+
}
|
|
1557
|
+
//#endregion
|
|
1542
1558
|
//#region src/translator/util/asset-imports.ts
|
|
1543
1559
|
function addAssetImport(file, request) {
|
|
1544
1560
|
(file.metadata.marko.assetImports ??= /* @__PURE__ */ new Set()).add(request);
|
|
@@ -1713,8 +1729,8 @@ const escapeXMLStr = (str) => unsafeXMLReg.test(str) ? str.replace(unsafeXMLReg,
|
|
|
1713
1729
|
function _escape(val) {
|
|
1714
1730
|
return val ? escapeXMLStr(val + "") : val === 0 ? "0" : "";
|
|
1715
1731
|
}
|
|
1716
|
-
const unsafeScriptReg =
|
|
1717
|
-
const escapeScriptStr = (str) => unsafeScriptReg.test(str) ? str.replace(unsafeScriptReg, "\\x3C
|
|
1732
|
+
const unsafeScriptReg = /<(\/?script|!--)/gi;
|
|
1733
|
+
const escapeScriptStr = (str) => unsafeScriptReg.test(str) ? str.replace(unsafeScriptReg, "\\x3C$1") : str;
|
|
1718
1734
|
function _escape_script(val) {
|
|
1719
1735
|
return val ? escapeScriptStr(val + "") : val === 0 ? "0" : "";
|
|
1720
1736
|
}
|
|
@@ -3766,9 +3782,9 @@ var for_default = {
|
|
|
3766
3782
|
if (!isAttrTag) allowAttrs.push("by");
|
|
3767
3783
|
(0, _marko_compiler_babel_utils.assertAllowedAttributes)(tag, allowAttrs);
|
|
3768
3784
|
if (isAttrTag) return;
|
|
3769
|
-
|
|
3770
|
-
|
|
3771
|
-
}
|
|
3785
|
+
const byAttr = getKnownAttrValues(tag.node).by;
|
|
3786
|
+
if (forType !== "of" && byAttr?.type === "StringLiteral") throw tag.hub.buildError(byAttr, `The [\`<for>\` tag](https://markojs.com/docs/reference/core-tag#for) only supports a string \`by\` key with \`of\`; use a \`by=(${forType === "in" ? "key, value" : "index"}) => ...\` function for \`<for ${forType}>\`.`);
|
|
3787
|
+
if (byAttr?.type === "Identifier" && !tag.scope.getBinding(byAttr.name) && tag.node.body.params.some((param) => Object.hasOwn(_marko_compiler.types.getBindingIdentifiers(param), byAttr.name))) throw tag.hub.buildError(byAttr, `The \`by=\` attribute is evaluated before the loop runs, so \`${byAttr.name}\` is not in scope. Key with a property name string (\`by="id"\`) or a function (\`by=(${byAttr.name}) => key\`).`);
|
|
3772
3788
|
const bodySection = startSection(tagBody);
|
|
3773
3789
|
if (!bodySection) {
|
|
3774
3790
|
dropNodes(getAllTagReferenceNodes(tag.node));
|
|
@@ -4300,6 +4316,7 @@ var native_tag_default = {
|
|
|
4300
4316
|
(0, _marko_compiler_babel_utils.getProgram)().node.extra.page ??= true;
|
|
4301
4317
|
break;
|
|
4302
4318
|
}
|
|
4319
|
+
if (tagName === "option") assertOptionInSelectWithValue(tag);
|
|
4303
4320
|
const isTextOnly = isTextOnlyNativeTag(tag);
|
|
4304
4321
|
const seen = {};
|
|
4305
4322
|
const { attributes } = tag.node;
|
|
@@ -4772,6 +4789,7 @@ function assertNativeAttrValueType(tag, attr) {
|
|
|
4772
4789
|
if (_marko_compiler.types.isObjectExpression(attr.value)) throw tag.hub.buildError(attr, `The \`${name}\` attribute cannot be a plain object (it would render as \`[object Object]\`).`, Error);
|
|
4773
4790
|
}
|
|
4774
4791
|
function assertNativeHandlerAttr(tag, attr) {
|
|
4792
|
+
if (_marko_compiler.types.isObjectExpression(attr.value)) throw tag.hub.buildError(attr.value, `The \`${attr.name}\` ${isEventHandler(attr.name) ? "event handler" : "change handler"} on a [native tag](https://markojs.com/docs/reference/native-tag) must be a function. Attribute values in Marko are plain JavaScript expressions, not JSX; remove the wrapping \`{ }\` (e.g. \`${attr.name}=myHandler\` or \`${attr.name}() { ... }\`).`, Error);
|
|
4775
4793
|
if ((0, _marko_compiler_babel_utils.computeNode)(attr.value)?.value) throw tag.hub.buildError(attr.value, `The \`${attr.name}\` ${isEventHandler(attr.name) ? "event handler" : "change handler"} on a [native tag](https://markojs.com/docs/reference/native-tag) must be a function or a falsey value (\`null\`, \`undefined\`, \`false\`, \`0\`, …).`, Error);
|
|
4776
4794
|
}
|
|
4777
4795
|
const lowercaseEventHandlerReg = /^on[a-z]/;
|
|
@@ -4796,6 +4814,31 @@ function getCanonicalTagName(tag) {
|
|
|
4796
4814
|
default: return tagName;
|
|
4797
4815
|
}
|
|
4798
4816
|
}
|
|
4817
|
+
function assertOptionInSelectWithValue(tag) {
|
|
4818
|
+
let parent = tag.parentPath;
|
|
4819
|
+
while (parent) {
|
|
4820
|
+
if (parent.isMarkoTag()) {
|
|
4821
|
+
if (analyzeTagNameType(parent) === 0) {
|
|
4822
|
+
const parentName = getCanonicalTagName(parent);
|
|
4823
|
+
if (parentName === "select") {
|
|
4824
|
+
if (parent.node.attributes.some((attr) => _marko_compiler.types.isMarkoAttribute(attr) && (attr.name === "value" || attr.name === "valueChange"))) {
|
|
4825
|
+
let hasValue = false;
|
|
4826
|
+
const attributes = tag.get("attributes");
|
|
4827
|
+
for (let i = 0; i < attributes.length; i++) {
|
|
4828
|
+
const attr = attributes[i].node;
|
|
4829
|
+
if (!_marko_compiler.types.isMarkoAttribute(attr) || attr.name === "value") hasValue = true;
|
|
4830
|
+
else if (attr.name === "selected") throw attributes[i].buildCodeFrameError("The `selected` attribute is not supported on an `<option>` within a `<select>` that has a `value` attribute; include the option's `value` in the select's `value` instead.");
|
|
4831
|
+
}
|
|
4832
|
+
if (!hasValue) throw tag.buildCodeFrameError("An `<option>` within a `<select>` that has a `value` attribute must also have a `value` attribute.");
|
|
4833
|
+
}
|
|
4834
|
+
return;
|
|
4835
|
+
}
|
|
4836
|
+
if (parentName !== "optgroup") return;
|
|
4837
|
+
} else if (!isControlFlowTag(parent)) return;
|
|
4838
|
+
} else if (parent.isProgram()) return;
|
|
4839
|
+
parent = parent.parentPath;
|
|
4840
|
+
}
|
|
4841
|
+
}
|
|
4799
4842
|
function getTextOnlyEscapeHelper(tagName) {
|
|
4800
4843
|
switch (tagName) {
|
|
4801
4844
|
case "script": return "_escape_script";
|
|
@@ -5171,8 +5214,9 @@ function flattenTextOnlyConditional(rootTag) {
|
|
|
5171
5214
|
rootTag.replaceWith(_marko_compiler.types.markoPlaceholder(expr, true));
|
|
5172
5215
|
}
|
|
5173
5216
|
function assertValidCondition(tag) {
|
|
5217
|
+
const conditionTagName = getTagName(tag);
|
|
5174
5218
|
(0, _marko_compiler_babel_utils.assertNoVar)(tag);
|
|
5175
|
-
(0, _marko_compiler_babel_utils.assertNoArgs)(tag);
|
|
5219
|
+
(0, _marko_compiler_babel_utils.assertNoArgs)(tag, conditionTagName === "else" ? "Write the condition as an attribute instead: `<else if=condition>`." : `Write the condition as a value attribute instead: \`<${conditionTagName}=condition>\`.`);
|
|
5176
5220
|
(0, _marko_compiler_babel_utils.assertNoParams)(tag);
|
|
5177
5221
|
assertHasBody$1(tag);
|
|
5178
5222
|
assertNoSpreadAttrs(tag);
|
|
@@ -5524,6 +5568,7 @@ var program_default = {
|
|
|
5524
5568
|
}
|
|
5525
5569
|
},
|
|
5526
5570
|
exit(program) {
|
|
5571
|
+
if (hasAnalyzeErrors()) return;
|
|
5527
5572
|
finalizeReferences();
|
|
5528
5573
|
const programExtra = program.node.extra;
|
|
5529
5574
|
const paramsBinding = programExtra.binding;
|
|
@@ -6985,7 +7030,7 @@ function addReadToExpression(root, binding, getter) {
|
|
|
6985
7030
|
const fnRoot = getFnRoot(root);
|
|
6986
7031
|
const exprRoot = getExprRoot(fnRoot || root);
|
|
6987
7032
|
const section = getOrCreateSection(exprRoot);
|
|
6988
|
-
const exprExtra = exprRoot.node.extra ??= { section };
|
|
7033
|
+
const exprExtra = getCanonicalExtra(exprRoot.node.extra ??= { section });
|
|
6989
7034
|
const read = addRead(exprExtra, node.extra ??= {}, binding, section, getter);
|
|
6990
7035
|
const { parent } = root;
|
|
6991
7036
|
if (parent.type === "BinaryExpression" && (parent.operator === "===" || parent.operator === "!==")) read.comparedTo = parent.left === node ? parent.right : parent.left;
|
|
@@ -7462,9 +7507,9 @@ const kDOMBinding$2 = Symbol("await tag dom binding");
|
|
|
7462
7507
|
var await_default = {
|
|
7463
7508
|
analyze(tag) {
|
|
7464
7509
|
(0, _marko_compiler_babel_utils.assertNoVar)(tag);
|
|
7465
|
-
(0, _marko_compiler_babel_utils.assertNoArgs)(tag);
|
|
7510
|
+
(0, _marko_compiler_babel_utils.assertNoArgs)(tag, "Write the promise as a value attribute and receive the result as a tag parameter instead: `<await|result|=promise>`.");
|
|
7466
7511
|
assertNoSpreadAttrs(tag);
|
|
7467
|
-
(0, _marko_compiler_babel_utils.assertNoAttributeTags)(tag);
|
|
7512
|
+
(0, _marko_compiler_babel_utils.assertNoAttributeTags)(tag, "For pending and error UI, wrap the `<await>` in a [`<try>` tag](https://markojs.com/docs/reference/core-tag#try) with `<@placeholder>` and `<@catch|err|>` attribute tags.");
|
|
7468
7513
|
const { node } = tag;
|
|
7469
7514
|
const tagBody = tag.get("body");
|
|
7470
7515
|
const section = getOrCreateSection(tag);
|
|
@@ -7573,7 +7618,10 @@ var const_default = {
|
|
|
7573
7618
|
assertNoBodyContent(tag);
|
|
7574
7619
|
const { node } = tag;
|
|
7575
7620
|
const [valueAttr] = node.attributes;
|
|
7576
|
-
if (!node.var)
|
|
7621
|
+
if (!node.var) {
|
|
7622
|
+
const looksLikeDeclaration = !node.attributes.some((attr) => _marko_compiler.types.isMarkoAttribute(attr) && (attr.default || attr.name === "value")) && node.attributes.length === 1 && _marko_compiler.types.isMarkoAttribute(node.attributes[0]) && /^[A-Za-z_$][\w$]*$/.test(node.attributes[0].name);
|
|
7623
|
+
throw tag.get("name").buildCodeFrameError(`The [\`<const>\` tag](https://markojs.com/docs/reference/core-tag#const) requires a [tag variable](https://markojs.com/docs/reference/language#tag-variables)${looksLikeDeclaration ? `; the variable goes after a slash: \`<const/${node.attributes[0].name}=...>\`. For a one time module level value, prefix a plain JavaScript statement with \`static\`.` : ", e.g. `<const/doubled=count * 2>`."}`);
|
|
7624
|
+
}
|
|
7577
7625
|
if (!valueAttr) throw tag.get("name").buildCodeFrameError("The [`<const>` tag](https://markojs.com/docs/reference/core-tag#const) requires a [`value=` attribute](https://markojs.com/docs/reference/language#shorthand-value).");
|
|
7578
7626
|
if (node.attributes.length > 1 || !_marko_compiler.types.isMarkoAttribute(valueAttr) || !valueAttr.default && valueAttr.name !== "value") throw tag.get("name").buildCodeFrameError("The [`<const>` tag](https://markojs.com/docs/reference/core-tag#const) only supports the [`value=` attribute](https://markojs.com/docs/reference/language#shorthand-value).");
|
|
7579
7627
|
const valueExtra = evaluate(valueAttr.value);
|
|
@@ -7772,6 +7820,7 @@ var export_default = {
|
|
|
7772
7820
|
//#endregion
|
|
7773
7821
|
//#region src/translator/core/html-comment.ts
|
|
7774
7822
|
const kNodeBinding$1 = Symbol("comment tag binding");
|
|
7823
|
+
const escapeCommentText = (text) => text.replace(/>/g, ">");
|
|
7775
7824
|
var html_comment_default = {
|
|
7776
7825
|
analyze(tag) {
|
|
7777
7826
|
(0, _marko_compiler_babel_utils.assertNoArgs)(tag);
|
|
@@ -7809,11 +7858,11 @@ var html_comment_default = {
|
|
|
7809
7858
|
const nodeBinding = tagExtra[kNodeBinding$1];
|
|
7810
7859
|
const write = writeTo(tag);
|
|
7811
7860
|
if (isOutputHTML()) {
|
|
7812
|
-
for (const child of tag.node.body.body) if (_marko_compiler.types.isMarkoText(child)) write`${child.value}`;
|
|
7861
|
+
for (const child of tag.node.body.body) if (_marko_compiler.types.isMarkoText(child)) write`${escapeCommentText(child.value)}`;
|
|
7813
7862
|
else if (_marko_compiler.types.isMarkoPlaceholder(child)) write`${callRuntime(child.escape ? "_escape_comment" : "_unescaped", child.value)}`;
|
|
7814
7863
|
} else {
|
|
7815
7864
|
const textLiteral = bodyToTextLiteral(tag.node.body);
|
|
7816
|
-
if (_marko_compiler.types.isStringLiteral(textLiteral)) write`${textLiteral}`;
|
|
7865
|
+
if (_marko_compiler.types.isStringLiteral(textLiteral)) write`${escapeCommentText(textLiteral.value)}`;
|
|
7817
7866
|
else addStatement("render", tagSection, tagExtra.referencedBindings, _marko_compiler.types.expressionStatement(callRuntime("_text", createScopeReadExpression(nodeBinding), textLiteral)));
|
|
7818
7867
|
}
|
|
7819
7868
|
exit(tag);
|
|
@@ -7937,7 +7986,8 @@ var let_default = {
|
|
|
7937
7986
|
} else {
|
|
7938
7987
|
const start = attr.loc?.start;
|
|
7939
7988
|
const end = attr.loc?.end;
|
|
7940
|
-
|
|
7989
|
+
let msg = "The [`<let>` tag](https://markojs.com/docs/reference/core-tag#let) only supports the [`value=` attribute](https://markojs.com/docs/reference/language#shorthand-value) and its change handler.";
|
|
7990
|
+
if (!tagVar && /^[A-Za-z_$][\w$]*$/.test(attr.name)) msg += ` To declare reactive state, the variable goes after a slash: \`<let/${attr.name}=...>\`. For a one time module level value, prefix a plain JavaScript statement with \`static\`.`;
|
|
7941
7991
|
if (start == null || end == null) throw tag.get("name").buildCodeFrameError(msg);
|
|
7942
7992
|
else throw tag.hub.buildError({ loc: {
|
|
7943
7993
|
start,
|
|
@@ -7948,7 +7998,7 @@ var let_default = {
|
|
|
7948
7998
|
(0, _marko_compiler_babel_utils.assertNoParams)(tag);
|
|
7949
7999
|
assertNoBodyContent(tag);
|
|
7950
8000
|
assertNoSpreadAttrs(tag);
|
|
7951
|
-
if (!tagVar) throw tag.get("name").buildCodeFrameError("The [`<let>` tag](https://markojs.com/docs/reference/core-tag#let) requires a [tag variable](https://markojs.com/docs/reference/language#tag-variables).");
|
|
8001
|
+
if (!tagVar) throw tag.get("name").buildCodeFrameError("The [`<let>` tag](https://markojs.com/docs/reference/core-tag#let) requires a [tag variable](https://markojs.com/docs/reference/language#tag-variables), e.g. `<let/count=0>`.");
|
|
7952
8002
|
if (!_marko_compiler.types.isIdentifier(tagVar)) throw tag.get("var").buildCodeFrameError("The [`<let>` tag](https://markojs.com/docs/reference/core-tag#let) variable cannot be destructured.");
|
|
7953
8003
|
if (valueChangeAttr && (0, _marko_compiler_babel_utils.computeNode)(valueChangeAttr.value)?.value) throw tag.get("attributes").find((attr) => attr.node === valueChangeAttr).get("value").buildCodeFrameError("The [`<let>` tag](https://markojs.com/docs/reference/core-tag#let) [`valueChange=` attribute](https://markojs.com/docs/reference/core-tag#controllable-let) must be a function.");
|
|
7954
8004
|
const tagSection = getOrCreateSection(tag);
|
|
@@ -8312,7 +8362,7 @@ function isLiteral(expr) {
|
|
|
8312
8362
|
}
|
|
8313
8363
|
function assertValidShow(tag) {
|
|
8314
8364
|
(0, _marko_compiler_babel_utils.assertNoVar)(tag);
|
|
8315
|
-
(0, _marko_compiler_babel_utils.assertNoArgs)(tag);
|
|
8365
|
+
(0, _marko_compiler_babel_utils.assertNoArgs)(tag, "Write the condition as a value attribute instead: `<show=condition>`.");
|
|
8316
8366
|
(0, _marko_compiler_babel_utils.assertNoParams)(tag);
|
|
8317
8367
|
assertNoSpreadAttrs(tag);
|
|
8318
8368
|
assertHasBody(tag);
|
|
@@ -9053,7 +9103,7 @@ function getInlinedBodyTag(parent) {
|
|
|
9053
9103
|
}
|
|
9054
9104
|
//#endregion
|
|
9055
9105
|
//#region src/translator/visitors/referenced-identifier.ts
|
|
9056
|
-
const
|
|
9106
|
+
const [getAbortResetEmitted] = createSectionState("abortResetEmitted", () => /* @__PURE__ */ new Set());
|
|
9057
9107
|
var referenced_identifier_default = {
|
|
9058
9108
|
migrate(identifier) {
|
|
9059
9109
|
const { name } = identifier.node;
|
|
@@ -9073,6 +9123,9 @@ var referenced_identifier_default = {
|
|
|
9073
9123
|
const section = getOrCreateSection(identifier);
|
|
9074
9124
|
section.hasAbortSignal = true;
|
|
9075
9125
|
setReferencesScope(identifier);
|
|
9126
|
+
const exprRoot = getExprRoot(identifier);
|
|
9127
|
+
const rootExtra = exprRoot.node.extra ??= { section };
|
|
9128
|
+
if (rootExtra.abortId === void 0) rootExtra.abortId = section.abortSignalExprs++;
|
|
9076
9129
|
}
|
|
9077
9130
|
},
|
|
9078
9131
|
translate(identifier) {
|
|
@@ -9087,16 +9140,10 @@ var referenced_identifier_default = {
|
|
|
9087
9140
|
else {
|
|
9088
9141
|
const section = getSection(identifier);
|
|
9089
9142
|
const exprRoot = getExprRoot(identifier);
|
|
9090
|
-
|
|
9091
|
-
|
|
9092
|
-
if (
|
|
9093
|
-
|
|
9094
|
-
abortIdsByExpression = /* @__PURE__ */ new Map();
|
|
9095
|
-
abortIdsByExpressionForSection.set(section, abortIdsByExpression);
|
|
9096
|
-
}
|
|
9097
|
-
if (exprId === void 0) {
|
|
9098
|
-
exprId = abortIdsByExpression.size;
|
|
9099
|
-
abortIdsByExpression.set(exprRoot, exprId);
|
|
9143
|
+
const exprId = exprRoot.node.extra.abortId;
|
|
9144
|
+
const resetEmitted = getAbortResetEmitted(section);
|
|
9145
|
+
if (!resetEmitted.has(exprRoot)) {
|
|
9146
|
+
resetEmitted.add(exprRoot);
|
|
9100
9147
|
addStatement("render", section, exprRoot.node.extra?.referencedBindings, _marko_compiler.types.expressionStatement(_marko_compiler.types.callExpression(importRuntime("$signalReset"), [scopeIdentifier, _marko_compiler.types.numericLiteral(exprId)])), false);
|
|
9101
9148
|
}
|
|
9102
9149
|
identifier.replaceWith(_marko_compiler.types.callExpression(importRuntime("$signal"), [scopeIdentifier, _marko_compiler.types.numericLiteral(exprId)]));
|
|
@@ -9108,7 +9155,11 @@ var referenced_identifier_default = {
|
|
|
9108
9155
|
//#region src/translator/visitors/scriptlet.ts
|
|
9109
9156
|
var scriptlet_default = {
|
|
9110
9157
|
analyze(scriptlet) {
|
|
9111
|
-
if (!scriptlet.node.static)
|
|
9158
|
+
if (!scriptlet.node.static) {
|
|
9159
|
+
reportAnalyzeError(scriptlet, scriptlet.buildCodeFrameError("Scriptlets are not supported when using the tags api."));
|
|
9160
|
+
scriptlet.skip();
|
|
9161
|
+
return;
|
|
9162
|
+
}
|
|
9112
9163
|
mergeReferences(getOrCreateSection(scriptlet), scriptlet.node, scriptlet.node.body);
|
|
9113
9164
|
if (scriptlet.node.target === "client") (0, _marko_compiler_babel_utils.getProgram)().node.extra.isInteractive = true;
|
|
9114
9165
|
},
|
|
@@ -9267,11 +9318,18 @@ function getTagRelativePath(tag) {
|
|
|
9267
9318
|
if (!relativePath) throw tagNotFoundError(tag);
|
|
9268
9319
|
return relativePath;
|
|
9269
9320
|
}
|
|
9321
|
+
const knownWrongTags = /* @__PURE__ */ new Map([
|
|
9322
|
+
["slot", "To render content passed to this tag, use a [dynamic tag](https://markojs.com/docs/reference/language#dynamic-tags): `<${input.content}/>`."],
|
|
9323
|
+
["state", "Reactive state is declared with the [`<let>` tag](https://markojs.com/docs/reference/core-tag#let): `<let/name=initialValue>`."],
|
|
9324
|
+
["fragment", "Marko templates and tag bodies may have multiple root nodes; no fragment wrapper is needed."]
|
|
9325
|
+
]);
|
|
9270
9326
|
function tagNotFoundError(tag) {
|
|
9271
9327
|
const tagName = getTagName(tag);
|
|
9272
9328
|
if (tagName && tag.scope.hasBinding(tagName)) return tag.get("name").buildCodeFrameError(`Local variables must be in a [dynamic tag](https://markojs.com/docs/reference/language#dynamic-tags) unless they are PascalCase. Use \`<\${${tagName}}/>\` or rename to \`${tagName.charAt(0).toUpperCase() + tagName.slice(1)}\`.`);
|
|
9273
9329
|
let didYouMean = "";
|
|
9274
|
-
|
|
9330
|
+
const knownWrongTagHint = tagName && knownWrongTags.get(tagName);
|
|
9331
|
+
if (knownWrongTagHint) didYouMean = ` ${knownWrongTagHint}`;
|
|
9332
|
+
else if (tagName) {
|
|
9275
9333
|
const closestTag = (0, fastest_levenshtein.closest)(tagName, Object.keys((0, _marko_compiler_babel_utils.getTaglibLookup)(tag.hub.file).merged.tags));
|
|
9276
9334
|
if ((0, fastest_levenshtein.distance)(tagName, closestTag) < 4) didYouMean = ` Did you mean \`<${closestTag}>\`?`;
|
|
9277
9335
|
}
|
|
@@ -9478,33 +9536,46 @@ function enableDynamicTagResume(tag) {
|
|
|
9478
9536
|
var tag_default = {
|
|
9479
9537
|
analyze: {
|
|
9480
9538
|
enter(tag) {
|
|
9481
|
-
|
|
9482
|
-
|
|
9483
|
-
|
|
9484
|
-
|
|
9485
|
-
|
|
9486
|
-
|
|
9487
|
-
|
|
9488
|
-
|
|
9489
|
-
|
|
9490
|
-
|
|
9491
|
-
|
|
9492
|
-
|
|
9493
|
-
|
|
9494
|
-
|
|
9495
|
-
|
|
9496
|
-
|
|
9497
|
-
|
|
9498
|
-
|
|
9499
|
-
|
|
9500
|
-
|
|
9501
|
-
|
|
9539
|
+
try {
|
|
9540
|
+
const tagDef = (0, _marko_compiler_babel_utils.getTagDef)(tag);
|
|
9541
|
+
const type = analyzeTagNameType(tag);
|
|
9542
|
+
const hook = tagDef?.analyzer?.hook;
|
|
9543
|
+
if (hook) {
|
|
9544
|
+
enter$1(hook, tag);
|
|
9545
|
+
return;
|
|
9546
|
+
}
|
|
9547
|
+
if (type === 0) {
|
|
9548
|
+
native_tag_default.analyze.enter(tag);
|
|
9549
|
+
return;
|
|
9550
|
+
}
|
|
9551
|
+
switch (type) {
|
|
9552
|
+
case 1:
|
|
9553
|
+
custom_tag_default.analyze.enter(tag);
|
|
9554
|
+
break;
|
|
9555
|
+
case 3:
|
|
9556
|
+
attribute_tag_default.analyze.enter(tag);
|
|
9557
|
+
break;
|
|
9558
|
+
case 2:
|
|
9559
|
+
dynamic_tag_default.analyze.enter(tag);
|
|
9560
|
+
break;
|
|
9561
|
+
}
|
|
9562
|
+
} catch (err) {
|
|
9563
|
+
(tag.node.extra ??= {}).analyzeFailed = true;
|
|
9564
|
+
reportAnalyzeError(tag, err);
|
|
9565
|
+
tag.skip();
|
|
9502
9566
|
}
|
|
9503
9567
|
},
|
|
9504
9568
|
exit(tag) {
|
|
9505
|
-
|
|
9506
|
-
|
|
9507
|
-
|
|
9569
|
+
if (tag.node.extra?.analyzeFailed) return;
|
|
9570
|
+
try {
|
|
9571
|
+
const hook = (0, _marko_compiler_babel_utils.getTagDef)(tag)?.analyzer?.hook;
|
|
9572
|
+
if (hook) {
|
|
9573
|
+
exit$1(hook, tag);
|
|
9574
|
+
return;
|
|
9575
|
+
}
|
|
9576
|
+
} catch (err) {
|
|
9577
|
+
(tag.node.extra ??= {}).analyzeFailed = true;
|
|
9578
|
+
reportAnalyzeError(tag, err);
|
|
9508
9579
|
return;
|
|
9509
9580
|
}
|
|
9510
9581
|
}
|
|
@@ -47,6 +47,9 @@ export interface Section {
|
|
|
47
47
|
properties: Opt<string>;
|
|
48
48
|
} | false | undefined;
|
|
49
49
|
hasAbortSignal: boolean;
|
|
50
|
+
/** Count of distinct `$signal` expression roots; analyze allocates each
|
|
51
|
+
* root's `abortId` from this so translates read, never re-derive. */
|
|
52
|
+
abortSignalExprs: number;
|
|
50
53
|
readsOwner: boolean;
|
|
51
54
|
isBranch: boolean;
|
|
52
55
|
content: null | {
|
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
import { types as t } from "@marko/compiler";
|
|
2
|
+
declare module "@marko/compiler/dist/types" {
|
|
3
|
+
interface NodeExtra {
|
|
4
|
+
/** `$signal` abort id for this expression root, allocated in analyze
|
|
5
|
+
* (see below) so every translate reads the same id. */
|
|
6
|
+
abortId?: number;
|
|
7
|
+
}
|
|
8
|
+
}
|
|
2
9
|
declare const _default: {
|
|
3
10
|
migrate(this: unknown, identifier: t.NodePath<t.Identifier>): void;
|
|
4
11
|
analyze(this: unknown, identifier: t.NodePath<t.Identifier>): void;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@marko/runtime-tags",
|
|
3
|
-
"version": "6.3.
|
|
3
|
+
"version": "6.3.3",
|
|
4
4
|
"description": "Optimized runtime for Marko templates.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"api",
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
".": {
|
|
21
21
|
"types": "./index.d.ts"
|
|
22
22
|
},
|
|
23
|
+
"./cheatsheet.md": "./cheatsheet.md",
|
|
23
24
|
"./package.json": "./package.json",
|
|
24
25
|
"./translator": "./dist/translator/index.js",
|
|
25
26
|
"./tags/*": "./tags/*",
|
|
@@ -43,13 +44,14 @@
|
|
|
43
44
|
"tags-html.d.ts",
|
|
44
45
|
"!**/meta.*.json",
|
|
45
46
|
"!**/__tests__",
|
|
46
|
-
"!**/*.tsbuildinfo"
|
|
47
|
+
"!**/*.tsbuildinfo",
|
|
48
|
+
"cheatsheet.md"
|
|
47
49
|
],
|
|
48
50
|
"scripts": {
|
|
49
51
|
"build": "node -r ~ts ./scripts/bundle.mts"
|
|
50
52
|
},
|
|
51
53
|
"dependencies": {
|
|
52
|
-
"@marko/compiler": "^5.
|
|
54
|
+
"@marko/compiler": "^5.40.2",
|
|
53
55
|
"csstype": "^3.2.3",
|
|
54
56
|
"fastest-levenshtein": "^1.0.16",
|
|
55
57
|
"magic-string": "^0.30.21"
|
|
@@ -61,6 +63,7 @@
|
|
|
61
63
|
".": {
|
|
62
64
|
"types": "./index.d.ts"
|
|
63
65
|
},
|
|
66
|
+
"./cheatsheet.md": "./cheatsheet.md",
|
|
64
67
|
"./translator": "./src/translator/index.ts",
|
|
65
68
|
"./tags/*": "./tags/*",
|
|
66
69
|
"./debug/*": "./src/*.ts",
|