jq79 0.4.15 → 0.4.17

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.4.15",
3
+ "version": "0.4.17",
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
@@ -7,6 +7,14 @@ import { transformSetupScript, transformFactoryScript, parsePropsPattern, parseF
7
7
 
8
8
  export { $, $$, $create } from "./dom"
9
9
  export { $reactive } from "./reactive"
10
+
11
+ // the package version, substituted at build time (tsup/vitest `define`, read
12
+ // from package.json - releases bump it there and nowhere else). The typeof
13
+ // guard is what keeps the raw source runnable: tests and any bundler that
14
+ // doesn't define it see a bare identifier, not a ReferenceError
15
+ declare const __JQ79_VERSION__: string
16
+ const VERSION = typeof __JQ79_VERSION__ === "string" ? __JQ79_VERSION__ : "0.0.0-dev"
17
+
10
18
  type TemplateNode = {
11
19
  tag: string
12
20
  attrs: Record<string, string>
@@ -1460,9 +1468,11 @@ const componentPartsFrom = (elements: Element[], hashSource: string): ComponentP
1460
1468
  return { template, scripts, styles }
1461
1469
  }
1462
1470
 
1463
- // loads .html URLs as components, delegating anything else to native import()
1471
+ // loads .html URLs as components, delegating anything else to native import().
1472
+ // Goes to fetchComponent rather than Component79.fetch because an import wants
1473
+ // the component, not the chainable handle the public entry point returns
1464
1474
  const importResource = (url: string): Promise<any> =>
1465
- /\.html?([?#]|$)/.test(url) ? Component79.fetch(url) : import(url)
1475
+ /\.html?([?#]|$)/.test(url) ? fetchComponent(url) : import(url)
1466
1476
 
1467
1477
  // ---------------------------------------------------------------------------
1468
1478
  // naming scripts for devtools
@@ -1773,6 +1783,10 @@ const fetchComponent = async (url: string): Promise<Component79> => {
1773
1783
  // jq79.detach() // detach, keeping state - mount() re-attaches
1774
1784
  // .destroy() // dispose effects and remove styles
1775
1785
  export class Component79 {
1786
+ // the version of jq79 this class came from, so a page can tell which build it
1787
+ // loaded (a CDN <script> pins nothing on its own)
1788
+ static readonly version: string = VERSION
1789
+
1776
1790
  template: TemplateNode[]
1777
1791
  scripts: TagBlock[]
1778
1792
  styles: TagBlock[]
@@ -1903,13 +1917,23 @@ export class Component79 {
1903
1917
  return true
1904
1918
  }
1905
1919
 
1906
- static fetch(url: string): Promise<Component79>
1907
- static fetch(urls: string[]): Promise<Component79[]>
1908
- // an array of URLs fetches them all at once and resolves to the components in
1909
- // the same order, so one await destructures them - and, like Promise.all, the
1910
- // first failure rejects the whole thing
1911
- static fetch(urls: string | string[]): Promise<Component79 | Component79[]> {
1912
- return Array.isArray(urls) ? Promise.all(urls.map(fetchComponent)) : fetchComponent(urls)
1920
+ // downloads and parses a component, handing back a PendingComponent79: a
1921
+ // handle that can be mounted right away, and that awaits to this component -
1922
+ // so both of these are the whole program
1923
+ //
1924
+ // Component79.fetch("./app.html").mount("main")
1925
+ // const app = await Component79.fetch("./app.html")
1926
+ static fetch(url: string): PendingComponent79 {
1927
+ if (Array.isArray(url)) throw new TypeError("Component79.fetch takes one URL; use fetchAll for an array")
1928
+ return new PendingComponent79(fetchComponent(url))
1929
+ }
1930
+
1931
+ // fetches them all at once and resolves to the components in the same order,
1932
+ // so one await destructures them - and, like Promise.all, the first failure
1933
+ // rejects the whole thing. A plain promise, not a handle: mounting a *list*
1934
+ // of components has no single meaning
1935
+ static fetchAll(urls: string[]): Promise<Component79[]> {
1936
+ return Promise.all(urls.map(fetchComponent))
1913
1937
  }
1914
1938
 
1915
1939
  // subscribes to this instance's $emit events, on top of the DOM CustomEvent
@@ -2175,6 +2199,90 @@ export class Component79 {
2175
2199
  }
2176
2200
  }
2177
2201
 
2202
+ // what Component79.fetch() hands back: a component that hasn't arrived yet.
2203
+ //
2204
+ // Every method queues onto the fetch and returns the handle, so a whole page
2205
+ // is one expression and the calls run in the order they were written:
2206
+ //
2207
+ // C79.fetch("./app.html").on("save", persist).mount("main", { user })
2208
+ //
2209
+ // It is also thenable, resolving to the Component79 itself - which is what
2210
+ // keeps `await Component79.fetch(url)` (and importResource, and a handle
2211
+ // dropped into Promise.all) working exactly as before. Queued calls keep the
2212
+ // resolved value, so awaiting a chain gives the mounted component.
2213
+ //
2214
+ // The catch: mount() here returns the handle, not the component - there is no
2215
+ // component yet to return. That's why the whole lifecycle is on the handle and
2216
+ // not just mount(): nobody should have to await merely to destroy something.
2217
+ export class PendingComponent79 {
2218
+ // the fetch with every queued call chained onto it, each passing the
2219
+ // component through - so `chain` always settles to the component, however
2220
+ // many calls were queued, and a failure anywhere rejects the rest
2221
+ private chain: Promise<Component79>
2222
+
2223
+ constructor(component: Promise<Component79>) {
2224
+ this.chain = component
2225
+ }
2226
+
2227
+ private queue(action: (component: Component79) => void): this {
2228
+ this.chain = this.chain.then(component => {
2229
+ action(component)
2230
+ return component
2231
+ })
2232
+ return this
2233
+ }
2234
+
2235
+ then<TResult1 = Component79, TResult2 = never>(
2236
+ onfulfilled?: ((value: Component79) => TResult1 | PromiseLike<TResult1>) | null,
2237
+ onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null,
2238
+ ): Promise<TResult1 | TResult2> {
2239
+ return this.chain.then(onfulfilled, onrejected)
2240
+ }
2241
+
2242
+ // a chain nobody awaits reports a failed fetch as an unhandled rejection,
2243
+ // like any dropped promise chain - these are for callers who'd rather handle
2244
+ // it. catch() returns a promise, not a handle: the chain ends here
2245
+ catch<TResult = never>(onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | null): Promise<Component79 | TResult> {
2246
+ return this.chain.catch(onrejected)
2247
+ }
2248
+
2249
+ finally(onfinally?: (() => void) | null): Promise<Component79> {
2250
+ return this.chain.finally(onfinally)
2251
+ }
2252
+
2253
+ mount(parent: Element | ShadowRoot | DocumentFragment | string, data?: Record<string, any>): this {
2254
+ return this.queue(component => component.mount(parent, data))
2255
+ }
2256
+
2257
+ mountShadow(parent: Element | ShadowRoot | DocumentFragment | string, data?: Record<string, any>): this {
2258
+ return this.queue(component => component.mountShadow(parent, data))
2259
+ }
2260
+
2261
+ render(data: Record<string, any> = {}): this {
2262
+ return this.queue(component => component.render(data))
2263
+ }
2264
+
2265
+ renderShadow(data: Record<string, any> = {}): this {
2266
+ return this.queue(component => component.renderShadow(data))
2267
+ }
2268
+
2269
+ on(eventName: string, listener: EmitListener): this {
2270
+ return this.queue(component => component.on(eventName, listener))
2271
+ }
2272
+
2273
+ off(eventName: string, listener: EmitListener): this {
2274
+ return this.queue(component => component.off(eventName, listener))
2275
+ }
2276
+
2277
+ detach(): this {
2278
+ return this.queue(component => component.detach())
2279
+ }
2280
+
2281
+ destroy(): this {
2282
+ return this.queue(component => component.destroy())
2283
+ }
2284
+ }
2285
+
2178
2286
  export { Component79 as C79 }
2179
2287
 
2180
2288
  export const parseComponent = (component: string): Component79 => new Component79(component)