jq79 0.3.25 → 0.3.26

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/src/jq79.ts CHANGED
@@ -119,8 +119,13 @@ const findComponentKey = (scope: Record<string, any>, tag: string): string | nul
119
119
  // become camelCase. Props stay live: a parent effect re-evaluates each
120
120
  // expression and writes it into the child's store. The component variable is
121
121
  // reactive too - while it's undefined (e.g. an `await import(...)` still in
122
- // flight) nothing renders, and the child appears when it resolves
123
- const renderNestedComponent = (key: string, node: TemplateNode, scope: Record<string, any>, fx: EffectScope): Node => {
122
+ // flight) nothing renders, and the child appears when it resolves.
123
+ // `shadow` is the parent's style mode, carried down the whole render: a child
124
+ // of a shadow-rendered component renders inside that shadow root, so its
125
+ // <style> has to go in there with it - document.head can't reach into a shadow
126
+ // tree, and a style that never applies to its own component would still be
127
+ // restyling the page around it
128
+ const renderNestedComponent = (key: string, node: TemplateNode, scope: Record<string, any>, fx: EffectScope, shadow: boolean): Node => {
124
129
  const anchor = document.createComment(key)
125
130
  const wrapper = document.createDocumentFragment()
126
131
  wrapper.appendChild(anchor)
@@ -166,8 +171,11 @@ const renderNestedComponent = (key: string, node: TemplateNode, scope: Record<st
166
171
  const seed = untracked(() =>
167
172
  Object.fromEntries(Object.entries(props).map(([name, expr]) => [name, evalExpr(expr, scope)]))
168
173
  )
174
+ // mounting into a fragment attaches no shadow root of its own: a
175
+ // shadow-rendered child keeps its <style> elements inline, next to the DOM
176
+ // they style, and the parent's shadow root is what scopes both
169
177
  const holder = document.createDocumentFragment()
170
- instance.render(seed).mount(holder)
178
+ ;(shadow ? instance.renderShadow(seed) : instance.render(seed)).mount(holder)
171
179
  anchor.parentNode!.insertBefore(holder, anchor.nextSibling)
172
180
 
173
181
  const syncFx = createEffectScope(scope)
@@ -228,7 +236,7 @@ const createWithScope = (expr: string, scope: Record<string, any>): Record<strin
228
236
  // normally. :if/:elseif/:else/:each are handled by renderNodes, which decides
229
237
  // *whether*/*how many times* a node is rendered before calling this. Tags
230
238
  // matching a PascalCase scope variable render as nested components instead
231
- const renderNode = (node: TemplateNode, outerScope: Record<string, any>, fx: EffectScope): Node => {
239
+ const renderNode = (node: TemplateNode, outerScope: Record<string, any>, fx: EffectScope, shadow: boolean): Node => {
232
240
  // :with applies to the element's own bindings (@events, :attrs) and its
233
241
  // whole subtree. On a :each element the item scope is already in place, so
234
242
  // :with="item" works
@@ -236,7 +244,7 @@ const renderNode = (node: TemplateNode, outerScope: Record<string, any>, fx: Eff
236
244
  const scope = withExpr !== undefined ? createWithScope(withExpr, outerScope) : outerScope
237
245
 
238
246
  const componentKey = findComponentKey(scope, node.tag)
239
- if (componentKey) return renderNestedComponent(componentKey, node, scope, fx)
247
+ if (componentKey) return renderNestedComponent(componentKey, node, scope, fx, shadow)
240
248
 
241
249
  const el = document.createElement(node.tag)
242
250
 
@@ -252,7 +260,7 @@ const renderNode = (node: TemplateNode, outerScope: Record<string, any>, fx: Eff
252
260
  const key = findComponentKey(scope, node.tag)
253
261
  if (!key) return
254
262
  upgraded = true
255
- el.replaceWith(renderNestedComponent(key, node, scope, fx))
263
+ el.replaceWith(renderNestedComponent(key, node, scope, fx, shadow))
256
264
  })
257
265
  }
258
266
 
@@ -287,7 +295,7 @@ const renderNode = (node: TemplateNode, outerScope: Record<string, any>, fx: Eff
287
295
  } else if (htmlExpr !== undefined) {
288
296
  fx.effect(() => { el.innerHTML = sanitizeHTML(String(evalExpr(htmlExpr, scope) ?? "")) })
289
297
  } else {
290
- el.appendChild(renderNodes(node.children, scope, fx))
298
+ el.appendChild(renderNodes(node.children, scope, fx, shadow))
291
299
  }
292
300
 
293
301
  return el
@@ -297,7 +305,7 @@ const renderNode = (node: TemplateNode, outerScope: Record<string, any>, fx: Eff
297
305
  // can be swapped in place without disturbing sibling positions. Only depends
298
306
  // on whatever the branch expressions read (e.g. "score"), and skips
299
307
  // rebuilding entirely when the active branch hasn't actually changed
300
- const renderConditional = (branches: ConditionalBranch[], scope: Record<string, any>, fx: EffectScope): Node => {
308
+ const renderConditional = (branches: ConditionalBranch[], scope: Record<string, any>, fx: EffectScope, shadow: boolean): Node => {
301
309
  const anchor = document.createComment("if")
302
310
  const wrapper = document.createDocumentFragment()
303
311
  wrapper.appendChild(anchor)
@@ -317,7 +325,7 @@ const renderConditional = (branches: ConditionalBranch[], scope: Record<string,
317
325
  if (!next) return
318
326
 
319
327
  branchFx = createEffectScope(scope)
320
- current = renderNode(next.node, scope, branchFx)
328
+ current = renderNode(next.node, scope, branchFx, shadow)
321
329
  anchor.parentNode!.insertBefore(current, anchor.nextSibling)
322
330
  })
323
331
 
@@ -346,7 +354,7 @@ type EachEntry = { key: any; item: any; scope: Record<string, any>; node: Node;
346
354
  // the first change - add :key for anything that gets reordered or filtered.
347
355
  // Each item gets its own scope via Object.create(scope), so `item`/`$index`
348
356
  // shadow same-named outer bindings without copying the parent scope's keys
349
- const renderEach = (node: TemplateNode, scope: Record<string, any>, fx: EffectScope): Node => {
357
+ const renderEach = (node: TemplateNode, scope: Record<string, any>, fx: EffectScope, shadow: boolean): Node => {
350
358
  const match = node.attrs[":each"].match(EACH_PATTERN)
351
359
  if (!match) return document.createComment(`invalid :each expression "${node.attrs[":each"]}"`)
352
360
 
@@ -382,7 +390,7 @@ const renderEach = (node: TemplateNode, scope: Record<string, any>, fx: EffectSc
382
390
  existing?.node.parentNode?.removeChild(existing.node)
383
391
 
384
392
  const itemFx = createEffectScope(scope)
385
- return { key, item, scope: itemScope, fx: itemFx, node: renderNode(itemNode, itemScope, itemFx) }
393
+ return { key, item, scope: itemScope, fx: itemFx, node: renderNode(itemNode, itemScope, itemFx, shadow) }
386
394
  })
387
395
 
388
396
  const nextKeys = new Set(nextEntries.map(entry => entry.key))
@@ -407,7 +415,7 @@ const renderEach = (node: TemplateNode, scope: Record<string, any>, fx: EffectSc
407
415
 
408
416
  // renders a list of sibling template nodes (text + elements), grouping
409
417
  // consecutive :if/:elseif/:else nodes into a single conditional block
410
- const renderNodes = (nodes: (TemplateNode | string)[], scope: Record<string, any>, fx: EffectScope): DocumentFragment => {
418
+ const renderNodes = (nodes: (TemplateNode | string)[], scope: Record<string, any>, fx: EffectScope, shadow = false): DocumentFragment => {
411
419
  const fragment = document.createDocumentFragment()
412
420
  let i = 0
413
421
 
@@ -425,7 +433,7 @@ const renderNodes = (nodes: (TemplateNode | string)[], scope: Record<string, any
425
433
  }
426
434
 
427
435
  if (":each" in node.attrs) {
428
- fragment.appendChild(renderEach(node, scope, fx))
436
+ fragment.appendChild(renderEach(node, scope, fx, shadow))
429
437
  i++
430
438
  continue
431
439
  }
@@ -455,19 +463,19 @@ const renderNodes = (nodes: (TemplateNode | string)[], scope: Record<string, any
455
463
  const elseNode = nextBranch(":else")
456
464
  if (elseNode) branches.push({ node: elseNode })
457
465
 
458
- fragment.appendChild(renderConditional(branches, scope, fx))
466
+ fragment.appendChild(renderConditional(branches, scope, fx, shadow))
459
467
  continue
460
468
  }
461
469
 
462
- fragment.appendChild(renderNode(node, scope, fx))
470
+ fragment.appendChild(renderNode(node, scope, fx, shadow))
463
471
  i++
464
472
  }
465
473
 
466
474
  return fragment
467
475
  }
468
476
 
469
- export const renderComponent = (component: Component79, data: ReactiveDeepData<Record<string, any>>): Node =>
470
- renderNodes(component.template, data, createEffectScope(data))
477
+ export const renderComponent = (component: Component79, data: ReactiveDeepData<Record<string, any>>, shadow = false): Node =>
478
+ renderNodes(component.template, data, createEffectScope(data), shadow)
471
479
 
472
480
  type ComponentParts = {
473
481
  template: TemplateNode[]
@@ -950,7 +958,7 @@ export class Component79 {
950
958
  })
951
959
 
952
960
  const content = document.createDocumentFragment()
953
- content.append(this.startMarker, renderNodes(this.template, store, fx), this.endMarker)
961
+ content.append(this.startMarker, renderNodes(this.template, store, fx, shadow), this.endMarker)
954
962
  this.content = content
955
963
 
956
964
  if (shadow) {