jq79 0.5.4 → 0.5.5
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 +64 -6
package/package.json
CHANGED
package/src/jq79.ts
CHANGED
|
@@ -176,12 +176,42 @@ const reportExprError = (expr: string, scope: Record<string, any>, error: unknow
|
|
|
176
176
|
scheduleExprReportFlush()
|
|
177
177
|
}
|
|
178
178
|
|
|
179
|
-
const
|
|
179
|
+
const runExpr = (expr: string, scope: Record<string, any>, extras?: Record<string, any>): any => {
|
|
180
180
|
const fn = compileExpr(expr, extras ? Object.keys(extras) : [])
|
|
181
|
-
if (!fn) return undefined
|
|
181
|
+
if (!fn) return undefined // a syntax error: compileExpr cached the failure, and it stays undefined
|
|
182
|
+
return fn(scope, ...(extras ? Object.values(extras) : []))
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const evalExpr = (expr: string, scope: Record<string, any>, extras?: Record<string, any>): any => {
|
|
186
|
+
try {
|
|
187
|
+
return runExpr(expr, scope, extras)
|
|
188
|
+
} catch (error) {
|
|
189
|
+
reportExprError(expr, scope, error)
|
|
190
|
+
return undefined
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// the same evaluation for an @event attribute, which swallows far less. Every
|
|
195
|
+
// word of the reason evalExpr catches is about rendering: an expression is
|
|
196
|
+
// re-evaluated per effect run, per interpolation, per :each item, so a value
|
|
197
|
+
// that is briefly undefined mid-render has to render empty rather than tear the
|
|
198
|
+
// render down. A handler runs in an event listener - not in an effect, once,
|
|
199
|
+
// when the user clicked - so there is no transient failure to absorb, only a
|
|
200
|
+
// bug to report, and an exception belongs in the console with its stack. That
|
|
201
|
+
// is what `@click="save"` has always done (the call happens outside the try,
|
|
202
|
+
// on the returned function); this is what makes `@click="save()"` and
|
|
203
|
+
// `@click="count++"` behave the same rather than the other way round.
|
|
204
|
+
//
|
|
205
|
+
// ReferenceError is the exception, for the reason it always is: it is not yet
|
|
206
|
+
// decidable at throw time. A factory assigns its names to the store when it
|
|
207
|
+
// returns, so a click while one is still in flight throws for a name that is
|
|
208
|
+
// about to exist - reportExprError re-checks after the scripts settle and stays
|
|
209
|
+
// quiet if it arrived, which throwing here would replace with a false alarm
|
|
210
|
+
const evalHandler = (expr: string, scope: Record<string, any>, extras: Record<string, any>): any => {
|
|
182
211
|
try {
|
|
183
|
-
return
|
|
212
|
+
return runExpr(expr, scope, extras)
|
|
184
213
|
} catch (error) {
|
|
214
|
+
if (!(error instanceof ReferenceError)) throw error
|
|
185
215
|
reportExprError(expr, scope, error)
|
|
186
216
|
return undefined
|
|
187
217
|
}
|
|
@@ -225,7 +255,7 @@ const bindEvent = (el: Element, attr: string, expr: string, scope: Record<string
|
|
|
225
255
|
if (mods.has("prevent")) event.preventDefault()
|
|
226
256
|
if (mods.has("stop")) event.stopPropagation()
|
|
227
257
|
|
|
228
|
-
const handler =
|
|
258
|
+
const handler = evalHandler(expr, scope, { $event: event })
|
|
229
259
|
if (typeof handler === "function") handler.call(el, event)
|
|
230
260
|
}, { once: mods.has("once"), capture: mods.has("capture") })
|
|
231
261
|
}
|
|
@@ -256,7 +286,7 @@ const wireTagEvent = (instance: Component79, attr: string, expr: string, scope:
|
|
|
256
286
|
// and the creation effect's definition guard no-ops a spurious wake - but
|
|
257
287
|
// "what a handler reads is nobody's dependency" shouldn't hinge on either
|
|
258
288
|
untracked(() => {
|
|
259
|
-
const handler =
|
|
289
|
+
const handler = evalHandler(expr, scope, { $event: event })
|
|
260
290
|
if (typeof handler === "function") handler(event)
|
|
261
291
|
})
|
|
262
292
|
}
|
|
@@ -1839,7 +1869,35 @@ const declareProps = (store: Record<string, any>, props: PropDecl[] | null) => {
|
|
|
1839
1869
|
const setupSignature = (script: TagBlock): PropDecl[] | null => {
|
|
1840
1870
|
const pattern = script.attrs[":setup"]
|
|
1841
1871
|
if (pattern === undefined) return null
|
|
1842
|
-
|
|
1872
|
+
if (pattern.trim() === "") return []
|
|
1873
|
+
const props = parsePropsPattern(pattern)
|
|
1874
|
+
if (!props) warnUnreadableSignature(script, pattern)
|
|
1875
|
+
return props
|
|
1876
|
+
}
|
|
1877
|
+
|
|
1878
|
+
// script blocks already warned about, keyed by the block itself - parsed once
|
|
1879
|
+
// and shared by every instance of a definition, the same reason warnUndeclared
|
|
1880
|
+
// keys on the template node. It matters for a smaller reason here too:
|
|
1881
|
+
// setupSignature is called three times per render (both declared-name passes
|
|
1882
|
+
// and the script loop's declareProps), so even one mount would say it thrice
|
|
1883
|
+
const signatureWarned = new WeakSet<TagBlock>()
|
|
1884
|
+
|
|
1885
|
+
// a value that isn't a props pattern reads as "declared no signature", which is
|
|
1886
|
+
// the most permissive mode there is - so a typo doesn't fail, it quietly opts
|
|
1887
|
+
// the component out of the contract it was trying to write. That is now the
|
|
1888
|
+
// only accidental route left to permissive: a bare :setup is closed and `_` is
|
|
1889
|
+
// the opt-out you have to ask for, so the mode nothing lands in by accident is
|
|
1890
|
+
// still reachable by getting it wrong. Both of parsePropsPattern's nulls count
|
|
1891
|
+
// - not-an-object (",{ a }", "props") and unbalanced ("{ a, b") - and only `_`
|
|
1892
|
+
// is exempt, because intent is the sole thing separating it from the typos
|
|
1893
|
+
const warnUnreadableSignature = (script: TagBlock, pattern: string) => {
|
|
1894
|
+
if (pattern.trim() === "_" || signatureWarned.has(script)) return
|
|
1895
|
+
signatureWarned.add(script)
|
|
1896
|
+
console.warn(
|
|
1897
|
+
`jq79: :setup="${pattern}" is not a props pattern, so this component declares no ` +
|
|
1898
|
+
`signature and takes whatever a parent passes - write the props it takes ` +
|
|
1899
|
+
`("{ a, b }"), a bare :setup for none, or "_" to stay open on purpose`
|
|
1900
|
+
)
|
|
1843
1901
|
}
|
|
1844
1902
|
|
|
1845
1903
|
// every prop name a component's scripts declare, across both script modes.
|