jq79 0.5.5 → 0.5.7

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jq79",
3
- "version": "0.5.5",
3
+ "version": "0.5.7",
4
4
  "description": "Mini reactive component library: single-file components, Svelte-style setup scripts, fine-grained proxy reactivity. Single-file build, zero dependencies.",
5
5
  "keywords": [
6
6
  "reactive",
package/src/jq79.ts CHANGED
@@ -127,6 +127,25 @@ const reportedExprErrors = new Set<string>()
127
127
  let pendingScripts = 0
128
128
  let flushScheduled = false
129
129
 
130
+ // everything else an expression can throw - overwhelmingly a member access on
131
+ // an undefined value, `{{ game.is.loaded }}` over a game with no `is`. Unlike a
132
+ // missing name it needs no deferral and gets none: the engine already caught a
133
+ // real exception and wrote the message, so there is nothing left to decide and
134
+ // it is reported where it throws, as an error rather than a warning.
135
+ //
136
+ // That means a value still on its way is reported too - `{{ user.name }}` over
137
+ // a user that a fetch will fill renders empty and says so, once. It is a
138
+ // deliberate trade against the silence it replaces, which hid a render that was
139
+ // actively wrong (a thrown `:disabled` is falsy, so the button rendered
140
+ // *enabled*). The fix is the one the message names: `user?.name`, or `:if`.
141
+ //
142
+ // Deduped by expression text alone. A `:each` over 1000 rows throws 1000 times
143
+ // per render and the Set is what keeps that to one line; keying finer - by
144
+ // message too, as the missing-name queue does - would let one broken path
145
+ // report once per distinct message. There is no queue behind it because there
146
+ // is nothing to re-check, and so nothing that could retain a live scope
147
+ const reportedFailedExprs = new Set<string>()
148
+
130
149
  const flushExprReports = () => {
131
150
  flushScheduled = false
132
151
  if (pendingScripts > 0) return // a script started meanwhile; its release re-schedules
@@ -161,8 +180,22 @@ const trackScript = (settled: Promise<unknown>) => {
161
180
  settled.then(release, release)
162
181
  }
163
182
 
183
+ // console.error, not warn: a name that resolves nowhere is a warning because
184
+ // the runtime can only say the name is absent, while this one caught a real
185
+ // exception - it has a message the engine wrote, and the expression rendered as
186
+ // nothing instead of doing what it says
187
+ const reportFailedExpr = (expr: string, error: unknown) => {
188
+ if (reportedFailedExprs.has(expr)) return
189
+ reportedFailedExprs.add(expr)
190
+ const message = (error as Error)?.message || String(error)
191
+ console.error(
192
+ `jq79: ${message} - evaluating "${expr}". The expression rendered as nothing. ` +
193
+ `If the value arrives later, guard it - "a?.b", or :if on the element.`
194
+ )
195
+ }
196
+
164
197
  const reportExprError = (expr: string, scope: Record<string, any>, error: unknown) => {
165
- if (!(error instanceof ReferenceError)) return
198
+ if (!(error instanceof ReferenceError)) return reportFailedExpr(expr, error)
166
199
  const match = MISSING_NAME_RE.exec(error.message)
167
200
  const name = match?.[1] ?? match?.[2]
168
201
  if (!name) return // an engine whose wording we don't know: stay quiet, as before
@@ -2276,6 +2309,7 @@ export class Component79 {
2276
2309
  // a different key
2277
2310
  reportedExprErrors.clear()
2278
2311
  pendingReports.clear()
2312
+ reportedFailedExprs.clear()
2279
2313
  const marker = this.startMarker
2280
2314
  const rendered = !!(marker && this.content)
2281
2315