jq79 0.5.6 → 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/dist/jq79.cjs +12 -12
- package/dist/jq79.cjs.map +1 -1
- package/dist/jq79.global.js +12 -12
- package/dist/jq79.global.js.map +1 -1
- package/dist/jq79.js +12 -12
- package/dist/jq79.js.map +1 -1
- package/package.json +1 -1
- package/src/jq79.ts +26 -56
package/package.json
CHANGED
package/src/jq79.ts
CHANGED
|
@@ -128,34 +128,23 @@ let pendingScripts = 0
|
|
|
128
128
|
let flushScheduled = false
|
|
129
129
|
|
|
130
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`.
|
|
132
|
-
//
|
|
133
|
-
//
|
|
134
|
-
//
|
|
135
|
-
// what mustn't happen (`@click="count++ + missing"` increments twice).
|
|
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.
|
|
136
135
|
//
|
|
137
|
-
//
|
|
138
|
-
//
|
|
139
|
-
//
|
|
140
|
-
//
|
|
141
|
-
//
|
|
142
|
-
// only what is still queued once the page has gone quiet.
|
|
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`.
|
|
143
141
|
//
|
|
144
|
-
//
|
|
145
|
-
//
|
|
146
|
-
//
|
|
147
|
-
//
|
|
148
|
-
//
|
|
149
|
-
// hid a render that was actively wrong
|
|
150
|
-
const FAILED_EXPR_DELAY = 1000
|
|
151
|
-
|
|
152
|
-
// keyed by expression text alone, not `message|expr`: the expression is what the
|
|
153
|
-
// author edits, and one broken path throwing two messages is still one bug. The
|
|
154
|
-
// entries hold no scope, only strings, so this queue cannot retain a live
|
|
155
|
-
// component the way pendingReports can
|
|
156
|
-
const failedExprs = new Map<string, string>()
|
|
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
|
|
157
147
|
const reportedFailedExprs = new Set<string>()
|
|
158
|
-
let failedFlushTimer: ReturnType<typeof setTimeout> | undefined
|
|
159
148
|
|
|
160
149
|
const flushExprReports = () => {
|
|
161
150
|
flushScheduled = false
|
|
@@ -179,25 +168,6 @@ const scheduleExprReportFlush = () => {
|
|
|
179
168
|
queueMicrotask(flushExprReports)
|
|
180
169
|
}
|
|
181
170
|
|
|
182
|
-
const flushFailedExprs = () => {
|
|
183
|
-
failedFlushTimer = undefined
|
|
184
|
-
if (pendingScripts > 0) return // a script started meanwhile; its release re-schedules
|
|
185
|
-
failedExprs.forEach((message, expr) => {
|
|
186
|
-
reportedFailedExprs.add(expr)
|
|
187
|
-
console.warn(
|
|
188
|
-
`jq79: ${message} - evaluating "${expr}". It rendered as nothing and was ` +
|
|
189
|
-
`still failing a moment later, so the value is probably never coming: check ` +
|
|
190
|
-
`the path. If it does arrive asynchronously, guard it - "a?.b", or :if on the element.`
|
|
191
|
-
)
|
|
192
|
-
})
|
|
193
|
-
failedExprs.clear()
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
const scheduleFailedExprFlush = () => {
|
|
197
|
-
if (failedFlushTimer !== undefined || pendingScripts > 0 || !failedExprs.size) return
|
|
198
|
-
failedFlushTimer = setTimeout(flushFailedExprs, FAILED_EXPR_DELAY)
|
|
199
|
-
}
|
|
200
|
-
|
|
201
171
|
// scripts run before the template renders, so the counter is already up when
|
|
202
172
|
// the first evaluation fails. Both script modes settle through a promise;
|
|
203
173
|
// the factory's has to cover the merge, not just the module body
|
|
@@ -206,16 +176,22 @@ const trackScript = (settled: Promise<unknown>) => {
|
|
|
206
176
|
const release = () => {
|
|
207
177
|
pendingScripts--
|
|
208
178
|
scheduleExprReportFlush()
|
|
209
|
-
scheduleFailedExprFlush()
|
|
210
179
|
}
|
|
211
180
|
settled.then(release, release)
|
|
212
181
|
}
|
|
213
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
|
|
214
187
|
const reportFailedExpr = (expr: string, error: unknown) => {
|
|
215
|
-
if (reportedFailedExprs.has(expr)
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
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
|
+
)
|
|
219
195
|
}
|
|
220
196
|
|
|
221
197
|
const reportExprError = (expr: string, scope: Record<string, any>, error: unknown) => {
|
|
@@ -241,12 +217,7 @@ const runExpr = (expr: string, scope: Record<string, any>, extras?: Record<strin
|
|
|
241
217
|
|
|
242
218
|
const evalExpr = (expr: string, scope: Record<string, any>, extras?: Record<string, any>): any => {
|
|
243
219
|
try {
|
|
244
|
-
|
|
245
|
-
// the re-check for the failed-expression queue: this run is the proof that
|
|
246
|
-
// the earlier throw was a value arriving late. Guarded on size so the
|
|
247
|
-
// normal page - where nothing has ever thrown - pays one comparison
|
|
248
|
-
if (failedExprs.size) failedExprs.delete(expr)
|
|
249
|
-
return value
|
|
220
|
+
return runExpr(expr, scope, extras)
|
|
250
221
|
} catch (error) {
|
|
251
222
|
reportExprError(expr, scope, error)
|
|
252
223
|
return undefined
|
|
@@ -2339,7 +2310,6 @@ export class Component79 {
|
|
|
2339
2310
|
reportedExprErrors.clear()
|
|
2340
2311
|
pendingReports.clear()
|
|
2341
2312
|
reportedFailedExprs.clear()
|
|
2342
|
-
failedExprs.clear()
|
|
2343
2313
|
const marker = this.startMarker
|
|
2344
2314
|
const rendered = !!(marker && this.content)
|
|
2345
2315
|
|