jtlt 0.12.0 → 0.13.0

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.
Files changed (57) hide show
  1. package/CHANGES.md +5 -0
  2. package/README.md +9 -5
  3. package/demo/calltemplate-params-demo.js +19 -15
  4. package/demo/codemirror.esm.js +140 -70
  5. package/demo/codemirror.js +9 -5
  6. package/demo/index.js +2 -1
  7. package/demo/vendor/fontoxpath/dist/fontoxpath.esm.js +508 -499
  8. package/demo/vendor/jamilih/dist/jml.mjs +516 -225
  9. package/demo/vendor/jsonpath-plus/dist/index-browser-esm.js +11 -5
  10. package/dist/AbstractJoiningTransformer.d.ts +12 -20
  11. package/dist/AbstractJoiningTransformer.d.ts.map +1 -1
  12. package/dist/DOMJoiningTransformer.d.ts +502 -19
  13. package/dist/DOMJoiningTransformer.d.ts.map +1 -1
  14. package/dist/JSONJoiningTransformer.d.ts +41 -25
  15. package/dist/JSONJoiningTransformer.d.ts.map +1 -1
  16. package/dist/JSONPathTransformer.d.ts +20 -20
  17. package/dist/JSONPathTransformer.d.ts.map +1 -1
  18. package/dist/JSONPathTransformerContext.d.ts +103 -80
  19. package/dist/JSONPathTransformerContext.d.ts.map +1 -1
  20. package/dist/StringJoiningTransformer.d.ts +42 -28
  21. package/dist/StringJoiningTransformer.d.ts.map +1 -1
  22. package/dist/XPathTransformer.d.ts +11 -11
  23. package/dist/XPathTransformer.d.ts.map +1 -1
  24. package/dist/XPathTransformerContext.d.ts +47 -45
  25. package/dist/XPathTransformerContext.d.ts.map +1 -1
  26. package/dist/XSLTStyleJSONPathResolver.d.ts +1 -1
  27. package/dist/XSLTStyleJSONPathResolver.d.ts.map +1 -1
  28. package/dist/index-browser.d.ts +2 -2
  29. package/dist/index-browser.d.ts.map +1 -1
  30. package/dist/index-node.d.ts +2 -2
  31. package/dist/index-node.d.ts.map +1 -1
  32. package/dist/index.d.ts +126 -167
  33. package/dist/index.d.ts.map +1 -1
  34. package/docs/API.expanded.md +6 -4
  35. package/docs/API.md +1 -1
  36. package/docs/TO-DO.md +4 -2
  37. package/eslint.config.js +17 -1
  38. package/package.json +23 -22
  39. package/pnpm-workspace.yaml +32 -4
  40. package/rollup.config.js +42 -0
  41. package/src/AbstractJoiningTransformer.js +8 -1
  42. package/src/DOMJoiningTransformer.js +56 -27
  43. package/src/JSONJoiningTransformer.js +64 -44
  44. package/src/JSONPathTransformer.js +86 -85
  45. package/src/JSONPathTransformerContext.js +95 -48
  46. package/src/StringJoiningTransformer.js +48 -23
  47. package/src/XPathTransformer.js +18 -14
  48. package/src/XPathTransformerContext.js +173 -123
  49. package/src/index-browser.js +1 -0
  50. package/src/index-node.js +2 -0
  51. package/src/index.js +125 -68
  52. package/test.html +17 -0
  53. package/test.js +45 -0
  54. package/tsconfig-prod.json +1 -0
  55. package/tsconfig.json +3 -2
  56. package/dist/types.d.ts +0 -204
  57. package/dist/types.d.ts.map +0 -1
@@ -1,6 +1,7 @@
1
- /* eslint-disable sonarjs/updated-loop-counter -- Ok */
2
1
  /* eslint-disable unicorn/prefer-global-this -- Easier */
2
+ /* eslint-disable unicorn/no-break-in-nested-loop -- Performance to avoid function? */
3
3
  /* eslint-disable sonarjs/no-control-regex -- Intentional */
4
+ /* eslint-disable unicorn/no-top-level-assignment-in-function -- Only need module level */
4
5
  /*
5
6
  Possible todos:
6
7
  0. Add XSLT to JML-string stylesheet (or even vice versa)
@@ -23,11 +24,28 @@ Other Todos:
23
24
  */
24
25
 
25
26
  /**
26
- * @typedef {Window & {DocumentFragment: any}} HTMLWindow
27
+ * @typedef {Window & {DocumentFragment: typeof DocumentFragment}} HTMLWindow
27
28
  */
28
29
 
29
30
  /**
30
- * @typedef {any} ArbitraryValue
31
+ * @typedef {unknown} ArbitraryValue
32
+ */
33
+
34
+ /**
35
+ * @typedef {unknown} StoredValue
36
+ */
37
+
38
+ /* eslint-disable jsdoc/reject-any-type -- user-defined callback arguments */
39
+ /**
40
+ * @typedef {any} UserArg
41
+ */
42
+ /**
43
+ * @typedef {any} ElementExpando
44
+ */
45
+ /* eslint-enable jsdoc/reject-any-type */
46
+
47
+ /**
48
+ * @typedef {HTMLElement & {[key: string]: ElementExpando}} ExpandoHTMLElement
31
49
  */
32
50
 
33
51
  /**
@@ -49,7 +67,7 @@ Other Todos:
49
67
  */
50
68
 
51
69
  /**
52
- * @type {import('jsdom').DOMWindow|HTMLWindow|undefined}
70
+ * @type {import('jsdom').DOMWindow|HTMLWindow|typeof globalThis|undefined}
53
71
  */
54
72
  let win;
55
73
 
@@ -152,9 +170,10 @@ function _appendNode(parent, child) {
152
170
  /**
153
171
  * Attach event in a cross-browser fashion.
154
172
  * @static
155
- * @param {HTMLElement} el DOM element to which to attach the event
173
+ * @template {HTMLElement} T
174
+ * @param {T} el DOM element to which to attach the event
156
175
  * @param {string} type The DOM event (without 'on') to attach to the element
157
- * @param {(evt: Event & {target: HTMLElement}) => void} handler The event handler to attach to the element
176
+ * @param {(evt: Event & {target: T}) => void} handler The event handler to attach to the element
158
177
  * @param {boolean} [capturing] Whether or not the event should be
159
178
  * capturing (W3C-browsers only); default is false; NOT IN USE
160
179
  * @returns {void}
@@ -165,13 +184,13 @@ function _addEvent(el, type, handler, capturing) {
165
184
  }
166
185
 
167
186
  /**
168
- * Creates a text node of the result of resolving an entity or character reference.
169
- * @param {'entity'|'decimal'|'hexadecimal'} type Type of reference
170
- * @param {string} prefix Text to prefix immediately after the "&"
171
- * @param {string} arg The body of the reference
172
- * @throws {TypeError}
173
- * @returns {Text} The text node of the resolved reference
174
- */
187
+ * Creates a text node of the result of resolving an entity or character reference.
188
+ * @param {'entity'|'decimal'|'hexadecimal'} type Type of reference
189
+ * @param {string} prefix Text to prefix immediately after the "&"
190
+ * @param {string} arg The body of the reference
191
+ * @throws {TypeError}
192
+ * @returns {Text} The text node of the resolved reference
193
+ */
175
194
  function _createSafeReference(type, prefix, arg) {
176
195
  /* c8 ignore next 3 */
177
196
  if (!doc) {
@@ -184,16 +203,17 @@ function _createSafeReference(type, prefix, arg) {
184
203
  }
185
204
  const elContainer = doc.createElement('div');
186
205
  // Todo: No workaround for XML?
187
- // // eslint-disable-next-line no-unsanitized/property
206
+ // eslint-disable-next-line no-unsanitized/property
188
207
  elContainer.innerHTML = '&' + prefix + arg + ';';
208
+ // eslint-disable-next-line unicorn/prefer-dom-node-html-methods -- No Safari support
189
209
  return doc.createTextNode(elContainer.innerHTML);
190
210
  }
191
211
 
192
212
  /**
193
- * @param {string} n0 Whole expression match (including "-")
194
- * @param {string} n1 Lower-case letter match
195
- * @returns {string} Uppercased letter
196
- */
213
+ * @param {string} n0 Whole expression match (including "-")
214
+ * @param {string} n1 Lower-case letter match
215
+ * @returns {string} Uppercased letter
216
+ */
197
217
  function _upperCase(n0, n1) {
198
218
  return n1.toUpperCase();
199
219
  }
@@ -207,24 +227,31 @@ function _isNullish(o) {
207
227
  return o === null || o === undefined;
208
228
  }
209
229
 
230
+ /**
231
+ * @param {unknown} item
232
+ * @returns {item is HTMLElement}
233
+ */
234
+ function _isHTMLElement(item) {
235
+ return Boolean(item && typeof item === 'object' && 'nodeType' in item && item.nodeType === 1);
236
+ }
237
+
210
238
  // Todo: Make as public utility, but also return types for undefined, boolean, number, document, etc.
211
239
  /**
212
- * @private
213
- * @static
214
- * @param {string|JamilihAttributes|JamilihArray|JamilihChildren|
215
- * JamilihDocumentFragment|JamilihAttributeNode|
216
- * JamilihOptions|HTMLElement|Document|DocumentFragment|null|undefined} item
217
- * @returns {"string"|"null"|"array"|"element"|"fragment"|"object"|
218
- * "symbol"|"bigint"|"function"|"number"|"boolean"|"undefined"|
219
- * "document"|"processing-instruction"|"non-container node"}
220
- */
240
+ * @private
241
+ * @static
242
+ * @param {string|JamilihAttributes|JamilihArray|JamilihChildren|
243
+ * JamilihDocumentFragment|JamilihAttributeNode|
244
+ * JamilihOptions|HTMLElement|Document|DocumentFragment|null|undefined} item
245
+ * @returns {"string"|"null"|"array"|"element"|"fragment"|"object"|
246
+ * "symbol"|"bigint"|"function"|"number"|"boolean"|"undefined"|
247
+ * "document"|"processing-instruction"|"non-container node"}
248
+ */
221
249
  function _getType(item) {
222
- const type = typeof item;
223
-
224
250
  // Appease TS
225
251
  if (typeof item === 'string' || typeof item === 'undefined') {
226
252
  return 'string';
227
253
  }
254
+ const type = typeof item;
228
255
  switch (type) {
229
256
  case 'object':
230
257
  if (item === null) {
@@ -254,23 +281,31 @@ function _getType(item) {
254
281
  }
255
282
 
256
283
  /**
257
- * @private
258
- * @static
259
- * @param {DocumentFragment} frag
260
- * @param {Node} node
261
- * @returns {DocumentFragment}
262
- */
284
+ * @private
285
+ * @static
286
+ * @param {DocumentFragment} frag
287
+ * @param {Node} node
288
+ * @returns {DocumentFragment}
289
+ */
263
290
  function _fragReducer(frag, node) {
264
291
  frag.append(node);
265
292
  return frag;
266
293
  }
267
294
 
268
295
  /**
269
- * @private
270
- * @static
271
- * @param {Object<string, string>} xmlnsObj
272
- * @returns {(...n: string[]) => string}
273
- */
296
+ * @param {string} str
297
+ * @returns {string}
298
+ */
299
+ function escapeReplacer(str) {
300
+ return str.replaceAll('$', '$$$$');
301
+ }
302
+
303
+ /**
304
+ * @private
305
+ * @static
306
+ * @param {Object<string, string>} xmlnsObj
307
+ * @returns {(...n: string[]) => string}
308
+ */
274
309
  function _replaceDefiner(xmlnsObj) {
275
310
  /**
276
311
  * @param {string[]} n
@@ -278,10 +313,10 @@ function _replaceDefiner(xmlnsObj) {
278
313
  */
279
314
  return function (...n) {
280
315
  const n0 = n[0];
281
- let retStr = xmlnsObj[''] ? ' xmlns="' + xmlnsObj[''] + '"' : n0; // Preserve XHTML
316
+ let retStr = xmlnsObj[''] ? ' xmlns="' + escapeReplacer(xmlnsObj['']) + '"' : n0; // Preserve XHTML
282
317
  for (const [ns, xmlnsVal] of Object.entries(xmlnsObj)) {
283
318
  if (ns !== '') {
284
- retStr += ' xmlns:' + ns + '="' + xmlnsVal + '"';
319
+ retStr += ' xmlns:' + ns + '="' + escapeReplacer(xmlnsVal) + '"';
285
320
  }
286
321
  }
287
322
  return retStr;
@@ -312,30 +347,30 @@ function _childrenToJML(node) {
312
347
  /**
313
348
  * Keep this in sync with `JamilihArray`'s first argument (minus `Document`).
314
349
  * @typedef {JamilihDoc|JamilihDoctype|JamilihTextNode|
315
- * JamilihAttributeNode|JamilihOptions|ElementName|HTMLElement|
316
- * JamilihDocumentFragment
317
- * } JamilihFirstArg
318
- */
350
+ * JamilihAttributeNode|JamilihOptions|ElementName|HTMLElement|
351
+ * JamilihDocumentFragment
352
+ * } JamilihFirstArg
353
+ */
319
354
 
320
355
  /**
321
- * @callback JamilihAppender
322
- * @param {JamilihArray|JamilihFirstArg|Node|TextNodeString} childJML
323
- * @returns {void}
324
- */
356
+ * @callback JamilihAppender
357
+ * @param {JamilihArray|JamilihArrayLike|JamilihFirstArg|Node|TextNodeString} childJML
358
+ * @returns {void}
359
+ */
325
360
 
326
361
  /**
327
- * @private
328
- * @static
329
- * @param {ParentNode} node
330
- * @returns {JamilihAppender}
331
- */
362
+ * @private
363
+ * @static
364
+ * @param {ParentNode} node
365
+ * @returns {JamilihAppender}
366
+ */
332
367
  function _appendJML(node) {
333
368
  return function (childJML) {
334
369
  if (typeof childJML === 'string' || typeof childJML === 'number') {
335
370
  throw new TypeError('Unexpected text string/number in the head');
336
371
  }
337
372
  if (Array.isArray(childJML)) {
338
- node.append(jml(...childJML));
373
+ node.append(jml(...(/** @type {JamilihArray} */childJML)));
339
374
  } else if (typeof childJML === 'object' && 'nodeType' in childJML) {
340
375
  node.append(childJML);
341
376
  } else {
@@ -345,23 +380,23 @@ function _appendJML(node) {
345
380
  }
346
381
 
347
382
  /**
348
- * @callback appender
349
- * @param {JamilihArray|JamilihFirstArg|Node|TextNodeString} childJML
350
- * @returns {void}
351
- */
383
+ * @callback appender
384
+ * @param {JamilihArray|JamilihArrayLike|JamilihFirstArg|Node|TextNodeString} childJML
385
+ * @returns {void}
386
+ */
352
387
 
353
388
  /**
354
- * @private
355
- * @static
356
- * @param {ParentNode} node
357
- * @returns {appender}
358
- */
389
+ * @private
390
+ * @static
391
+ * @param {ParentNode} node
392
+ * @returns {appender}
393
+ */
359
394
  function _appendJMLOrText(node) {
360
395
  return function (childJML) {
361
396
  if (typeof childJML === 'string' || typeof childJML === 'number') {
362
397
  node.append(String(childJML));
363
398
  } else if (Array.isArray(childJML)) {
364
- node.append(jml(...childJML));
399
+ node.append(jml(...(/** @type {JamilihArray} */childJML)));
365
400
  } else if (typeof childJML === 'object' && 'nodeType' in childJML) {
366
401
  node.append(childJML);
367
402
  } else {
@@ -371,9 +406,9 @@ function _appendJMLOrText(node) {
371
406
  }
372
407
 
373
408
  /**
374
- * @private
375
- * @static
376
- */
409
+ * @private
410
+ * @static
411
+ */
377
412
  /*
378
413
  function _DOMfromJMLOrString (childNodeJML) {
379
414
  if (typeof childNodeJML === 'string') {
@@ -384,9 +419,9 @@ function _DOMfromJMLOrString (childNodeJML) {
384
419
  */
385
420
 
386
421
  /**
387
- * @typedef {HTMLElement|DocumentFragment|Comment|Attr|
388
- * Text|Document|DocumentType|ProcessingInstruction|CDATASection} JamilihReturn
389
- */
422
+ * @typedef {HTMLElement|DocumentFragment|Comment|Attr|
423
+ * Text|Document|DocumentType|ProcessingInstruction|CDATASection} JamilihReturn
424
+ */
390
425
  // 'string|JamilihOptions|JamilihDocumentFragment|JamilihAttributes|(string|JamilihArray)[]
391
426
 
392
427
  /**
@@ -403,11 +438,11 @@ function _DOMfromJMLOrString (childNodeJML) {
403
438
 
404
439
  /**
405
440
  * @typedef {{
406
- * open?: boolean|ShadowRootJamilihArrayContainer,
407
- * closed?: boolean|ShadowRootJamilihArrayContainer,
408
- * template?: string|HTMLTemplateElement|TemplateJamilihArray,
409
- * content?: ShadowRootJamilihArrayContainer|DocumentFragment
410
- * }} JamilihShadowRootObject
441
+ * open?: boolean|ShadowRootJamilihArrayContainer,
442
+ * closed?: boolean|ShadowRootJamilihArrayContainer,
443
+ * template?: string|HTMLTemplateElement|TemplateJamilihArray,
444
+ * content?: ShadowRootJamilihArrayContainer|DocumentFragment
445
+ * }} JamilihShadowRootObject
411
446
  */
412
447
 
413
448
  /**
@@ -429,18 +464,21 @@ function _DOMfromJMLOrString (childNodeJML) {
429
464
  */
430
465
 
431
466
  /**
432
- * @typedef {(this: HTMLElement, event: Event & {target: HTMLElement}) => void} EventHandler
467
+ * @template {HTMLElement} [T=HTMLElement]
468
+ * @typedef {(this: T, event: Event & {target: T}) => void} EventHandler
433
469
  */
434
470
 
435
471
  /**
472
+ * @template {HTMLElement} [T=HTMLElement]
436
473
  * @typedef {{
437
- * [key: string]: EventHandler|[EventHandler, boolean]
474
+ * [key: string]: EventHandler<T>|[EventHandler<T>, boolean]
438
475
  * }} OnAttributeObject
439
476
  */
440
477
 
441
478
  /**
479
+ * @template {HTMLElement} [T=HTMLElement]
442
480
  * @typedef {{
443
- * $on?: OnAttributeObject|null
481
+ * $on?: OnAttributeObject<T>|null
444
482
  * }} OnAttribute
445
483
  */
446
484
 
@@ -452,7 +490,6 @@ function _DOMfromJMLOrString (childNodeJML) {
452
490
  * @typedef {((this: HTMLElement, event?: Event) => void)} HandlerAttributeValue
453
491
  */
454
492
 
455
- /* eslint-disable jsdoc/valid-types -- jsdoc-type-pratt-parser Bug */
456
493
  /**
457
494
  * @typedef {{
458
495
  * [key: string]: HandlerAttributeValue
@@ -476,23 +513,22 @@ function _DOMfromJMLOrString (childNodeJML) {
476
513
  */
477
514
 
478
515
  /**
479
- * @typedef {{[key: string]: string|number|boolean|((this: DefineMixin, ...args: any[]) => any)}} DefineMixin
516
+ * @typedef {{[key: string]: unknown}} DefineMixin
480
517
  */
481
518
 
482
519
  /**
483
520
  * @typedef {{
484
521
  * new (): HTMLElement;
485
- * prototype: HTMLElement & {[key: string]: any}
522
+ * prototype: HTMLElement
486
523
  * }} DefineConstructor
487
524
  */
488
- /* eslint-enable jsdoc/valid-types -- https://github.com/jsdoc-type-pratt-parser/jsdoc-type-pratt-parser/issues/131 */
489
525
 
490
526
  /**
491
527
  * @typedef {(this: HTMLElement) => void} DefineUserConstructor
492
528
  */
493
529
 
494
530
  /**
495
- * @typedef {[DefineConstructor|DefineUserConstructor|DefineMixin, DefineOptions?]|[DefineConstructor|DefineUserConstructor, DefineMixin?, DefineOptions?]} DefineObjectArray
531
+ * @typedef {[DefineConstructor|DefineUserConstructor|DefineMixin, DefineOptions?]|[DefineMixin, DefineConstructor]|[DefineConstructor|DefineUserConstructor, DefineMixin?, DefineOptions?]} DefineObjectArray
496
532
  */
497
533
 
498
534
  /**
@@ -500,11 +536,27 @@ function _DOMfromJMLOrString (childNodeJML) {
500
536
  */
501
537
 
502
538
  /**
503
- * @typedef {{elem?: HTMLElement, [key: string]: any}} SymbolObject
539
+ * @template [T=ArbitraryValue]
540
+ * @template {HTMLElement} [U=HTMLElement]
541
+ * @typedef {T & {elem?: U}} SymbolObject
542
+ */
543
+
544
+ /**
545
+ * @template {HTMLElement} [T=HTMLElement]
546
+ * @typedef {(this: T, ...args: UserArg[]) => UserArg} SymbolMethod
547
+ */
548
+
549
+ /**
550
+ * @typedef {(...args: UserArg[]) => UserArg} BoundSymbolMethod
504
551
  */
505
552
 
506
553
  /**
507
- * @typedef {[symbol|string, ((this: HTMLElement, ...args: any[]) => any)|SymbolObject]} SymbolArray
554
+ * @template {HTMLElement} [T=HTMLElement]
555
+ * @typedef {[symbol|string, SymbolMethod<T>|SymbolObject<ArbitraryValue, T>]} SymbolArray
556
+ */
557
+
558
+ /**
559
+ * @typedef {BoundSymbolMethod|SymbolObject|ArbitraryValue} SymbolResult
508
560
  */
509
561
 
510
562
  /**
@@ -512,7 +564,7 @@ function _DOMfromJMLOrString (childNodeJML) {
512
564
  */
513
565
 
514
566
  /**
515
- * @typedef {[string, object]|string|{[key: string]: any}} PluginValue
567
+ * @typedef {[string, object]|string|object} PluginValue
516
568
  */
517
569
 
518
570
  /**
@@ -527,15 +579,15 @@ function _DOMfromJMLOrString (childNodeJML) {
527
579
 
528
580
  /**
529
581
  * @typedef {{
530
- * [key: string]: string|number|((this: HTMLElement, ...args: any[]) => any)
531
- * }} DataAttributeObject
532
- */
582
+ * [key: string]: string|number|((this: HTMLElement, ...args: UserArg[]) => UserArg)
583
+ * }} DataAttributeObject
584
+ */
533
585
 
534
586
  /**
535
587
  * @typedef {{
536
- * $data?: true|string[]|Map<any, any>|WeakMap<any, any>|DataAttributeObject|
588
+ * $data?: true|string[]|Map<HTMLElement, UserArg>|WeakMap<HTMLElement, UserArg>|DataAttributeObject|
537
589
  * [undefined, DataAttributeObject]|
538
- * [Map<any, any>|WeakMap<any, any>|undefined, DataAttributeObject]
590
+ * [Map<HTMLElement, UserArg>|WeakMap<HTMLElement, UserArg>|undefined, DataAttributeObject]
539
591
  * }} DataAttribute
540
592
  */
541
593
 
@@ -557,18 +609,16 @@ function _DOMfromJMLOrString (childNodeJML) {
557
609
  * }} JamilihShadowRootAttribute
558
610
  */
559
611
 
560
- /* eslint-disable jsdoc/valid-types -- jsdoc-type-pratt-parser Bug */
561
612
  /**
562
613
  * @typedef {{
563
614
  * is?: string|null,
564
615
  * $define?: DefineObject
565
616
  * }} DefineAttribute
566
617
  */
567
- /* eslint-enable jsdoc/valid-types -- jsdoc-type-pratt-parser Bug */
568
618
 
569
619
  /**
570
620
  * @typedef {{
571
- * $custom?: {[key: string]: any}
621
+ * $custom?: {[key: string]: unknown}
572
622
  * }} CustomAttribute
573
623
  */
574
624
 
@@ -641,21 +691,30 @@ function _DOMfromJMLOrString (childNodeJML) {
641
691
  */
642
692
 
643
693
  /**
694
+ * @typedef {Document|ElementName|HTMLElement|DocumentFragment|
695
+ * JamilihDocumentFragment|JamilihDoc|JamilihDoctype|JamilihTextNode|
696
+ * JamilihAttributeNode} JamilihFirstArgument
697
+ */
698
+
699
+ /**
700
+ * Array-form Jamilih input whose tuple positions were widened by operations
701
+ * such as `Array#map`.
644
702
  * @typedef {(
645
- * JamilihArray|TextNodeString|HTMLElement|Comment|ProcessingInstruction|
646
- * Text|DocumentFragment|JamilihProcessingInstruction|JamilihDocumentFragment|
647
- * PluginReference
648
- * )[]} JamilihChildren
703
+ * JamilihFirstArg|JamilihAttributes|JamilihArrayLike|TextNodeString|
704
+ * ShadowRoot|null
705
+ * )[]} JamilihArrayLike
649
706
  */
650
707
 
651
- // Todo: DocumentType, Comment, ProcessingInstruction, Text
652
- // Todo: JamilihCDATANode, JamilihComment, JamilihProcessingInstruction
653
708
  /**
654
- * @typedef {Document|ElementName|HTMLElement|DocumentFragment|
655
- * JamilihDocumentFragment|JamilihDoc|JamilihDoctype|JamilihTextNode|
656
- * JamilihAttributeNode} JamilihFirstArgument
709
+ * @typedef {(
710
+ * JamilihArray|JamilihArrayLike|TextNodeString|HTMLElement|Comment|
711
+ * ProcessingInstruction|Text|DocumentFragment|JamilihProcessingInstruction|
712
+ * JamilihDocumentFragment|PluginReference
713
+ * )[]} JamilihChildren
657
714
  */
658
715
 
716
+ // Todo: DocumentType, Comment, ProcessingInstruction, Text
717
+ // Todo: JamilihCDATANode, JamilihComment, JamilihProcessingInstruction
659
718
  /**
660
719
  * This would be clearer with overrides, but using as typedef.
661
720
  *
@@ -701,8 +760,8 @@ function _DOMfromJMLOrString (childNodeJML) {
701
760
 
702
761
  /**
703
762
  * @typedef {{
704
- * root: [Map<HTMLElement,any>|WeakMap<HTMLElement,any>, any],
705
- * [key: string]: [Map<HTMLElement,any>|WeakMap<HTMLElement,any>, any]
763
+ * root: [Map<HTMLElement,UserArg>|WeakMap<HTMLElement,UserArg>, UserArg],
764
+ * [key: string]: [Map<HTMLElement,UserArg>|WeakMap<HTMLElement,UserArg>, UserArg]
706
765
  * }} MapWithRoot
707
766
  */
708
767
 
@@ -714,7 +773,7 @@ function _DOMfromJMLOrString (childNodeJML) {
714
773
  * @typedef {object} JamilihOptions
715
774
  * @property {TraversalState} [$state]
716
775
  * @property {JamilihPlugin[]} [$plugins]
717
- * @property {MapWithRoot|[Map<HTMLElement,any>|WeakMap<HTMLElement,any>, any]} [$map]
776
+ * @property {MapWithRoot|[Map<HTMLElement,UserArg>|WeakMap<HTMLElement,UserArg>, UserArg]} [$map]
718
777
  */
719
778
 
720
779
  /**
@@ -754,27 +813,213 @@ function getMatchingPlugin(opts, pluginName) {
754
813
  });
755
814
  }
756
815
 
757
- /* eslint-disable jsdoc/valid-types -- pratt parser bug */
758
816
  /**
759
817
  * @template T
760
818
  * @typedef {T[keyof T]} ValueOf
761
819
  */
762
- /* eslint-enable jsdoc/valid-types -- pratt parser bug */
763
820
 
764
- /* eslint-disable jsdoc/valid-types -- pratt parser bug */
821
+ /**
822
+ * @template {JamilihArray} T
823
+ * @typedef {Extract<Extract<T[number], {$custom?: {[key: string]: unknown}}>['$custom'], object>} RawCustomFromJamilihArray
824
+ */
825
+
826
+ /**
827
+ * @template M
828
+ * @typedef {M extends object
829
+ * ? string extends keyof M
830
+ * ? object
831
+ * : M
832
+ * : object} SpecificDefineMixin
833
+ */
834
+
835
+ /**
836
+ * @template D
837
+ * @typedef {D extends [infer First, infer Second, ...ArbitraryValue[]]
838
+ * ? (First extends DefineMixin
839
+ * ? SpecificDefineMixin<First>
840
+ * : Second extends DefineMixin
841
+ * ? SpecificDefineMixin<Second>
842
+ * : object)
843
+ * : D extends [infer First]
844
+ * ? First extends DefineMixin
845
+ * ? SpecificDefineMixin<First>
846
+ * : object
847
+ * : D extends DefineMixin
848
+ * ? SpecificDefineMixin<D>
849
+ * : object} DefineMixinFromValue
850
+ */
851
+
852
+ /**
853
+ * @template {JamilihArray} T
854
+ * @typedef {T[number] extends infer Item
855
+ * ? Item extends {$define: infer D}
856
+ * ? DefineMixinFromValue<D>
857
+ * : never
858
+ * : never} RawDefineMixinFromJamilihArray
859
+ */
860
+
861
+ /**
862
+ * @template D
863
+ * @typedef {D extends [infer First, infer Second, ...ArbitraryValue[]]
864
+ * ? First extends DefineConstructor
865
+ * ? First['prototype']
866
+ * : Second extends DefineConstructor
867
+ * ? Second['prototype']
868
+ * : never
869
+ * : D extends DefineConstructor
870
+ * ? D['prototype']
871
+ * : never} ElementFromDefineValue
872
+ */
873
+
874
+ /**
875
+ * @template {JamilihArray} T
876
+ * @typedef {T[number] extends infer Item
877
+ * ? Item extends {$define: infer D}
878
+ * ? ElementFromDefineValue<D>
879
+ * : never
880
+ * : never} ElementFromJamilihDefine
881
+ */
882
+
883
+ /**
884
+ * @template {JamilihArray} T
885
+ * @typedef {Extract<T[number], {xmlns: unknown}> extends never ? false : true} HasXmlnsFromJamilihArray
886
+ */
887
+
888
+ /**
889
+ * @template {JamilihArray} T
890
+ * @typedef {T extends [infer K, ...ArbitraryValue[]]
891
+ * ? (HasXmlnsFromJamilihArray<T> extends true
892
+ * ? Element
893
+ * : ElementFromJamilihDefine<T> extends never
894
+ * ? K extends keyof HTMLElementTagNameMap
895
+ * ? HTMLElementTagNameMap[K]
896
+ * : HTMLElement
897
+ * : ElementFromJamilihDefine<T>)
898
+ * : Element} ElementFromJamilihArray
899
+ */
900
+
901
+ /**
902
+ * @template A
903
+ * @template {Element} E
904
+ * @template X
905
+ * @typedef {A extends {$custom: infer C}
906
+ * ? (C extends object
907
+ * ? Omit<A, '$custom'> & {$custom?: C & ThisType<E & C & X>}
908
+ * : A)
909
+ * : A} WithCustomThis
910
+ */
911
+
912
+ /**
913
+ * @template D
914
+ * @template {Element} E
915
+ * @template X
916
+ * @typedef {D extends [infer First, infer Second, ...infer Rest]
917
+ * ? (First extends DefineMixin
918
+ * ? [First & ThisType<E & First & X>, Second, ...Rest]
919
+ * : Second extends DefineMixin
920
+ * ? [First, Second & ThisType<E & Second & X>, ...Rest]
921
+ * : D)
922
+ * : D extends [infer First]
923
+ * ? First extends DefineMixin
924
+ * ? [First & ThisType<E & First & X>]
925
+ * : D
926
+ * : D extends DefineMixin
927
+ * ? D & ThisType<E & D & X>
928
+ * : D} WithDefineThisValue
929
+ */
930
+
931
+ /**
932
+ * @template A
933
+ * @template {Element} E
934
+ * @template X
935
+ * @typedef {A extends {$define: infer D}
936
+ * ? Omit<A, '$define'> & {$define?: WithDefineThisValue<D, E, X>}
937
+ * : A} WithDefineThis
938
+ */
939
+
940
+ /**
941
+ * @template A
942
+ * @typedef {A extends {$custom: infer C}
943
+ * ? C extends object ? C : object
944
+ * : object} CustomFromJamilihItem
945
+ */
946
+
947
+ /**
948
+ * @template A
949
+ * @typedef {A extends {$define: infer D}
950
+ * ? DefineMixinFromValue<D>
951
+ * : object} DefineMixinFromJamilihItem
952
+ */
953
+
954
+ /**
955
+ * @template {JamilihArray} T
956
+ * @template {Element} E
957
+ * @typedef {{[K in keyof T]: WithCustomThis<
958
+ * WithDefineThis<T[K], E, CustomFromJamilihItem<T[K]>>,
959
+ * E,
960
+ * DefineMixinFromJamilihItem<T[K]>
961
+ * >}} JamilihArrayWithCustomThis
962
+ */
963
+
964
+ /**
965
+ * @template A
966
+ * @typedef {A extends (infer Item)[]
967
+ * ? (Extract<Item, JamilihFirstArg> extends never ? never : A)
968
+ * : A} ValidateJamilihArrayLike
969
+ */
970
+
971
+ /**
972
+ * @template A
973
+ * @typedef {A extends (infer Child)[]
974
+ * ? A & (Child extends unknown[] ? ValidateJamilihArrayLike<Child> : Child)[]
975
+ * : A} ValidateJamilihChildContainer
976
+ */
977
+
978
+ /**
979
+ * @template {JamilihArray} T
980
+ * @typedef {{[K in keyof T]: ValidateJamilihChildContainer<T[K]>}} ValidateJamilihArrayLikes
981
+ */
982
+
983
+ /**
984
+ * @template {JamilihArray} T
985
+ * @typedef {(
986
+ * RawCustomFromJamilihArray<T> extends never
987
+ * ? object
988
+ * : RawCustomFromJamilihArray<T>
989
+ * )} CustomFromJamilihArray
990
+ */
991
+
992
+ /**
993
+ * @template {JamilihArray} T
994
+ * @typedef {(
995
+ * RawDefineMixinFromJamilihArray<T> extends never
996
+ * ? object
997
+ * : RawDefineMixinFromJamilihArray<T>
998
+ * )} DefineMixinFromJamilihArray
999
+ */
1000
+
1001
+ /**
1002
+ * @template U
1003
+ * @template W
1004
+ * @typedef {U extends void ? (ExpandoHTMLElement & W) : (U & W)} ResolvedElement
1005
+ */
1006
+
765
1007
  /**
766
1008
  * Creates an XHTML or HTML element (XHTML is preferred, but only in browsers
767
1009
  * that support); any element after element can be omitted, and any subsequent
768
1010
  * type or types added afterwards.
769
1011
  * @template {JamilihArray} T
770
- * @param {T} args
771
- * @returns {T extends [keyof HTMLElementTagNameMap, any?, any?, any?]
772
- * ? HTMLElementTagNameMap[T[0]] : JamilihReturn}
1012
+ * @template {T extends [infer K, ...ArbitraryValue[]] ? (HasXmlnsFromJamilihArray<T> extends true ? Element : ElementFromJamilihDefine<T> extends never ? K extends keyof HTMLElementTagNameMap ? HTMLElementTagNameMap[K] : K extends string ? HTMLElement : void : ElementFromJamilihDefine<T>) : void} U
1013
+ * @template {ElementFromJamilihArray<T>} E
1014
+ * @template {CustomFromJamilihArray<T>} W
1015
+ * @template {DefineMixinFromJamilihArray<T>} D
1016
+ * @param {JamilihArrayWithCustomThis<T, E> & ValidateJamilihArrayLikes<T>} args
1017
+ * @returns {U extends void ? JamilihReturn : ResolvedElement<U, W & D>}
773
1018
  * The newly created (and possibly already appended)
774
1019
  * element or array of elements
775
1020
  */
1021
+
776
1022
  const jml = function jml(...args) {
777
- /* eslint-enable jsdoc/valid-types -- pratt parser bug */
778
1023
  if (!win) {
779
1024
  throw new Error('No window object');
780
1025
  }
@@ -782,7 +1027,7 @@ const jml = function jml(...args) {
782
1027
  throw new Error('No document object');
783
1028
  }
784
1029
 
785
- /** @type {(Document|DocumentFragment|HTMLElement) & {[key: string]: any}} */
1030
+ /** @type {(Document|DocumentFragment|HTMLElement) & {[key: string]: ElementExpando}} */
786
1031
  let elem = doc.createDocumentFragment();
787
1032
  /**
788
1033
  *
@@ -797,20 +1042,15 @@ const jml = function jml(...args) {
797
1042
  }
798
1043
  for (let [att, attVal] of Object.entries(atts)) {
799
1044
  att = ATTR_MAP.has(att) ? String(ATTR_MAP.get(att)) : att;
800
-
801
- /**
802
- * @typedef {any} ElementExpando
803
- */
804
-
805
1045
  if (NULLABLES.has(att)) {
806
1046
  attVal = checkPluginValue(elem, att, /** @type {string|JamilihArray} */attVal, opts);
807
1047
  if (!_isNullish(attVal)) {
808
- /** @type {ElementExpando} */elem[att] = attVal;
1048
+ elem[att] = attVal;
809
1049
  }
810
1050
  continue;
811
- } else if (ATTR_DOM.has(att)) {
1051
+ }
1052
+ if (ATTR_DOM.has(att)) {
812
1053
  attVal = checkPluginValue(elem, att, /** @type {string|JamilihArray} */attVal, opts);
813
- /** @type {ElementExpando} */
814
1054
  elem[att] = attVal;
815
1055
  continue;
816
1056
  }
@@ -895,6 +1135,9 @@ const jml = function jml(...args) {
895
1135
  }
896
1136
  case '$custom':
897
1137
  {
1138
+ if (attVal !== null && (typeof attVal === 'object' || typeof attVal === 'function') && Object.prototype.propertyIsEnumerable.call(attVal, '__proto__')) {
1139
+ throw new TypeError('`$custom` may not define `__proto__`');
1140
+ }
898
1141
  Object.assign(elem, attVal);
899
1142
  break;
900
1143
  }
@@ -965,7 +1208,8 @@ const jml = function jml(...args) {
965
1208
  const defineObj = /** @type {DefineObject} */attVal;
966
1209
  if (Array.isArray(defineObj)) {
967
1210
  if (defineObj.length <= 2) {
968
- [cnstrctr, options] = defineObj;
1211
+ [cnstrctr] = defineObj;
1212
+ options = /** @type {DefineOptions|undefined} */defineObj[1];
969
1213
  if (typeof options === 'string') {
970
1214
  // Todo: Allow creating a definition without using it;
971
1215
  // that may be the only reason to have a string here which
@@ -1004,7 +1248,7 @@ const jml = function jml(...args) {
1004
1248
  }
1005
1249
  if (mixin) {
1006
1250
  Object.entries(mixin).forEach(([methodName, method]) => {
1007
- /** @type {DefineConstructor} */cnstrctr.prototype[methodName] = method;
1251
+ Reflect.set(/** @type {DefineConstructor} */cnstrctr.prototype, methodName, method);
1008
1252
  });
1009
1253
  }
1010
1254
  // console.log('def', def, '::', typeof options === 'object' ? options : undefined);
@@ -1013,9 +1257,13 @@ const jml = function jml(...args) {
1013
1257
  }
1014
1258
  case '$symbol':
1015
1259
  {
1016
- const [symbol, func] = /** @type {SymbolArray} */attVal;
1260
+ if (!_isHTMLElement(elem)) {
1261
+ throw new TypeError('Element expected for `$symbol`');
1262
+ }
1263
+ const symbolElem = /** @type {ResolvedElement<U, W>} */elem;
1264
+ const [symbol, func] = /** @type {SymbolArray<ResolvedElement<U, W>>} */attVal;
1017
1265
  if (typeof func === 'function') {
1018
- const funcBound = func.bind(/** @type {HTMLElement} */elem);
1266
+ const funcBound = func.bind(symbolElem);
1019
1267
  if (typeof symbol === 'string') {
1020
1268
  // @ts-expect-error
1021
1269
  elem[Symbol.for(symbol)] = funcBound;
@@ -1025,7 +1273,7 @@ const jml = function jml(...args) {
1025
1273
  }
1026
1274
  } else {
1027
1275
  const obj = func;
1028
- obj.elem = /** @type {HTMLElement} */elem;
1276
+ obj.elem = symbolElem;
1029
1277
  if (typeof symbol === 'string') {
1030
1278
  // @ts-expect-error
1031
1279
  elem[Symbol.for(symbol)] = obj;
@@ -1038,7 +1286,7 @@ const jml = function jml(...args) {
1038
1286
  }
1039
1287
  case '$data':
1040
1288
  {
1041
- setMap(/** @type {true|string[]|Map<any, any>|WeakMap<any, any>|DataAttributeObject} */
1289
+ setMap(/** @type {true|string[]|Map<HTMLElement, unknown>|WeakMap<HTMLElement, unknown>|DataAttributeObject} */
1042
1290
  attVal);
1043
1291
  break;
1044
1292
  }
@@ -1061,15 +1309,15 @@ const jml = function jml(...args) {
1061
1309
  case '$document':
1062
1310
  {
1063
1311
  // Todo: Conditionally create XML document
1064
- const docNode = doc.implementation.createHTMLDocument();
1065
1312
  if (!attVal) {
1066
1313
  throw new Error('Bad attribute value');
1067
1314
  }
1315
+ const docNode = doc.implementation.createHTMLDocument();
1068
1316
  const jamlihDoc = /** @type {JamilihDocument} */attVal;
1069
1317
  if (jamlihDoc.childNodes) {
1070
1318
  // Remove any extra nodes created by createHTMLDocument().
1071
1319
  const j = jamlihDoc.childNodes.length;
1072
- while (docNode.childNodes[j]) {
1320
+ while (Object.hasOwn(docNode.childNodes, j)) {
1073
1321
  const cn = docNode.childNodes[j];
1074
1322
  cn.remove();
1075
1323
  // `j` should stay the same as removing will cause node to be present
@@ -1094,7 +1342,7 @@ const jml = function jml(...args) {
1094
1342
  if (jamlihDoc.title) {
1095
1343
  docNode.title = jamlihDoc.title; // Appends after meta
1096
1344
  }
1097
- if (jamlihDoc.head && head) {
1345
+ if (head && jamlihDoc.head) {
1098
1346
  // each child of `head` is:
1099
1347
  // (JamilihArray|TextNodeString|HTMLElement|Comment|ProcessingInstruction|
1100
1348
  // Text|DocumentFragment|JamilihProcessingInstruction|JamilihDocumentFragment)
@@ -1108,7 +1356,7 @@ const jml = function jml(...args) {
1108
1356
  jamlihDoc.head.forEach(_appendJML(head));
1109
1357
  }
1110
1358
  }
1111
- if (jamlihDoc.body && body) {
1359
+ if (body && jamlihDoc.body) {
1112
1360
  jamlihDoc.body.forEach(_appendJMLOrText(body));
1113
1361
  }
1114
1362
  }
@@ -1135,16 +1383,17 @@ const jml = function jml(...args) {
1135
1383
  case '$on':
1136
1384
  {
1137
1385
  // Events
1386
+ const onElem = /** @type {ResolvedElement<U, W>} */elem;
1138
1387
  // Allow for no-op by defaulting to `{}`
1139
- // eslint-disable-next-line prefer-const -- Ok as mixed
1140
- for (let [p2, val] of Object.entries(/** @type {OnAttributeObject} */attVal || {})) {
1388
+ // eslint-disable-next-line prefer-const, unicorn/no-unreadable-for-of-expression -- Ok as mixed
1389
+ for (let [p2, val] of Object.entries(/** @type {OnAttributeObject<ResolvedElement<U, W>>} */attVal || {})) {
1141
1390
  if (typeof val === 'function') {
1142
1391
  val = [val, false];
1143
1392
  }
1144
1393
  if (typeof val[0] !== 'function') {
1145
1394
  throw new TypeError(`Expect a function for \`$on\`; args: ${JSON.stringify(args)}`);
1146
1395
  }
1147
- _addEvent(/** @type {HTMLElement} */elem, p2, val[0], val[1]); // element, event name, handler, capturing
1396
+ _addEvent(onElem, p2, val[0], val[1]); // element, event name, handler, capturing
1148
1397
  }
1149
1398
  break;
1150
1399
  }
@@ -1168,7 +1417,11 @@ const jml = function jml(...args) {
1168
1417
  const pastInitialProp = startProp !== '';
1169
1418
  Object.keys(atVal).forEach(key => {
1170
1419
  const value = atVal[key];
1171
- prop = pastInitialProp ? startProp + key.replaceAll(hyphenForCamelCase, _upperCase).replace(/^([a-z])/u, _upperCase) : startProp + key.replaceAll(hyphenForCamelCase, _upperCase);
1420
+ prop = startProp + (pastInitialProp
1421
+ // eslint-disable-next-line unicorn/no-unsafe-string-replacement -- Function
1422
+ ? key.replaceAll(hyphenForCamelCase, _upperCase).replace(/^([a-z])/u, _upperCase)
1423
+ // eslint-disable-next-line unicorn/no-unsafe-string-replacement -- Function
1424
+ : key.replaceAll(hyphenForCamelCase, _upperCase));
1172
1425
  if (value === null || typeof value !== 'object') {
1173
1426
  if (!_isNullish(value)) {
1174
1427
  elem.dataset[prop] = value;
@@ -1187,7 +1440,7 @@ const jml = function jml(...args) {
1187
1440
  // Don't remove this `if` block (for sake of no-innerHTML build)
1188
1441
  case 'innerHTML':
1189
1442
  if (!_isNullish(attVal)) {
1190
- // // eslint-disable-next-line no-unsanitized/property
1443
+ // eslint-disable-next-line no-unsanitized/property
1191
1444
  elem.innerHTML = attVal;
1192
1445
  }
1193
1446
  break;
@@ -1229,6 +1482,7 @@ const jml = function jml(...args) {
1229
1482
  elem.style.cssFloat = styleVal;
1230
1483
  elem.style.styleFloat = styleVal; // Harmless though we could make conditional on older IE instead
1231
1484
  } else {
1485
+ // eslint-disable-next-line unicorn/no-unsafe-string-replacement -- Function
1232
1486
  elem.style[p2.replaceAll(hyphenForCamelCase, _upperCase)] = styleVal;
1233
1487
  }
1234
1488
  }
@@ -1318,12 +1572,12 @@ const jml = function jml(...args) {
1318
1572
  const defaultMap = opts.$map && /** @type {MapWithRoot} */opts.$map.root;
1319
1573
 
1320
1574
  /**
1321
- * @param {true|string[]|Map<any, any>|WeakMap<any, any>|DataAttributeObject} dataVal
1575
+ * @param {true|string[]|Map<HTMLElement, UserArg>|WeakMap<HTMLElement, UserArg>|DataAttributeObject|[Map<HTMLElement, UserArg>|WeakMap<HTMLElement, UserArg>|undefined, DataAttributeObject|UserArg]} dataVal
1322
1576
  * @returns {void}
1323
1577
  */
1324
1578
  const setMap = dataVal => {
1325
1579
  let map, obj;
1326
- const defMap = /** @type {[Map<HTMLElement, any> | WeakMap<HTMLElement, any>, any]} */defaultMap;
1580
+ const defMap = /** @type {[Map<HTMLElement, UserArg> | WeakMap<HTMLElement, UserArg>, UserArg]} */defaultMap;
1327
1581
  // Boolean indicating use of default map and object
1328
1582
  if (dataVal === true) {
1329
1583
  [map, obj] = defMap;
@@ -1347,7 +1601,7 @@ const jml = function jml(...args) {
1347
1601
  map = defMap[0];
1348
1602
  obj = dataVal;
1349
1603
  }
1350
- /** @type {Map<HTMLElement, any> | WeakMap<HTMLElement, any>} */
1604
+ /** @type {Map<HTMLElement, UserArg> | WeakMap<HTMLElement, UserArg>} */
1351
1605
  map.set(/** @type {HTMLElement} */
1352
1606
  elem, obj);
1353
1607
  };
@@ -1359,7 +1613,7 @@ const jml = function jml(...args) {
1359
1613
  // null always indicates a place-holder (only needed for last argument if want array returned)
1360
1614
  if (i === argc - 1) {
1361
1615
  // Casting needing unless changing `jml()` signature with overloads
1362
- return /** @type {ArbitraryValue} */nodes.length <= 1 ? nodes[0]
1616
+ return /** @type {U extends void ? JamilihReturn : ResolvedElement<U, W & D>} */nodes.length <= 1 ? nodes[0]
1363
1617
  // eslint-disable-next-line unicorn/no-array-callback-reference
1364
1618
  : nodes.reduce(_fragReducer, doc.createDocumentFragment()); // nodes;
1365
1619
  }
@@ -1378,7 +1632,7 @@ const jml = function jml(...args) {
1378
1632
  if (val && typeof val === 'object') {
1379
1633
  const procValues = [];
1380
1634
  for (const [p, procInstVal] of Object.entries(val)) {
1381
- procValues.push(p + '=' + '"' +
1635
+ procValues.push(p + '="' +
1382
1636
  // https://www.w3.org/TR/xml-stylesheet/#NT-PseudoAttValue
1383
1637
  procInstVal.replaceAll('"', '&quot;') + '"');
1384
1638
  }
@@ -1440,13 +1694,12 @@ const jml = function jml(...args) {
1440
1694
  /* c8 ignore next 4 */
1441
1695
  elem = doc.createElementNS
1442
1696
  // Should create separate file for this
1443
- /* eslint-disable object-shorthand -- Casting */ ? (/** @type {HTMLElement} */doc.createElementNS(NS_HTML, elStr, {
1697
+ ? (/** @type {HTMLElement} */doc.createElementNS(NS_HTML, elStr, {
1444
1698
  is: (/** @type {string} */is)
1445
1699
  })
1446
1700
  /* c8 ignore next 1 */) : doc.createElement(elStr, {
1447
1701
  is: (/** @type {string} */is)
1448
1702
  });
1449
- /* eslint-enable object-shorthand -- Casting */
1450
1703
  } else /* c8 ignore next */if (doc.createElementNS) {
1451
1704
  elem = doc.createElementNS(NS_HTML, elStr);
1452
1705
  /* c8 ignore next 3 */
@@ -1474,15 +1727,16 @@ const jml = function jml(...args) {
1474
1727
  // elem.setAttribute('xmlns', atts.xmlns); // Doesn't work
1475
1728
  // Can't set namespaceURI dynamically, renameNode() is not supported, and setAttribute() doesn't work to change the namespace, so we resort to this hack
1476
1729
  const xmlnsObj = /** @type {XmlnsAttributeObject} */atts;
1477
- const replacer = xmlnsObj.xmlns && typeof xmlnsObj.xmlns === 'object' ? _replaceDefiner(xmlnsObj.xmlns) : ' xmlns="' + xmlnsObj.xmlns + '"';
1730
+ const replacer = xmlnsObj.xmlns && typeof xmlnsObj.xmlns === 'object' ? _replaceDefiner(xmlnsObj.xmlns) : ' xmlns="' + escapeReplacer(xmlnsObj.xmlns) + '"';
1478
1731
  // try {
1479
1732
  // Also fix DOMParser to work with text/html
1480
1733
  elem = nodes[nodes.length - 1] =
1481
1734
  // Why doesn't `HTMLWindow` have `DOMParser`?
1482
1735
  new /** @type {import('jsdom').DOMWindow} */win.DOMParser().parseFromString(new /** @type {import('jsdom').DOMWindow} */win.XMLSerializer().serializeToString(elem).
1483
1736
  // Mozilla adds XHTML namespace
1484
- replace(' xmlns="' + NS_HTML + '"',
1737
+ replace(' xmlns="' + escapeReplacer(NS_HTML) + '"',
1485
1738
  // Needed to cast here, despite either overload working
1739
+ // eslint-disable-next-line unicorn/no-unsafe-string-replacement -- Escaped
1486
1740
  /** @type {string} */
1487
1741
  replacer), 'application/xml').documentElement;
1488
1742
  // Todo: Report to plugins
@@ -1521,12 +1775,19 @@ const jml = function jml(...args) {
1521
1775
  // Arrays or arrays of arrays indicate child nodes
1522
1776
  const child = /** @type {JamilihChildren} */arg;
1523
1777
  const cl = child.length;
1778
+ /**
1779
+ * @param {number} childIndex
1780
+ * @returns {TypeError}
1781
+ */
1782
+ const getBadChildrenError = childIndex => {
1783
+ return new TypeError(`Bad children (parent array: ${JSON.stringify(args)}; index ${childIndex} of child: ${JSON.stringify(child)})`);
1784
+ };
1524
1785
  for (let j = 0; j < cl; j++) {
1525
1786
  // Go through children array container to handle elements
1526
1787
  const childContent = child[j];
1527
1788
  const childContentType = typeof childContent;
1528
1789
  if (childContent === null || _isNullish(childContent)) {
1529
- throw new TypeError(`Bad children (parent array: ${JSON.stringify(args)}; index ${j} of child: ${JSON.stringify(child)})`);
1790
+ throw getBadChildrenError(j);
1530
1791
  }
1531
1792
  switch (childContentType) {
1532
1793
  // Todo: determine whether null or function should have special handling or be converted to text
@@ -1538,12 +1799,17 @@ const jml = function jml(...args) {
1538
1799
  default:
1539
1800
  // bigint, symbol, function
1540
1801
  if (typeof childContent !== 'object') {
1541
- throw new TypeError(`Bad children (parent array: ${JSON.stringify(args)}; index ${j} of child: ${JSON.stringify(child)})`);
1802
+ throw getBadChildrenError(j);
1542
1803
  }
1543
1804
  if (Array.isArray(childContent)) {
1544
1805
  // Arrays representing child elements
1806
+ const childHeadType = typeof childContent[0];
1807
+ if (childContent.length === 0 || childHeadType !== 'string' && childHeadType !== 'object' || childContent[0] === null) {
1808
+ throw getBadChildrenError(j);
1809
+ }
1545
1810
  opts.$state = 'children';
1546
- _appendNode(elem, jml(opts, ...childContent));
1811
+ const childArgs = /** @type {JamilihArray} */[opts, ...childContent];
1812
+ _appendNode(elem, jml(...childArgs));
1547
1813
  } else if ('#' in childContent) {
1548
1814
  // Fragment
1549
1815
  opts.$state = 'fragmentChildren';
@@ -1573,7 +1839,7 @@ const jml = function jml(...args) {
1573
1839
  }
1574
1840
 
1575
1841
  // Casting needing unless changing `jml()` signature with overloads
1576
- return /** @type {ArbitraryValue} */ret;
1842
+ return /** @type {U extends void ? JamilihReturn : ResolvedElement<U, W & D>} */ret;
1577
1843
  };
1578
1844
 
1579
1845
  /**
@@ -1643,8 +1909,8 @@ class DOMException extends Error {
1643
1909
 
1644
1910
  /**
1645
1911
  * @typedef {JamilihArray|JamilihDoctype|
1646
- * JamilihCDATANode|JamilihEntityReference|JamilihProcessingInstruction|
1647
- * JamilihComment|JamilihDocumentFragment} JamilihChildType
1912
+ * JamilihCDATANode|JamilihEntityReference|JamilihProcessingInstruction|
1913
+ * JamilihComment|JamilihDocumentFragment} JamilihChildType
1648
1914
  */
1649
1915
 
1650
1916
  /**
@@ -1652,15 +1918,15 @@ class DOMException extends Error {
1652
1918
  */
1653
1919
 
1654
1920
  /**
1655
- * Converts a DOM object or a string of HTML into a Jamilih object (or string).
1656
- * @param {string|HTMLElement|Node|Entity} nde If a string, will parse as document
1657
- * @param {ToJmlConfig} [config] Configuration object
1658
- * @throws {TypeError}
1659
- * @returns {JamilihType|string} Array containing the elements which represent
1660
- * a Jamilih object, or, if `stringOutput` is true, it will be the stringified
1661
- * version of such an object
1662
- */
1663
- jml.toJML = function (nde, {
1921
+ * Converts a DOM object or a string of HTML into a Jamilih object (or string).
1922
+ * @param {string|HTMLElement|Node|Entity} nde If a string, will parse as document
1923
+ * @param {ToJmlConfig} [config] Configuration object
1924
+ * @throws {TypeError}
1925
+ * @returns {JamilihType|string} Array containing the elements which represent
1926
+ * a Jamilih object, or, if `stringOutput` is true, it will be the stringified
1927
+ * version of such an object
1928
+ */
1929
+ const toJML = function (nde, {
1664
1930
  stringOutput = false,
1665
1931
  reportInvalidState = true,
1666
1932
  stripWhitespace = false
@@ -1675,7 +1941,7 @@ jml.toJML = function (nde, {
1675
1941
 
1676
1942
  /**
1677
1943
  * @todo Find more specific type than `any`
1678
- * @typedef {{[key: (number|string)]: any}} IndexableObject
1944
+ * @typedef {{[key: (number|string)]: UserArg}} IndexableObject
1679
1945
  */
1680
1946
 
1681
1947
  const ret = /** @type {IndexableObject} */[];
@@ -1689,12 +1955,14 @@ jml.toJML = function (nde, {
1689
1955
  */
1690
1956
  function invalidStateError(msg) {
1691
1957
  // These are probably only necessary if working with text/html
1692
- if (reportInvalidState) {
1693
- // INVALID_STATE_ERR per section 9.3 XHTML 5: http://www.w3.org/TR/html5/the-xhtml-syntax.html
1694
- const e = new DOMException(msg, 'INVALID_STATE_ERR');
1695
- e.code = 11;
1696
- throw e;
1958
+ if (!reportInvalidState) {
1959
+ return;
1697
1960
  }
1961
+
1962
+ // INVALID_STATE_ERR per section 9.3 XHTML 5: https://www.w3.org/TR/html5/the-xhtml-syntax.html
1963
+ const e = new DOMException(msg, 'INVALID_STATE_ERR');
1964
+ e.code = 11;
1965
+ throw e;
1698
1966
  }
1699
1967
 
1700
1968
  /**
@@ -1781,8 +2049,13 @@ jml.toJML = function (nde, {
1781
2049
  namespaces = {
1782
2050
  ...namespaces
1783
2051
  };
1784
- const xmlChars = /^([\u0009\u000A\u000D\u0020-\uD7FF\uE000-\uFFFD]|[\uD800-\uDBFF][\uDC00-\uDFFF])*$/u; // eslint-disable-line no-control-regex
1785
- if ([2, 3, 4, 7, 8].includes(type) && /** @type {Node} */nodeOrEntity.nodeValue && !xmlChars.test(/** @type {Node} */nodeOrEntity.nodeValue)) {
2052
+ const xmlChars = /^([\u{9}\u{A}\u{D}\u{20}-\u{D7FF}\u{E000}-\u{FFFD}]|[\uD800-\uDBFF][\uDC00-\uDFFF])*$/u; // eslint-disable-line no-control-regex
2053
+
2054
+ // eslint-disable-next-line prefer-destructuring -- TS
2055
+ const nodeValue = /** @type {Node} */nodeOrEntity.nodeValue;
2056
+ if ([2, 3, 4, 7, 8].includes(type) &&
2057
+ // eslint-disable-next-line unicorn/prefer-simple-condition-first -- Safer
2058
+ nodeValue && !xmlChars.test(nodeValue)) {
1786
2059
  invalidStateError('Node has bad XML character value');
1787
2060
  }
1788
2061
 
@@ -1886,6 +2159,7 @@ jml.toJML = function (nde, {
1886
2159
  {
1887
2160
  // CDATA
1888
2161
  const node = /** @type {CDATASection} */nodeOrEntity;
2162
+ // eslint-disable-next-line unicorn/no-useless-concat -- Safer if pasting
1889
2163
  if (node.nodeValue?.includes(']]' + '>')) {
1890
2164
  invalidStateError('CDATA cannot end with closing ]]>');
1891
2165
  }
@@ -1969,7 +2243,7 @@ jml.toJML = function (nde, {
1969
2243
  name: /** @type {DocumentType} */node.name
1970
2244
  }
1971
2245
  };
1972
- const pubIdChar = /^(\u0020|\u000D|\u000A|[a-zA-Z0-9]|[-'()+,./:=?;!*#@$_%])*$/u; // eslint-disable-line no-control-regex
2246
+ const pubIdChar = /^(\u{20}|\u{D}|\u{A}|[a-zA-Z0-9]|[-'()+,./:=?;!*#@$_%])*$/u; // eslint-disable-line no-control-regex
1973
2247
  if (!pubIdChar.test(/** @type {DocumentType} */node.publicId)) {
1974
2248
  invalidStateError('A publicId must have valid characters.');
1975
2249
  }
@@ -2011,34 +2285,37 @@ jml.toJML = function (nde, {
2011
2285
  }
2012
2286
  return ret[0];
2013
2287
  };
2288
+ jml.toJML = toJML;
2014
2289
 
2015
2290
  /**
2016
2291
  * @param {string|HTMLElement} dom
2017
2292
  * @param {ToJmlConfig} [config]
2018
2293
  * @returns {string}
2019
2294
  */
2020
- jml.toJMLString = function (dom, config) {
2021
- return /** @type {string} */jml.toJML(dom, Object.assign(config || {}, {
2295
+ const toJMLString = function (dom, config) {
2296
+ return /** @type {string} */toJML(dom, Object.assign(config || {}, {
2022
2297
  stringOutput: true
2023
2298
  }));
2024
2299
  };
2300
+ jml.toJMLString = toJMLString;
2025
2301
 
2026
2302
  /**
2027
2303
  *
2028
2304
  * @param {JamilihArray} args
2029
2305
  * @returns {JamilihReturn}
2030
2306
  */
2031
- jml.toDOM = function (...args) {
2307
+ const toDOM = function (...args) {
2032
2308
  // Alias for jml()
2033
2309
  return jml(...args);
2034
2310
  };
2311
+ jml.toDOM = toDOM;
2035
2312
 
2036
2313
  /**
2037
2314
  *
2038
2315
  * @param {JamilihArray} args
2039
2316
  * @returns {string}
2040
2317
  */
2041
- jml.toHTML = function (...args) {
2318
+ const toHTML = function (...args) {
2042
2319
  // Todo: Replace this with version of jml() that directly builds a string
2043
2320
  const ret = jml(...args);
2044
2321
  switch (ret.nodeType) {
@@ -2053,18 +2330,20 @@ jml.toHTML = function (...args) {
2053
2330
  case 2:
2054
2331
  {
2055
2332
  // ATTR
2056
- return `${/** @type {Attr} */ret.name}="${/** @type {Attr} */ret.value.replaceAll('"', '&quot;')}"`;
2333
+ return `${ /** @type {Attr} */ret.name}="${ /** @type {Attr} */ret.value.replaceAll('"', '&quot;')}"`;
2057
2334
  }
2058
2335
  case 3:
2059
2336
  {
2060
2337
  // TEXT
2061
2338
  // Fallthrough
2062
2339
  // } case 4: { // CDATA
2340
+ // eslint-disable-next-line prefer-destructuring -- TS
2341
+ const nodeValue = /** @type {Text|CDATASection} */ret.nodeValue;
2063
2342
  /* c8 ignore next 3 */
2064
- if (!ret.nodeValue) {
2343
+ if (!nodeValue) {
2065
2344
  throw new TypeError('Unexpected null Text node');
2066
2345
  }
2067
- return /** @type {Text|CDATASection} */ret.nodeValue;
2346
+ return nodeValue;
2068
2347
  // case 5: // Entity Reference Node
2069
2348
  // No 6: Entity Node
2070
2349
  // No 12: Notation Node
@@ -2076,7 +2355,6 @@ jml.toHTML = function (...args) {
2076
2355
  return `<?${node.target} ${node.data}?>`;
2077
2356
  // } case 8: { // Comment
2078
2357
  // return `<!--${ret.nodeValue}-->`;
2079
- // eslint-disable-next-line sonarjs/no-fallthrough
2080
2358
  }
2081
2359
  case 9:
2082
2360
  case 11:
@@ -2084,7 +2362,7 @@ jml.toHTML = function (...args) {
2084
2362
  // DOCUMENT FRAGMENT
2085
2363
  const node = /** @type {DocumentFragment} */ret;
2086
2364
  return [...node.childNodes].map(childNode => {
2087
- return jml.toHTML(/** @type {JamilihFirstArgument} */childNode);
2365
+ return toHTML(/** @type {JamilihFirstArgument} */childNode);
2088
2366
  }).join('');
2089
2367
  }
2090
2368
  case 10:
@@ -2098,47 +2376,52 @@ jml.toHTML = function (...args) {
2098
2376
  throw new Error('Unexpected node type');
2099
2377
  }
2100
2378
  };
2379
+ jml.toHTML = toHTML;
2101
2380
 
2102
2381
  /**
2103
2382
  *
2104
2383
  * @param {JamilihArray} args
2105
2384
  * @returns {string}
2106
2385
  */
2107
- jml.toDOMString = function (...args) {
2386
+ const toDOMString = function (...args) {
2108
2387
  // Alias for jml.toHTML for parity with jml.toJMLString
2109
- return jml.toHTML(...args);
2388
+ return toHTML(...args);
2110
2389
  };
2390
+ jml.toDOMString = toDOMString;
2111
2391
 
2112
2392
  /**
2113
2393
  *
2114
2394
  * @param {JamilihArray} args
2115
2395
  * @returns {string}
2116
2396
  */
2117
- jml.toXML = function (...args) {
2397
+ const toXML = function (...args) {
2118
2398
  if (!win) {
2119
2399
  throw new Error('No window object set');
2120
2400
  }
2121
2401
  const ret = jml(...args);
2122
2402
  return new /** @type {import('jsdom').DOMWindow} */win.XMLSerializer().serializeToString(ret);
2123
2403
  };
2404
+ jml.toXML = toXML;
2124
2405
 
2125
2406
  /**
2126
2407
  *
2127
2408
  * @param {JamilihArray} args
2128
2409
  * @returns {string}
2129
2410
  */
2130
- jml.toXMLDOMString = function (...args) {
2411
+ const toXMLDOMString = function (...args) {
2131
2412
  // Alias for jml.toXML for parity with jml.toJMLString
2132
- return jml.toXML(...args);
2413
+ return toXML(...args);
2133
2414
  };
2415
+ jml.toXMLDOMString = toXMLDOMString;
2134
2416
 
2135
2417
  /**
2136
2418
  * Element-aware wrapper for `Map`.
2419
+ * @template V
2137
2420
  */
2138
2421
  class JamilihMap extends Map {
2139
2422
  /**
2140
2423
  * @param {?(string|HTMLElement)} element
2141
- * @returns {ArbitraryValue}
2424
+ * @returns {V}
2142
2425
  */
2143
2426
  get(element) {
2144
2427
  const elem = typeof element === 'string' ? $(element) : element;
@@ -2146,78 +2429,82 @@ class JamilihMap extends Map {
2146
2429
  }
2147
2430
  /**
2148
2431
  * @param {string|HTMLElement} element
2149
- * @param {ArbitraryValue} value
2150
- * @returns {ArbitraryValue}
2432
+ * @param {V} value
2433
+ * @returns {this}
2151
2434
  */
2152
2435
  set(element, value) {
2153
2436
  const elem = typeof element === 'string' ? $(element) : element;
2154
- return super.set.call(this, elem, value);
2437
+ super.set.call(this, elem, value);
2438
+ return this;
2155
2439
  }
2156
2440
  /**
2157
2441
  * @param {string|HTMLElement} element
2158
2442
  * @param {string} methodName
2159
- * @param {...ArbitraryValue} args
2160
- * @returns {ArbitraryValue}
2443
+ * @param {...UserArg} args
2444
+ * @returns {StoredValue}
2161
2445
  */
2162
2446
  invoke(element, methodName, ...args) {
2163
2447
  const elem = typeof element === 'string' ? $(element) : element;
2164
- return this.get(elem)[methodName](elem, ...args);
2448
+ return /** @type {UserArg} */this.get(elem)[methodName](elem, ...args);
2165
2449
  }
2166
2450
  }
2167
2451
 
2168
2452
  /**
2169
2453
  * Element-aware wrapper for `WeakMap`.
2170
- * @extends {WeakMap<any>}
2454
+ * @template V
2171
2455
  */
2172
2456
  class JamilihWeakMap extends WeakMap {
2173
2457
  /**
2174
- * @param {HTMLElement} element
2175
- * @returns {ArbitraryValue}
2458
+ * @param {?(string|object|symbol)} element
2459
+ * @returns {V}
2176
2460
  */
2177
2461
  get(element) {
2178
2462
  const elem = typeof element === 'string' ? $(element) : element;
2179
2463
  if (!elem) {
2180
2464
  throw new Error("Can't find the element");
2181
2465
  }
2182
- return super.get.call(this, elem);
2466
+ return super.get.call(this, /** @type {object} */elem);
2183
2467
  }
2184
2468
  /**
2185
- * @param {HTMLElement} element
2186
- * @param {ArbitraryValue} value
2187
- * @returns {ArbitraryValue}
2469
+ * @param {?(string|object|symbol)} element
2470
+ * @param {V} value
2471
+ * @returns {this}
2188
2472
  */
2189
2473
  set(element, value) {
2190
2474
  const elem = typeof element === 'string' ? $(element) : element;
2191
2475
  if (!elem) {
2192
2476
  throw new Error("Can't find the element");
2193
2477
  }
2194
- return super.set.call(this, elem, value);
2478
+ super.set.call(this, /** @type {object} */elem, value);
2479
+ return this;
2195
2480
  }
2196
2481
  /**
2197
2482
  * @param {string|HTMLElement} element
2198
2483
  * @param {string} methodName
2199
- * @param {...ArbitraryValue} args
2200
- * @returns {ArbitraryValue}
2484
+ * @param {...UserArg} args
2485
+ * @returns {StoredValue}
2201
2486
  */
2202
2487
  invoke(element, methodName, ...args) {
2203
2488
  const elem = typeof element === 'string' ? $(element) : element;
2204
2489
  if (!elem) {
2205
2490
  throw new Error("Can't find the element");
2206
2491
  }
2207
- return this.get(elem)[methodName](elem, ...args);
2492
+ return /** @type {UserArg} */this.get(elem)[methodName](elem, ...args);
2208
2493
  }
2209
2494
  }
2210
2495
  jml.Map = JamilihMap;
2211
2496
  jml.WeakMap = JamilihWeakMap;
2212
2497
 
2213
2498
  /**
2214
- * @typedef {[JamilihWeakMap|JamilihMap, HTMLElement]} MapAndElementArray
2499
+ * @template V
2500
+ * @typedef {[JamilihWeakMap<V>|JamilihMap<V>, HTMLElement]} MapAndElementArray
2215
2501
  */
2216
2502
 
2217
2503
  /**
2218
- * @param {{[key: string]: any}} obj
2504
+ * @template V
2505
+ * @param {V} obj
2219
2506
  * @param {JamilihArrayPostOptions} args
2220
- * @returns {MapAndElementArray}
2507
+ * @returns {MapAndElementArray<V>}
2221
2508
  */
2222
2509
  jml.weak = function (obj, ...args) {
2223
2510
  const map = new JamilihWeakMap();
@@ -2228,9 +2515,10 @@ jml.weak = function (obj, ...args) {
2228
2515
  };
2229
2516
 
2230
2517
  /**
2231
- * @param {ArbitraryValue} obj
2518
+ * @template V
2519
+ * @param {V} obj
2232
2520
  * @param {JamilihArrayPostOptions} args
2233
- * @returns {MapAndElementArray}
2521
+ * @returns {MapAndElementArray<V>}
2234
2522
  */
2235
2523
  jml.strong = function (obj, ...args) {
2236
2524
  const map = new JamilihMap();
@@ -2243,7 +2531,7 @@ jml.strong = function (obj, ...args) {
2243
2531
  /**
2244
2532
  * @param {string|HTMLElement} element If a string, will be interpreted as a selector
2245
2533
  * @param {symbol|string} sym If a string, will be used with `Symbol.for`
2246
- * @returns {ArbitraryValue} The value associated with the symbol
2534
+ * @returns {SymbolResult} The value associated with the symbol
2247
2535
  */
2248
2536
  jml.symbol = jml.sym = jml.for = function (element, sym) {
2249
2537
  const elem = typeof element === 'string' ? $(element) : element;
@@ -2253,16 +2541,16 @@ jml.symbol = jml.sym = jml.for = function (element, sym) {
2253
2541
  };
2254
2542
 
2255
2543
  /**
2256
- * @typedef {((elem: HTMLElement, ...args: any[]) => void)|{[key: string]: (elem: HTMLElement, ...args: any[]) => void}} MapCommand
2544
+ * @typedef {((elem: HTMLElement, ...args: UserArg[]) => void)|{[key: string]: (elem: HTMLElement, ...args: UserArg[]) => void}} MapCommand
2257
2545
  */
2258
2546
 
2259
2547
  /**
2260
2548
  * @param {?(string|HTMLElement)} elem If a string, will be interpreted as a selector
2261
2549
  * @param {symbol|string|Map<HTMLElement, MapCommand>|WeakMap<HTMLElement, MapCommand>} symOrMap If a string, will be used with `Symbol.for`
2262
- * @param {string|any} methodName Can be `any` if the symbol or map directly
2550
+ * @param {string|UserArg} methodName Can be `UserArg` if the symbol or map directly
2263
2551
  * points to a function (it is then used as the first argument).
2264
- * @param {ArbitraryValue[]} args
2265
- * @returns {ArbitraryValue}
2552
+ * @param {UserArg[]} args
2553
+ * @returns {StoredValue}
2266
2554
  */
2267
2555
  jml.command = function (elem, symOrMap, methodName, ...args) {
2268
2556
  elem = typeof elem === 'string' ? $(elem) : elem;
@@ -2275,7 +2563,7 @@ jml.command = function (elem, symOrMap, methodName, ...args) {
2275
2563
  if (typeof func === 'function') {
2276
2564
  return func(methodName, ...args); // Already has `this` bound to `elem`
2277
2565
  }
2278
- return func[methodName](...args);
2566
+ return /** @type {UserArg} */func[methodName](...args);
2279
2567
  }
2280
2568
  func = /** @type {Map<HTMLElement, MapCommand>|WeakMap<HTMLElement, MapCommand>} */symOrMap.get(elem);
2281
2569
  if (!func) {
@@ -2291,10 +2579,10 @@ jml.command = function (elem, symOrMap, methodName, ...args) {
2291
2579
  /**
2292
2580
  * Expects properties `document`, `XMLSerializer`, and `DOMParser`.
2293
2581
  * Also updates `body` with `document.body`.
2294
- * @param {import('jsdom').DOMWindow|HTMLWindow|undefined} wind
2582
+ * @param {import('jsdom').DOMWindow|HTMLWindow|typeof globalThis|undefined} wind
2295
2583
  * @returns {void}
2296
2584
  */
2297
- jml.setWindow = wind => {
2585
+ const setWindow = wind => {
2298
2586
  win = wind;
2299
2587
  doc = win?.document;
2300
2588
  if (doc && doc.body) {
@@ -2302,28 +2590,31 @@ jml.setWindow = wind => {
2302
2590
  body = /** @type {HTMLBodyElement} */doc.body;
2303
2591
  }
2304
2592
  };
2593
+ jml.setWindow = setWindow;
2305
2594
 
2306
2595
  /**
2307
- * @returns {import('jsdom').DOMWindow|HTMLWindow}
2596
+ * @returns {import('jsdom').DOMWindow|HTMLWindow|typeof globalThis}
2308
2597
  */
2309
- jml.getWindow = () => {
2598
+ const getWindow = () => {
2310
2599
  if (!win) {
2311
2600
  throw new Error('No window object set');
2312
2601
  }
2313
2602
  return win;
2314
2603
  };
2604
+ jml.getWindow = getWindow;
2315
2605
 
2316
2606
  /**
2317
2607
  * Does not run Jamilih so can be further processed.
2318
- * @param {ArbitraryValue[]} array
2319
- * @param {ArbitraryValue} glu
2320
- * @returns {ArbitraryValue[]}
2608
+ * @template T
2609
+ * @param {T[]} array
2610
+ * @param {T} glu
2611
+ * @returns {T[]}
2321
2612
  */
2322
2613
  function glue(array, glu) {
2323
2614
  return [...array].reduce((arr, item) => {
2324
2615
  arr.push(item, glu);
2325
2616
  return arr;
2326
- }, []).slice(0, -1);
2617
+ }, /** @type {T[]} */[]).slice(0, -1);
2327
2618
  }
2328
2619
 
2329
2620
  /**
@@ -2336,6 +2627,6 @@ if (doc && doc.body) {
2336
2627
  // eslint-disable-next-line prefer-destructuring -- Needed for type
2337
2628
  body = /** @type {HTMLBodyElement} */doc.body;
2338
2629
  }
2339
- const nbsp = '\u00A0'; // Very commonly needed in templates
2630
+ const nbsp = '\u{A0}'; // Very commonly needed in templates
2340
2631
 
2341
- export { $, $$, DOMException, body, jml as default, glue, jml, nbsp };
2632
+ export { $, $$, DOMException, body, getWindow, glue, jml, nbsp, setWindow, toDOM, toDOMString, toHTML, toJML, toJMLString, toXML, toXMLDOMString };