@es-joy/jsoe 0.3.0 → 0.4.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 (189) hide show
  1. package/.eslintignore +3 -0
  2. package/.eslintrc.cjs +1 -0
  3. package/.ncurc.cjs +9 -0
  4. package/CHANGES.md +4 -0
  5. package/demo/index.html +8 -0
  6. package/demo/index.js +1 -1
  7. package/dist/formatAndTypeChoices.d.ts +56 -0
  8. package/dist/formatAndTypeChoices.d.ts.map +1 -0
  9. package/dist/formats/indexedDBKey.d.ts +4 -0
  10. package/dist/formats/indexedDBKey.d.ts.map +1 -0
  11. package/dist/formats/json.d.ts +4 -0
  12. package/dist/formats/json.d.ts.map +1 -0
  13. package/dist/formats/schemaAndArbitrary.d.ts +6 -0
  14. package/dist/formats/schemaAndArbitrary.d.ts.map +1 -0
  15. package/dist/formats/schemaOnly.d.ts +6 -0
  16. package/dist/formats/schemaOnly.d.ts.map +1 -0
  17. package/dist/formats/structuredCloning.d.ts +20 -0
  18. package/dist/formats/structuredCloning.d.ts.map +1 -0
  19. package/dist/formats.d.ts +26 -0
  20. package/dist/formats.d.ts.map +1 -0
  21. package/dist/fundamentalTypes/BooleanObjectType.d.ts +8 -0
  22. package/dist/fundamentalTypes/BooleanObjectType.d.ts.map +1 -0
  23. package/dist/fundamentalTypes/NumberObjectType.d.ts +6 -0
  24. package/dist/fundamentalTypes/NumberObjectType.d.ts.map +1 -0
  25. package/dist/fundamentalTypes/StringObjectType.d.ts +6 -0
  26. package/dist/fundamentalTypes/StringObjectType.d.ts.map +1 -0
  27. package/dist/fundamentalTypes/arrayReferenceType.d.ts +8 -0
  28. package/dist/fundamentalTypes/arrayReferenceType.d.ts.map +1 -0
  29. package/dist/fundamentalTypes/arrayType.d.ts +60 -0
  30. package/dist/fundamentalTypes/arrayType.d.ts.map +1 -0
  31. package/dist/fundamentalTypes/bigintType.d.ts +6 -0
  32. package/dist/fundamentalTypes/bigintType.d.ts.map +1 -0
  33. package/dist/fundamentalTypes/dateType.d.ts +26 -0
  34. package/dist/fundamentalTypes/dateType.d.ts.map +1 -0
  35. package/dist/fundamentalTypes/nullType.d.ts +6 -0
  36. package/dist/fundamentalTypes/nullType.d.ts.map +1 -0
  37. package/dist/fundamentalTypes/numberType.d.ts +6 -0
  38. package/dist/fundamentalTypes/numberType.d.ts.map +1 -0
  39. package/dist/fundamentalTypes/objectReferenceType.d.ts +8 -0
  40. package/dist/fundamentalTypes/objectReferenceType.d.ts.map +1 -0
  41. package/dist/fundamentalTypes/objectType.d.ts +6 -0
  42. package/dist/fundamentalTypes/objectType.d.ts.map +1 -0
  43. package/dist/fundamentalTypes/regexpType.d.ts +8 -0
  44. package/dist/fundamentalTypes/regexpType.d.ts.map +1 -0
  45. package/dist/fundamentalTypes/sparseUndefinedType.d.ts +6 -0
  46. package/dist/fundamentalTypes/sparseUndefinedType.d.ts.map +1 -0
  47. package/dist/fundamentalTypes/stringType.d.ts +6 -0
  48. package/dist/fundamentalTypes/stringType.d.ts.map +1 -0
  49. package/dist/fundamentalTypes/undefinedType.d.ts +6 -0
  50. package/dist/fundamentalTypes/undefinedType.d.ts.map +1 -0
  51. package/dist/index.d.ts +5 -0
  52. package/dist/index.d.ts.map +1 -0
  53. package/dist/src/deepEqual.d.ts +3 -0
  54. package/dist/src/deepEqual.d.ts.map +1 -0
  55. package/dist/src/formatAndTypeChoices.d.ts +47 -0
  56. package/dist/src/formatAndTypeChoices.d.ts.map +1 -0
  57. package/dist/src/formats/indexedDBKey.d.ts +9 -0
  58. package/dist/src/formats/indexedDBKey.d.ts.map +1 -0
  59. package/dist/src/formats/json.d.ts +9 -0
  60. package/dist/src/formats/json.d.ts.map +1 -0
  61. package/dist/src/formats/schemaAndArbitrary.d.ts +6 -0
  62. package/dist/src/formats/schemaAndArbitrary.d.ts.map +1 -0
  63. package/dist/src/formats/schemaOnly.d.ts +6 -0
  64. package/dist/src/formats/schemaOnly.d.ts.map +1 -0
  65. package/dist/src/formats/structuredCloning.d.ts +15 -0
  66. package/dist/src/formats/structuredCloning.d.ts.map +1 -0
  67. package/dist/src/formats.d.ts +18 -0
  68. package/dist/src/formats.d.ts.map +1 -0
  69. package/dist/src/fundamentalTypes/BooleanObjectType.d.ts +6 -0
  70. package/dist/src/fundamentalTypes/BooleanObjectType.d.ts.map +1 -0
  71. package/dist/src/fundamentalTypes/NumberObjectType.d.ts +6 -0
  72. package/dist/src/fundamentalTypes/NumberObjectType.d.ts.map +1 -0
  73. package/dist/src/fundamentalTypes/StringObjectType.d.ts +6 -0
  74. package/dist/src/fundamentalTypes/StringObjectType.d.ts.map +1 -0
  75. package/dist/src/fundamentalTypes/arrayReferenceType.d.ts +6 -0
  76. package/dist/src/fundamentalTypes/arrayReferenceType.d.ts.map +1 -0
  77. package/dist/src/fundamentalTypes/arrayType.d.ts +6 -0
  78. package/dist/src/fundamentalTypes/arrayType.d.ts.map +1 -0
  79. package/dist/src/fundamentalTypes/bigintType.d.ts +6 -0
  80. package/dist/src/fundamentalTypes/bigintType.d.ts.map +1 -0
  81. package/dist/src/fundamentalTypes/dateType.d.ts +6 -0
  82. package/dist/src/fundamentalTypes/dateType.d.ts.map +1 -0
  83. package/dist/src/fundamentalTypes/nullType.d.ts +6 -0
  84. package/dist/src/fundamentalTypes/nullType.d.ts.map +1 -0
  85. package/dist/src/fundamentalTypes/numberType.d.ts +6 -0
  86. package/dist/src/fundamentalTypes/numberType.d.ts.map +1 -0
  87. package/dist/src/fundamentalTypes/objectReferenceType.d.ts +6 -0
  88. package/dist/src/fundamentalTypes/objectReferenceType.d.ts.map +1 -0
  89. package/dist/src/fundamentalTypes/objectType.d.ts +6 -0
  90. package/dist/src/fundamentalTypes/objectType.d.ts.map +1 -0
  91. package/dist/src/fundamentalTypes/regexpType.d.ts +6 -0
  92. package/dist/src/fundamentalTypes/regexpType.d.ts.map +1 -0
  93. package/dist/src/fundamentalTypes/sparseUndefinedType.d.ts +6 -0
  94. package/dist/src/fundamentalTypes/sparseUndefinedType.d.ts.map +1 -0
  95. package/dist/src/fundamentalTypes/stringType.d.ts +6 -0
  96. package/dist/src/fundamentalTypes/stringType.d.ts.map +1 -0
  97. package/dist/src/fundamentalTypes/undefinedType.d.ts +6 -0
  98. package/dist/src/fundamentalTypes/undefinedType.d.ts.map +1 -0
  99. package/dist/src/index.d.ts +5 -0
  100. package/dist/src/index.d.ts.map +1 -0
  101. package/dist/src/subTypes/blobHTMLType.d.ts +26 -0
  102. package/dist/src/subTypes/blobHTMLType.d.ts.map +1 -0
  103. package/dist/src/subTypes/falseType.d.ts +6 -0
  104. package/dist/src/subTypes/falseType.d.ts.map +1 -0
  105. package/dist/src/subTypes/trueType.d.ts +6 -0
  106. package/dist/src/subTypes/trueType.d.ts.map +1 -0
  107. package/dist/src/superTypes/SpecialNumberSuperType.d.ts +11 -0
  108. package/dist/src/superTypes/SpecialNumberSuperType.d.ts.map +1 -0
  109. package/dist/src/superTypes/SpecialRealNumberSuperType.d.ts +6 -0
  110. package/dist/src/superTypes/SpecialRealNumberSuperType.d.ts.map +1 -0
  111. package/dist/src/typeChoices.d.ts +30 -0
  112. package/dist/src/typeChoices.d.ts.map +1 -0
  113. package/dist/src/types.d.ts +333 -0
  114. package/dist/src/types.d.ts.map +1 -0
  115. package/dist/src/utils/dialogs.d.ts +29 -0
  116. package/dist/src/utils/dialogs.d.ts.map +1 -0
  117. package/dist/src/utils/jsonPointer.d.ts +42 -0
  118. package/dist/src/utils/jsonPointer.d.ts.map +1 -0
  119. package/dist/src/utils/templateUtils.d.ts +59 -0
  120. package/dist/src/utils/templateUtils.d.ts.map +1 -0
  121. package/dist/src/utils/types.d.ts +2 -0
  122. package/dist/src/utils/types.d.ts.map +1 -0
  123. package/dist/subTypes/blobHTMLType.d.ts +14 -0
  124. package/dist/subTypes/blobHTMLType.d.ts.map +1 -0
  125. package/dist/subTypes/falseType.d.ts +8 -0
  126. package/dist/subTypes/falseType.d.ts.map +1 -0
  127. package/dist/subTypes/trueType.d.ts +8 -0
  128. package/dist/subTypes/trueType.d.ts.map +1 -0
  129. package/dist/superTypes/SpecialNumberSuperType.d.ts +14 -0
  130. package/dist/superTypes/SpecialNumberSuperType.d.ts.map +1 -0
  131. package/dist/superTypes/SpecialRealNumberSuperType.d.ts +6 -0
  132. package/dist/superTypes/SpecialRealNumberSuperType.d.ts.map +1 -0
  133. package/dist/typeChoices.d.ts +103 -0
  134. package/dist/typeChoices.d.ts.map +1 -0
  135. package/dist/types.d.ts +523 -0
  136. package/dist/types.d.ts.map +1 -0
  137. package/dist/utils/dialogs.d.ts +109 -0
  138. package/dist/utils/dialogs.d.ts.map +1 -0
  139. package/dist/utils/jsonPointer.d.ts +46 -0
  140. package/dist/utils/jsonPointer.d.ts.map +1 -0
  141. package/dist/utils/templateUtils.d.ts +59 -0
  142. package/dist/utils/templateUtils.d.ts.map +1 -0
  143. package/dist/utils/types.d.ts +2 -0
  144. package/dist/utils/types.d.ts.map +1 -0
  145. package/dist/vendor/jamilih/dist/jml-es.d.ts +436 -0
  146. package/dist/vendor/jamilih/dist/jml-es.d.ts.map +1 -0
  147. package/dist/vendor/typeson-registry/dist/index.d.ts +400 -0
  148. package/dist/vendor/typeson-registry/dist/index.d.ts.map +1 -0
  149. package/dist/vendor-imports.d.ts +3 -0
  150. package/dist/vendor-imports.d.ts.map +1 -0
  151. package/package.json +16 -11
  152. package/postorder-traversal.js +58 -0
  153. package/src/formatAndTypeChoices.js +95 -50
  154. package/src/formats/indexedDBKey.js +49 -57
  155. package/src/formats/json.js +47 -56
  156. package/src/formats/schemaOnly.js +12 -2
  157. package/src/formats/structuredCloning.js +162 -105
  158. package/src/formats.js +38 -10
  159. package/src/fundamentalTypes/BooleanObjectType.js +6 -4
  160. package/src/fundamentalTypes/NumberObjectType.js +5 -3
  161. package/src/fundamentalTypes/StringObjectType.js +1 -1
  162. package/src/fundamentalTypes/arrayReferenceType.js +29 -14
  163. package/src/fundamentalTypes/arrayType.js +789 -278
  164. package/src/fundamentalTypes/bigintType.js +2 -2
  165. package/src/fundamentalTypes/dateType.js +42 -14
  166. package/src/fundamentalTypes/nullType.js +1 -1
  167. package/src/fundamentalTypes/numberType.js +2 -2
  168. package/src/fundamentalTypes/objectReferenceType.js +7 -5
  169. package/src/fundamentalTypes/regexpType.js +45 -24
  170. package/src/fundamentalTypes/sparseUndefinedType.js +7 -1
  171. package/src/fundamentalTypes/stringType.js +1 -1
  172. package/src/fundamentalTypes/undefinedType.js +2 -2
  173. package/src/subTypes/blobHTMLType.js +42 -22
  174. package/src/subTypes/falseType.js +2 -2
  175. package/src/subTypes/trueType.js +2 -2
  176. package/src/superTypes/SpecialNumberSuperType.js +13 -6
  177. package/src/superTypes/SpecialRealNumberSuperType.js +11 -5
  178. package/src/typeChoices.js +137 -55
  179. package/src/types.js +309 -146
  180. package/src/utils/dialogs.js +98 -29
  181. package/src/utils/jsonPointer.js +4 -0
  182. package/src/utils/templateUtils.js +27 -12
  183. package/src/vendor-imports.js +8 -0
  184. package/tsconfig.json +18 -0
  185. package/typings/sceditor.d.ts +20 -0
  186. package/vendor/jamilih/dist/jml-es.js +1019 -389
  187. package/vendor/jamilih/dist/jml.d.ts +436 -0
  188. package/vendor/jamilih/package.json +136 -0
  189. package/vendor/typeson-registry/dist/index.js +365 -83
@@ -1,3 +1,4 @@
1
+ var _win;
1
2
  /*
2
3
  Possible todos:
3
4
  0. Add XSLT to JML-string stylesheet (or even vice versa)
@@ -28,10 +29,44 @@ Other Todos:
28
29
  0. Redo browser testing of jml (including ensuring IE7 can work even if test framework can't work)
29
30
  */
30
31
 
31
- // istanbul ignore next
32
- let win = typeof window !== 'undefined' && window;
33
- // istanbul ignore next
34
- let doc = typeof document !== 'undefined' && document || win && win.document;
32
+ /**
33
+ * @typedef {Window & {DocumentFragment: any}} HTMLWindow
34
+ */
35
+
36
+ /**
37
+ * @typedef {any} ArbitraryValue
38
+ */
39
+
40
+ /**
41
+ * @typedef {number} Integer
42
+ */
43
+
44
+ /**
45
+ * @typedef {{
46
+ * element: Document|Element|DocumentFragment,
47
+ * attribute: {name: string|null, value: JamilihAttValue},
48
+ * opts: JamilihOptions
49
+ * }} PluginSettings
50
+ */
51
+
52
+ /**
53
+ * @typedef {object} JamilihPlugin
54
+ * @property {string} name
55
+ * @property {(opts: PluginSettings) => string} set
56
+ */
57
+
58
+ /**
59
+ * @type {import('jsdom').DOMWindow|HTMLWindow|undefined}
60
+ */
61
+ let win;
62
+
63
+ /* c8 ignore next */
64
+ if (typeof window !== 'undefined' && window) {
65
+ win = window;
66
+ }
67
+
68
+ /* c8 ignore next */
69
+ let doc = typeof document !== 'undefined' && document || ((_win = win) === null || _win === void 0 ? void 0 : _win.document);
35
70
 
36
71
  // STATIC PROPERTIES
37
72
 
@@ -43,11 +78,7 @@ const possibleOptions = ['$plugins',
43
78
 
44
79
  const NS_HTML = 'http://www.w3.org/1999/xhtml',
45
80
  hyphenForCamelCase = /-([a-z])/gu;
46
- const ATTR_MAP = {
47
- maxlength: 'maxLength',
48
- minlength: 'minLength',
49
- readonly: 'readOnly'
50
- };
81
+ const ATTR_MAP = new Map([['maxlength', 'maxLength'], ['minlength', 'minLength'], ['readonly', 'readOnly']]);
51
82
 
52
83
  // We define separately from ATTR_DOM for clarity (and parity with JsonML) but no current need
53
84
  // We don't set attribute esp. for boolean atts as we want to allow setting of `undefined`
@@ -87,8 +118,27 @@ const NULLABLES = ['autocomplete', 'dir',
87
118
  'max', 'min', 'minLength', 'maxLength', 'title' // HTMLElement
88
119
  ];
89
120
 
90
- const $ = sel => doc.querySelector(sel);
91
- const $$ = sel => [...doc.querySelectorAll(sel)];
121
+ /**
122
+ * @param {string} sel
123
+ * @returns {Element|null}
124
+ */
125
+ const $ = sel => {
126
+ if (!doc) {
127
+ throw new Error('No document object');
128
+ }
129
+ return doc.querySelector(sel);
130
+ };
131
+
132
+ /**
133
+ * @param {string} sel
134
+ * @returns {Element[]}
135
+ */
136
+ const $$ = sel => {
137
+ if (!doc) {
138
+ return [];
139
+ }
140
+ return [...doc.querySelectorAll(sel)];
141
+ };
92
142
 
93
143
  /**
94
144
  * Retrieve the (lower-cased) HTML name of a node.
@@ -101,74 +151,20 @@ function _getHTMLNodeName(node) {
101
151
  }
102
152
 
103
153
  /**
104
- * Apply styles if this is a style tag.
105
- * @static
106
- * @param {Node} node The element to check whether it is a style tag
107
- * @returns {void}
108
- */
109
- function _applyAnyStylesheet(node) {
110
- // Only used in IE
111
- // istanbul ignore else
112
- if (!doc.createStyleSheet) {
113
- return;
114
- }
115
- // istanbul ignore next
116
- if (_getHTMLNodeName(node) === 'style') {
117
- // IE
118
- const ss = doc.createStyleSheet(); // Create a stylesheet to actually do something useful
119
- ss.cssText = node.cssText;
120
- // We continue to add the style tag, however
121
- }
122
- }
123
-
124
- /**
125
- * Need this function for IE since options weren't otherwise getting added.
126
154
  * @private
127
155
  * @static
128
- * @param {Element} parent The parent to which to append the element
129
- * @param {Node} child The element or other node to append to the parent
156
+ * @param {Document|DocumentFragment|Element} parent The parent to which to append the element
157
+ * @param {Node|string} child The element or other node to append to the parent
130
158
  * @throws {Error} Rethrow if problem with `append` and unhandled
131
159
  * @returns {void}
132
160
  */
133
161
  function _appendNode(parent, child) {
134
162
  const parentName = _getHTMLNodeName(parent);
135
-
136
- // IE only
137
- // istanbul ignore if
138
- if (doc.createStyleSheet) {
139
- if (parentName === 'script') {
140
- parent.text = child.nodeValue;
141
- return;
142
- }
143
- if (parentName === 'style') {
144
- parent.cssText = child.nodeValue; // This will not apply it--just make it available within the DOM cotents
145
- return;
146
- }
147
- }
148
163
  if (parentName === 'template') {
149
- parent.content.append(child);
164
+ /** @type {HTMLTemplateElement} */parent.content.append(child);
150
165
  return;
151
166
  }
152
- try {
153
- parent.append(child); // IE9 is now ok with this
154
- } catch (e) {
155
- // istanbul ignore next
156
- const childName = _getHTMLNodeName(child);
157
- // istanbul ignore next
158
- if (parentName === 'select' && childName === 'option') {
159
- try {
160
- // Since this is now DOM Level 4 standard behavior (and what IE7+ can handle), we try it first
161
- parent.add(child);
162
- } catch (err) {
163
- // DOM Level 2 did require a second argument, so we try it too just in case the user is using an older version of Firefox, etc.
164
- parent.add(child, null); // IE7 has a problem with this, but IE8+ is ok
165
- }
166
-
167
- return;
168
- }
169
- // istanbul ignore next
170
- throw e;
171
- }
167
+ parent.append(child); // IE9 is now ok with this
172
168
  }
173
169
 
174
170
  /**
@@ -194,6 +190,9 @@ function _addEvent(el, type, handler, capturing) {
194
190
  * @returns {Text} The text node of the resolved reference
195
191
  */
196
192
  function _createSafeReference(type, prefix, arg) {
193
+ if (!doc) {
194
+ throw new Error('No document defined');
195
+ }
197
196
  // For security reasons related to innerHTML, we ensure this string only
198
197
  // contains potential entity characters
199
198
  if (!/^\w+$/u.test(arg)) {
@@ -228,11 +227,20 @@ function _isNullish(o) {
228
227
  /**
229
228
  * @private
230
229
  * @static
231
- * @param {string|JamilihAttributes|JamilihArray|Element|DocumentFragment} item
232
- * @returns {"string"|"null"|"array"|"element"|"fragment"|"object"|"symbol"|"function"|"number"|"boolean"}
230
+ * @param {string|JamilihAttributes|JamilihArray|JamilihChildren|
231
+ * JamilihDocumentFragment|JamilihAttributeNode|
232
+ * JamilihOptions|Element|Document|DocumentFragment|null|undefined} item
233
+ * @returns {"string"|"null"|"array"|"element"|"fragment"|"object"|
234
+ * "symbol"|"bigint"|"function"|"number"|"boolean"|"undefined"|
235
+ * "document"|"non-container node"}
233
236
  */
234
237
  function _getType(item) {
235
238
  const type = typeof item;
239
+
240
+ // Appease TS
241
+ if (typeof item === 'string' || typeof item === 'undefined') {
242
+ return 'string';
243
+ }
236
244
  switch (type) {
237
245
  case 'object':
238
246
  if (item === null) {
@@ -274,11 +282,16 @@ function _fragReducer(frag, node) {
274
282
  /**
275
283
  * @private
276
284
  * @static
277
- * @param {Object<{string:string}>} xmlnsObj
278
- * @returns {string}
285
+ * @param {Object<string, string>} xmlnsObj
286
+ * @returns {(...n: string[]) => string}
279
287
  */
280
288
  function _replaceDefiner(xmlnsObj) {
281
- return function (n0) {
289
+ /**
290
+ * @param {string[]} n
291
+ * @returns {string}
292
+ */
293
+ return function (...n) {
294
+ const n0 = n[0];
282
295
  let retStr = xmlnsObj[''] ? ' xmlns="' + xmlnsObj[''] + '"' : n0; // Preserve XHTML
283
296
  for (const [ns, xmlnsVal] of Object.entries(xmlnsObj)) {
284
297
  if (ns !== '') {
@@ -290,48 +303,52 @@ function _replaceDefiner(xmlnsObj) {
290
303
  }
291
304
 
292
305
  /**
293
- * @typedef {JamilihAttributes} AttributeArray
294
- * @property {string} 0 The key
295
- * @property {string} 1 The value
296
- */
297
-
298
- /**
299
- * @callback ChildrenToJMLCallback
300
- * @param {JamilihArray|Jamilih} childNodeJML
301
- * @param {Integer} i
302
- * @returns {void}
303
- */
306
+ * @callback ChildrenToJMLCallback
307
+ * @param {JamilihArray|JamilihChildType|string} childNodeJML
308
+ * @param {Integer} i
309
+ * @returns {void}
310
+ */
304
311
 
305
312
  /**
306
- * @private
307
- * @static
308
- * @param {Node} node
309
- * @returns {ChildrenToJMLCallback}
310
- */
313
+ * @private
314
+ * @static
315
+ * @param {Node} node
316
+ * @returns {ChildrenToJMLCallback}
317
+ */
311
318
  function _childrenToJML(node) {
312
319
  return function (childNodeJML, i) {
313
320
  const cn = node.childNodes[i];
314
- const j = Array.isArray(childNodeJML) ? jml(...childNodeJML) : jml(childNodeJML);
321
+ const j = Array.isArray(childNodeJML) ? jml(... /** @type {JamilihArray} */childNodeJML) : jml(childNodeJML);
315
322
  cn.replaceWith(j);
316
323
  };
317
324
  }
318
325
 
326
+ /**
327
+ * Keep this in sync with `JamilihArray`'s first argument (minus `Document`).
328
+ * @typedef {JamilihDoc|JamilihDoctype|JamilihTextNode|
329
+ * JamilihAttributeNode|JamilihOptions|ElementName|Element|
330
+ * JamilihDocumentFragment
331
+ * } JamilihFirstArg
332
+ */
333
+
319
334
  /**
320
335
  * @callback JamilihAppender
321
- * @param {JamilihArray} childJML
336
+ * @param {JamilihArray|JamilihFirstArg|Node} childJML
322
337
  * @returns {void}
323
338
  */
324
339
 
325
340
  /**
326
341
  * @private
327
342
  * @static
328
- * @param {Node} node
343
+ * @param {ParentNode} node
329
344
  * @returns {JamilihAppender}
330
345
  */
331
346
  function _appendJML(node) {
332
347
  return function (childJML) {
333
348
  if (Array.isArray(childJML)) {
334
349
  node.append(jml(...childJML));
350
+ } else if (typeof childJML === 'object' && 'nodeType' in childJML) {
351
+ node.append(childJML);
335
352
  } else {
336
353
  node.append(jml(childJML));
337
354
  }
@@ -340,14 +357,14 @@ function _appendJML(node) {
340
357
 
341
358
  /**
342
359
  * @callback appender
343
- * @param {string|JamilihArray} childJML
360
+ * @param {JamilihArray|JamilihFirstArg|Node} childJML
344
361
  * @returns {void}
345
362
  */
346
363
 
347
364
  /**
348
365
  * @private
349
366
  * @static
350
- * @param {Node} node
367
+ * @param {ParentNode} node
351
368
  * @returns {appender}
352
369
  */
353
370
  function _appendJMLOrText(node) {
@@ -356,6 +373,8 @@ function _appendJMLOrText(node) {
356
373
  node.append(childJML);
357
374
  } else if (Array.isArray(childJML)) {
358
375
  node.append(jml(...childJML));
376
+ } else if (typeof childJML === 'object' && 'nodeType' in childJML) {
377
+ node.append(childJML);
359
378
  } else {
360
379
  node.append(jml(childJML));
361
380
  }
@@ -376,43 +395,333 @@ function _DOMfromJMLOrString (childNodeJML) {
376
395
  */
377
396
 
378
397
  /**
379
- * @typedef {Element|DocumentFragment} JamilihReturn
398
+ * @typedef {Element|DocumentFragment|Comment|Attr|
399
+ * Text|Document|DocumentType|ProcessingInstruction|CDATASection} JamilihReturn
380
400
  */
401
+ // 'string|JamilihOptions|JamilihDocumentFragment|JamilihAttributes|(string|JamilihArray)[]
381
402
 
382
403
  /**
383
- * @typedef {PlainObject<string, string>} JamilihAttributes
384
- */
404
+ * Can either be an array of:
405
+ * 1. JamilihAttributes followed by an array of JamilihArrays or Elements.
406
+ * (Cannot be multiple single JamilihArrays despite TS type).
407
+ * 2. Any number of JamilihArrays.
408
+ * @typedef {[(JamilihAttributes|JamilihArray|JamilihArray[]|Element), ...(JamilihArray|JamilihArray[]|Element)[]]} TemplateJamilihArray
409
+ */
385
410
 
386
411
  /**
387
- * @typedef {GenericArray} JamilihArray
388
- * @property {string} 0 The element to create (by lower-case name)
389
- * @property {JamilihAttributes} [1] Attributes to add with the key as the
390
- * attribute name and value as the attribute value; important for IE where
391
- * the input element's type cannot be added later after already added to the page
392
- * @param {Element[]} [children] The optional children of this element
393
- * (but raw DOM elements required to be specified within arrays since
394
- * could not otherwise be distinguished from siblings being added)
395
- * @param {Element} [parent] The optional parent to which to attach the element
396
- * (always the last unless followed by null, in which case it is the
397
- * second-to-last)
398
- * @param {null} [returning] Can use null to indicate an array of elements
399
- * should be returned
400
- */
412
+ * @typedef {(JamilihArray|Element)[]} ShadowRootJamilihArrayContainer
413
+ */
414
+
415
+ /**
416
+ * @typedef {{
417
+ * open?: boolean|ShadowRootJamilihArrayContainer,
418
+ * closed?: boolean|ShadowRootJamilihArrayContainer,
419
+ * template?: string|HTMLTemplateElement|TemplateJamilihArray,
420
+ * content?: ShadowRootJamilihArrayContainer|DocumentFragment
421
+ * }} JamilihShadowRootObject
422
+ */
423
+
424
+ /**
425
+ * @typedef {{[key: string]: string}} XmlnsAttributeObject
426
+ */
427
+
428
+ /**
429
+ * @typedef {null|XmlnsAttributeObject} XmlnsAttributeValue
430
+ */
431
+
432
+ /**
433
+ * @typedef {{
434
+ * [key: string]: string|number|null|DatasetAttributeObject
435
+ * }} DatasetAttributeObject
436
+ */
437
+
438
+ /**
439
+ * @typedef {string|undefined|{[key: string]: string|null}} StyleAttributeValue
440
+ */
441
+
442
+ /**
443
+ * @typedef {(this: Element, event?: Event) => void} EventHandler
444
+ */
401
445
 
402
446
  /**
403
- * @typedef {PlainObject} JamilihOptions
404
- * @property {"root"|"attributeValue"|"fragment"|"children"|"fragmentChildren"} $state
447
+ * @typedef {{
448
+ * [key: string]: EventHandler|[EventHandler, boolean]
449
+ * }} OnAttributeObject
450
+ */
451
+
452
+ /**
453
+ * @typedef {{
454
+ * $on?: OnAttributeObject|null
455
+ * }} OnAttribute
456
+ */
457
+
458
+ /**
459
+ * @typedef {boolean} BooleanAttribute
460
+ */
461
+
462
+ /**
463
+ * @typedef {((this: Element, event?: Event) => void)} HandlerAttributeValue
464
+ */
465
+
466
+ /* eslint-disable jsdoc/valid-types -- jsdoc-type-pratt-parser Bug */
467
+ /**
468
+ * @typedef {{
469
+ * [key: string]: HandlerAttributeValue
470
+ * }} OnHandlerObject
471
+ */
472
+
473
+ /**
474
+ * @typedef {number} StringifiableNumber
475
+ */
476
+
477
+ /**
478
+ * @typedef {{
479
+ * name: string,
480
+ * systemId?: string,
481
+ * publicId?: string
482
+ * }} JamilihDocumentType
483
+ */
484
+
485
+ /**
486
+ * @typedef {string|{extends?: string}} DefineOptions
487
+ */
488
+
489
+ /**
490
+ * @typedef {{[key: string]: string|number|boolean|((this: DefineMixin, ...args: any[]) => any)}} DefineMixin
491
+ */
492
+
493
+ /**
494
+ * @typedef {{
495
+ * new (): HTMLElement;
496
+ * prototype: HTMLElement & {[key: string]: any}
497
+ * }} DefineConstructor
498
+ */
499
+ /* eslint-enable jsdoc/valid-types -- https://github.com/jsdoc-type-pratt-parser/jsdoc-type-pratt-parser/issues/131 */
500
+
501
+ /**
502
+ * @typedef {(this: HTMLElement) => void} DefineUserConstructor
503
+ */
504
+
505
+ /**
506
+ * @typedef {[DefineConstructor|DefineUserConstructor|DefineMixin, DefineOptions?]|[DefineConstructor|DefineUserConstructor, DefineMixin?, DefineOptions?]} DefineObjectArray
507
+ */
508
+
509
+ /**
510
+ * @typedef {DefineObjectArray|DefineConstructor|DefineMixin|DefineUserConstructor} DefineObject
511
+ */
512
+
513
+ /**
514
+ * @typedef {{elem?: Element, [key: string]: any}} SymbolObject
515
+ */
516
+
517
+ /**
518
+ * @typedef {[symbol|string, ((this: Element, ...args: any[]) => any)|SymbolObject]} SymbolArray
519
+ */
520
+
521
+ /**
522
+ * @typedef {null|undefined} NullableAttributeValue
523
+ */
524
+
525
+ /**
526
+ * @typedef {(string|NullableAttributeValue|BooleanAttribute|
527
+ * JamilihArray|JamilihShadowRootObject|StringifiableNumber|
528
+ * JamilihDocumentType|JamilihDocument|XmlnsAttributeValue|
529
+ * OnAttributeObject|
530
+ * HandlerAttributeValue|DefineObject|SymbolArray|PluginReference
531
+ * )} JamilihAttValue
532
+ */
533
+
534
+ /**
535
+ * @typedef {{
536
+ * [key: string]: string|number|((this: Element, ...args: any[]) => any)
537
+ * }} DataAttributeObject
405
538
  */
406
539
 
407
540
  /**
408
- * @param {Element} elem
409
- * @param {string} att
410
- * @param {string} attVal
541
+ * @typedef {{
542
+ * $data?: true|string[]|Map<any, any>|WeakMap<any, any>|DataAttributeObject|
543
+ * [undefined, DataAttributeObject]|
544
+ * [Map<any, any>|WeakMap<any, any>|undefined, DataAttributeObject]
545
+ * }} DataAttribute
546
+ */
547
+
548
+ /**
549
+ * @typedef {{
550
+ * dataset?: DatasetAttributeObject
551
+ * }} DatasetAttribute
552
+ */
553
+
554
+ /**
555
+ * @typedef {{
556
+ * style?: StyleAttributeValue
557
+ * }} StyleAttribute
558
+ */
559
+
560
+ /**
561
+ * @typedef {{
562
+ * $shadow?: JamilihShadowRootObject
563
+ * }} JamilihShadowRootAttribute
564
+ */
565
+
566
+ /* eslint-disable jsdoc/valid-types -- jsdoc-type-pratt-parser Bug */
567
+ /**
568
+ * @typedef {{
569
+ * is?: string|null,
570
+ * $define?: DefineObject
571
+ * }} DefineAttribute
572
+ */
573
+ /* eslint-enable jsdoc/valid-types -- jsdoc-type-pratt-parser Bug */
574
+
575
+ /**
576
+ * @typedef {{
577
+ * $custom?: {[key: string]: any}
578
+ * }} CustomAttribute
579
+ */
580
+
581
+ /**
582
+ * @typedef {{
583
+ * $symbol?: SymbolArray
584
+ * }} SymbolAttribute
585
+ */
586
+
587
+ /**
588
+ * @typedef {{
589
+ * xmlns?: string|null|XmlnsAttributeObject
590
+ * }} XmlnsAttribute
591
+ */
592
+
593
+ /**
594
+ * `OnHandlerObject &` wasn't working, so added `HandlerAttributeValue`.
595
+ * @typedef {DataAttribute & StyleAttribute & JamilihShadowRootAttribute &
596
+ * DefineAttribute & DatasetAttribute & CustomAttribute & SymbolAttribute &
597
+ * OnAttribute & XmlnsAttribute &
598
+ * Partial<JamilihAttributeNode> & Partial<JamilihTextNode> &
599
+ * Partial<JamilihDoc> & Partial<JamilihDoctype> & {
600
+ * [key: string]: JamilihAttValue|HandlerAttributeValue,
601
+ * }} JamilihAttributes
602
+ */
603
+
604
+ /**
605
+ * @typedef {{
606
+ * title?: string,
607
+ * childNodes?: JamilihChildType[],
608
+ * $DOCTYPE?: JamilihDocumentType,
609
+ * head?: JamilihChildren
610
+ * body?: JamilihChildren
611
+ * }} JamilihDocument
612
+ */
613
+
614
+ /**
615
+ * @typedef {{
616
+ * $document: JamilihDocument
617
+ * }} JamilihDoc
618
+ */
619
+
620
+ /**
621
+ * @typedef {{$DOCTYPE: JamilihDocumentType}} JamilihDoctype
622
+ */
623
+
624
+ /**
625
+ * @typedef {{'#': (JamilihArray|TextNodeString|Element)[]}} JamilihDocumentFragment
626
+ */
627
+
628
+ /**
629
+ * @typedef {string} ElementName
630
+ */
631
+
632
+ /**
633
+ * @typedef {string} TextNodeString
634
+ */
635
+
636
+ /**
637
+ * @typedef {{[key: string]: string}} PluginReference
638
+ */
639
+
640
+ /**
641
+ * @typedef {(
642
+ * JamilihArray|TextNodeString|Element|Comment|ProcessingInstruction|
643
+ * Text|DocumentFragment|JamilihProcessingInstruction|JamilihDocumentFragment|
644
+ * PluginReference
645
+ * )[]} JamilihChildren
646
+ */
647
+
648
+ /**
649
+ * @typedef {Document|ElementName|Element|DocumentFragment|
650
+ * JamilihDocumentFragment|JamilihDoc|JamilihDoctype|JamilihTextNode|
651
+ * JamilihAttributeNode} JamilihFirstArgument
652
+ */
653
+
654
+ /**
655
+ * This would be clearer with overrides, but using as typedef.
656
+ *
657
+ * The optional 0th argument is an Jamilih options object or fragment.
658
+ *
659
+ * The first argument is the element to create (by lower-case name) or DOM element.
660
+ *
661
+ * The second optional argument are attributes to add with the key as the
662
+ * attribute name and value as the attribute value.
663
+ * The third optional argument are an array of children for this element
664
+ * (but raw DOM elements are required to be specified within arrays since
665
+ * could not otherwise be distinguished from siblings being added).
666
+ * The fourth optional argument are a sequence of sibling Elements, represented
667
+ * as DOM elements, or string/attributes/children sequences.
668
+ * The fifth optional argument is the parent to which to attach the element
669
+ * (always the last unless followed by null, in which case it is the
670
+ * second-to-last).
671
+ * The sixth last optional argument is null, used to indicate an array of elements
672
+ * should be returned.
673
+ * @typedef {[
674
+ * JamilihOptions|JamilihFirstArgument,
675
+ * (JamilihFirstArgument|
676
+ * JamilihAttributes|
677
+ * JamilihChildren|
678
+ * Element|ShadowRoot|
679
+ * null)?,
680
+ * (JamilihAttributes|
681
+ * JamilihChildren|
682
+ * Element|ShadowRoot|
683
+ * ElementName|null)?,
684
+ * ...(JamilihAttributes|
685
+ * JamilihChildren|
686
+ * Element|ShadowRoot|
687
+ * ElementName|null)[]
688
+ * ]} JamilihArray
689
+ */
690
+
691
+ /**
692
+ * @typedef {[
693
+ * (string|Element|ShadowRoot), (JamilihArray[]|JamilihAttributes|Element|ShadowRoot|null)?, ...(JamilihArray[]|Element|JamilihAttributes|ShadowRoot|null)[]
694
+ * ]} JamilihArrayPostOptions
695
+ */
696
+
697
+ /**
698
+ * @typedef {{
699
+ * root: [Map<Element,any>|WeakMap<Element,any>, any],
700
+ * [key: string]: [Map<Element,any>|WeakMap<Element,any>, any]
701
+ * }} MapWithRoot
702
+ */
703
+
704
+ /**
705
+ * @typedef {"root"|"attributeValue"|"element"|"fragment"|"children"|"fragmentChildren"} TraversalState
706
+ */
707
+
708
+ /**
709
+ * @typedef {object} JamilihOptions
710
+ * @property {TraversalState} [$state]
711
+ * @property {JamilihPlugin[]} [$plugins]
712
+ * @property {MapWithRoot|[Map<Element,any>|WeakMap<Element,any>, any]} [$map]
713
+ */
714
+
715
+ /**
716
+ * @param {Document|Element|DocumentFragment} elem
717
+ * @param {string|null} att
718
+ * @param {JamilihAttValue} attVal
411
719
  * @param {JamilihOptions} opts
412
- * @returns {void}
720
+ * @param {TraversalState} [state]
721
+ * @returns {string|null}
413
722
  */
414
- function checkPluginValue(elem, att, attVal, opts) {
415
- opts.$state = 'attributeValue';
723
+ function checkPluginValue(elem, att, attVal, opts, state) {
724
+ opts.$state = state !== null && state !== void 0 ? state : 'attributeValue';
416
725
  if (attVal && typeof attVal === 'object') {
417
726
  const matchingPlugin = getMatchingPlugin(opts, Object.keys(attVal)[0]);
418
727
  if (matchingPlugin) {
@@ -426,17 +735,18 @@ function checkPluginValue(elem, att, attVal, opts) {
426
735
  });
427
736
  }
428
737
  }
429
- return attVal;
738
+ return (/** @type {string} */attVal
739
+ );
430
740
  }
431
741
 
432
742
  /**
433
743
  * @param {JamilihOptions} opts
434
- * @param {string} item
435
- * @returns {JamilihPlugin}
744
+ * @param {string} pluginName
745
+ * @returns {JamilihPlugin|undefined}
436
746
  */
437
- function getMatchingPlugin(opts, item) {
747
+ function getMatchingPlugin(opts, pluginName) {
438
748
  return opts.$plugins && opts.$plugins.find(p => {
439
- return p.name === item;
749
+ return p.name === pluginName;
440
750
  });
441
751
  }
442
752
 
@@ -444,29 +754,47 @@ function getMatchingPlugin(opts, item) {
444
754
  * Creates an XHTML or HTML element (XHTML is preferred, but only in browsers
445
755
  * that support); any element after element can be omitted, and any subsequent
446
756
  * type or types added afterwards.
447
- * @param {...JamilihArray} args
757
+ * @param {JamilihArray} args
448
758
  * @returns {JamilihReturn} The newly created (and possibly already appended)
449
759
  * element or array of elements
450
760
  */
451
761
  const jml = function jml(...args) {
762
+ if (!win) {
763
+ throw new Error('No window object');
764
+ }
765
+ if (!doc) {
766
+ throw new Error('No document object');
767
+ }
768
+
769
+ /** @type {(Document|DocumentFragment|Element) & {[key: string]: any}} */
452
770
  let elem = doc.createDocumentFragment();
453
771
  /**
454
772
  *
455
- * @param {Object<{string: string}>} atts
773
+ * @param {JamilihAttributes} atts
456
774
  * @throws {TypeError}
457
775
  * @returns {void}
458
776
  */
459
777
  function _checkAtts(atts) {
778
+ if (!doc) {
779
+ throw new Error('No document object');
780
+ }
460
781
  for (let [att, attVal] of Object.entries(atts)) {
461
- att = att in ATTR_MAP ? ATTR_MAP[att] : att;
782
+ var _ATTR_MAP$get;
783
+ att = (_ATTR_MAP$get = ATTR_MAP.get(att)) !== null && _ATTR_MAP$get !== void 0 ? _ATTR_MAP$get : att;
784
+
785
+ /**
786
+ * @typedef {any} ElementExpando
787
+ */
788
+
462
789
  if (NULLABLES.includes(att)) {
463
- attVal = checkPluginValue(elem, att, attVal, opts);
790
+ attVal = checkPluginValue(elem, att, /** @type {string|JamilihArray} */attVal, opts);
464
791
  if (!_isNullish(attVal)) {
465
- elem[att] = attVal;
792
+ /** @type {ElementExpando} */elem[att] = attVal;
466
793
  }
467
794
  continue;
468
795
  } else if (ATTR_DOM.includes(att)) {
469
- attVal = checkPluginValue(elem, att, attVal, opts);
796
+ attVal = checkPluginValue(elem, att, /** @type {string|JamilihArray} */attVal, opts);
797
+ /** @type {ElementExpando} */
470
798
  elem[att] = attVal;
471
799
  continue;
472
800
  }
@@ -484,8 +812,8 @@ const jml = function jml(...args) {
484
812
  case '#':
485
813
  {
486
814
  // Document fragment
487
- opts.$state = 'fragmentChilden';
488
- nodes[nodes.length] = jml(opts, attVal);
815
+ opts.$state = 'fragmentChildren';
816
+ nodes[nodes.length] = jml(opts, /** @type {JamilihArray[]} */attVal);
489
817
  break;
490
818
  }
491
819
  case '$shadow':
@@ -493,24 +821,38 @@ const jml = function jml(...args) {
493
821
  const {
494
822
  open,
495
823
  closed
496
- } = attVal;
824
+ } = /** @type {JamilihShadowRootObject} */attVal;
497
825
  let {
498
826
  content,
499
827
  template
500
- } = attVal;
501
- const shadowRoot = elem.attachShadow({
828
+ } = /** @type {JamilihShadowRootObject} */attVal;
829
+ const shadowRoot = /** @type {Element} */elem.attachShadow({
502
830
  mode: closed || open === false ? 'closed' : 'open'
503
831
  });
504
832
  if (template) {
505
833
  if (Array.isArray(template)) {
506
- template = _getType(template[0]) === 'object' ? jml('template', ...template, doc.body) : jml('template', template, doc.body);
834
+ template = /** @type {HTMLTemplateElement} */
835
+ _getType(template[0]) === 'object' ? jml('template', ...
836
+ /**
837
+ * @type {[
838
+ * JamilihAttributes, ...(JamilihArray[]|Element)[]
839
+ * ]}
840
+ */
841
+ template, doc.body) : jml('template',
842
+ /**
843
+ * @type {JamilihArray[]|Element}
844
+ */
845
+ template, doc.body);
507
846
  } else if (typeof template === 'string') {
508
- template = $(template);
847
+ template = /** @type {HTMLTemplateElement} */$(template);
509
848
  }
510
- jml(template.content.cloneNode(true), shadowRoot);
849
+ jml( /** @type {HTMLTemplateElement} */
850
+ /** @type {HTMLTemplateElement} */template.content.cloneNode(true), shadowRoot);
511
851
  } else {
512
852
  if (!content) {
513
- content = open || closed;
853
+ if (open !== true) {
854
+ content = open || typeof closed === 'boolean' ? content : closed;
855
+ }
514
856
  }
515
857
  if (content && typeof content !== 'boolean') {
516
858
  if (Array.isArray(content)) {
@@ -540,9 +882,11 @@ const jml = function jml(...args) {
540
882
  Object.assign(elem, attVal);
541
883
  break;
542
884
  }
543
- /* istanbul ignore next */
544
885
  case '$define':
545
886
  {
887
+ if (!('localName' in elem)) {
888
+ throw new Error('Element expected for `$define`');
889
+ }
546
890
  const localName = elem.localName.toLowerCase();
547
891
  // Note: customized built-ins sadly not working yet
548
892
  const customizedBuiltIn = !localName.includes('-');
@@ -553,7 +897,7 @@ const jml = function jml(...args) {
553
897
  if (customizedBuiltIn) {
554
898
  is = elem.getAttribute('is');
555
899
  if (!is) {
556
- if (!{}.hasOwnProperty.call(atts, 'is')) {
900
+ if (!Object.hasOwn(atts, 'is')) {
557
901
  throw new TypeError(`Expected \`is\` with \`$define\` on built-in; args: ${JSON.stringify(args)}`);
558
902
  }
559
903
  atts.is = checkPluginValue(elem, 'is', atts.is, opts);
@@ -563,12 +907,20 @@ const jml = function jml(...args) {
563
907
  } = atts);
564
908
  }
565
909
  }
566
- const def = customizedBuiltIn ? is : localName;
910
+ const def = customizedBuiltIn ? /** @type {string} */is : localName;
567
911
  if (window.customElements.get(def)) {
568
912
  break;
569
913
  }
914
+
915
+ /**
916
+ * @param {DefineUserConstructor} [cnstrct]
917
+ * @returns {DefineConstructor}
918
+ */
570
919
  const getConstructor = cnstrct => {
571
- const baseClass = options && options.extends ? doc.createElement(options.extends).constructor : customizedBuiltIn ? doc.createElement(localName).constructor : window.HTMLElement;
920
+ if (!doc) {
921
+ throw new Error('No document object');
922
+ }
923
+ const baseClass = typeof options === 'object' && typeof options.extends === 'string' ? /** @type {typeof HTMLElement} */doc.createElement(options.extends).constructor : customizedBuiltIn ? /** @type {typeof HTMLElement} */doc.createElement(localName).constructor : window.HTMLElement;
572
924
 
573
925
  /**
574
926
  * Class wrapping base class.
@@ -579,14 +931,24 @@ const jml = function jml(...args) {
579
931
  */
580
932
  constructor() {
581
933
  super();
934
+ /** @type {DefineUserConstructor} */
582
935
  cnstrct.call(this);
583
936
  }
584
937
  } : class extends baseClass {};
585
938
  };
586
- let cnstrctr, options, mixin;
587
- if (Array.isArray(attVal)) {
588
- if (attVal.length <= 2) {
589
- [cnstrctr, options] = attVal;
939
+
940
+ /** @type {DefineConstructor|DefineUserConstructor|DefineMixin} */
941
+ let cnstrctr;
942
+
943
+ /**
944
+ * @type {DefineOptions|undefined}
945
+ */
946
+ let options;
947
+ let mixin;
948
+ const defineObj = /** @type {DefineObject} */attVal;
949
+ if (Array.isArray(defineObj)) {
950
+ if (defineObj.length <= 2) {
951
+ [cnstrctr, options] = defineObj;
590
952
  if (typeof options === 'string') {
591
953
  // Todo: Allow creating a definition without using it;
592
954
  // that may be the only reason to have a string here which
@@ -594,7 +956,7 @@ const jml = function jml(...args) {
594
956
  options = {
595
957
  extends: options
596
958
  };
597
- } else if (options && !{}.hasOwnProperty.call(options, 'extends')) {
959
+ } else if (options && !Object.hasOwn(options, 'extends')) {
598
960
  mixin = options;
599
961
  }
600
962
  if (typeof cnstrctr === 'object') {
@@ -602,21 +964,21 @@ const jml = function jml(...args) {
602
964
  cnstrctr = getConstructor();
603
965
  }
604
966
  } else {
605
- [cnstrctr, mixin, options] = attVal;
967
+ [cnstrctr, mixin, options] = defineObj;
606
968
  if (typeof options === 'string') {
607
969
  options = {
608
970
  extends: options
609
971
  };
610
972
  }
611
973
  }
612
- } else if (typeof attVal === 'function') {
613
- cnstrctr = attVal;
974
+ } else if (typeof defineObj === 'function') {
975
+ cnstrctr = /** @type {DefineConstructor} */defineObj;
614
976
  } else {
615
- mixin = attVal;
977
+ mixin = defineObj;
616
978
  cnstrctr = getConstructor();
617
979
  }
618
980
  if (!cnstrctr.toString().startsWith('class')) {
619
- cnstrctr = getConstructor(cnstrctr);
981
+ cnstrctr = getConstructor( /** @type {DefineUserConstructor} */cnstrctr);
620
982
  }
621
983
  if (!options && customizedBuiltIn) {
622
984
  options = {
@@ -625,29 +987,33 @@ const jml = function jml(...args) {
625
987
  }
626
988
  if (mixin) {
627
989
  Object.entries(mixin).forEach(([methodName, method]) => {
628
- cnstrctr.prototype[methodName] = method;
990
+ /** @type {DefineConstructor} */cnstrctr.prototype[methodName] = method;
629
991
  });
630
992
  }
631
993
  // console.log('def', def, '::', typeof options === 'object' ? options : undefined);
632
- window.customElements.define(def, cnstrctr, typeof options === 'object' ? options : undefined);
994
+ window.customElements.define(def, /** @type {DefineConstructor} */cnstrctr, typeof options === 'object' ? options : undefined);
633
995
  break;
634
996
  }
635
997
  case '$symbol':
636
998
  {
637
- const [symbol, func] = attVal;
999
+ const [symbol, func] = /** @type {SymbolArray} */attVal;
638
1000
  if (typeof func === 'function') {
639
- const funcBound = func.bind(elem);
1001
+ const funcBound = func.bind( /** @type {Element} */elem);
640
1002
  if (typeof symbol === 'string') {
1003
+ // @ts-expect-error
641
1004
  elem[Symbol.for(symbol)] = funcBound;
642
1005
  } else {
1006
+ // @ts-expect-error
643
1007
  elem[symbol] = funcBound;
644
1008
  }
645
1009
  } else {
646
1010
  const obj = func;
647
- obj.elem = elem;
1011
+ obj.elem = /** @type {Element} */elem;
648
1012
  if (typeof symbol === 'string') {
1013
+ // @ts-expect-error
649
1014
  elem[Symbol.for(symbol)] = obj;
650
1015
  } else {
1016
+ // @ts-expect-error
651
1017
  elem[symbol] = obj;
652
1018
  }
653
1019
  }
@@ -655,72 +1021,90 @@ const jml = function jml(...args) {
655
1021
  }
656
1022
  case '$data':
657
1023
  {
658
- setMap(attVal);
1024
+ setMap( /** @type {true|string[]|Map<any, any>|WeakMap<any, any>|DataAttributeObject} */
1025
+ attVal);
659
1026
  break;
660
1027
  }
661
1028
  case '$attribute':
662
1029
  {
663
1030
  // Attribute node
664
- const node = attVal.length === 3 ? doc.createAttributeNS(attVal[0], attVal[1]) : doc.createAttribute(attVal[0]);
665
- node.value = attVal[attVal.length - 1];
1031
+ const attr = /** @type {JamilihAttributeNodeValue} */attVal;
1032
+ const node = attr.length === 3 ? doc.createAttributeNS(attr[0], attr[1]) : doc.createAttribute( /** @type {string} */attr[0]);
1033
+ node.value = /** @type {string} */attr[attr.length - 1];
666
1034
  nodes[nodes.length] = node;
667
1035
  break;
668
1036
  }
669
1037
  case '$text':
670
1038
  {
671
1039
  // Todo: Also allow as jml(['a text node']) (or should that become a fragment)?
672
- const node = doc.createTextNode(attVal);
1040
+ const node = doc.createTextNode( /** @type {string} */attVal);
673
1041
  nodes[nodes.length] = node;
674
1042
  break;
675
1043
  }
676
1044
  case '$document':
677
1045
  {
678
1046
  // Todo: Conditionally create XML document
679
- const node = doc.implementation.createHTMLDocument();
680
- if (attVal.childNodes) {
1047
+ const docNode = doc.implementation.createHTMLDocument();
1048
+ if (!attVal) {
1049
+ throw new Error('Bad attribute value');
1050
+ }
1051
+ const jamlihDoc = /** @type {JamilihDocument} */attVal;
1052
+ if (jamlihDoc.childNodes) {
681
1053
  // Remove any extra nodes created by createHTMLDocument().
682
- const j = attVal.childNodes.length;
683
- while (node.childNodes[j]) {
684
- const cn = node.childNodes[j];
1054
+ const j = jamlihDoc.childNodes.length;
1055
+ while (docNode.childNodes[j]) {
1056
+ const cn = docNode.childNodes[j];
685
1057
  cn.remove();
686
1058
  // `j` should stay the same as removing will cause node to be present
687
1059
  }
688
1060
 
689
- attVal.childNodes.forEach(_childrenToJML(node));
1061
+ jamlihDoc.childNodes.forEach(_childrenToJML(docNode));
690
1062
  } else {
691
- if (attVal.$DOCTYPE) {
1063
+ if (jamlihDoc.$DOCTYPE) {
1064
+ var _docNode$firstChild;
692
1065
  const dt = {
693
- $DOCTYPE: attVal.$DOCTYPE
1066
+ $DOCTYPE: jamlihDoc.$DOCTYPE
694
1067
  };
695
1068
  const doctype = jml(dt);
696
- node.firstChild.replaceWith(doctype);
1069
+ (_docNode$firstChild = docNode.firstChild) === null || _docNode$firstChild === void 0 ? void 0 : _docNode$firstChild.replaceWith(doctype);
697
1070
  }
698
- const html = node.childNodes[1];
699
- const head = html.childNodes[0];
700
- const body = html.childNodes[1];
701
- if (attVal.title || attVal.head) {
1071
+ const html = docNode.querySelector('html');
1072
+ const head = html === null || html === void 0 ? void 0 : html.querySelector('head');
1073
+ const body = html === null || html === void 0 ? void 0 : html.querySelector('body');
1074
+ if (jamlihDoc.title || jamlihDoc.head) {
702
1075
  const meta = doc.createElement('meta');
703
1076
  // eslint-disable-next-line unicorn/text-encoding-identifier-case -- HTML
704
1077
  meta.setAttribute('charset', 'utf-8');
705
- head.append(meta);
706
- if (attVal.title) {
707
- node.title = attVal.title; // Appends after meta
1078
+ head === null || head === void 0 ? void 0 : head.append(meta);
1079
+ if (jamlihDoc.title) {
1080
+ docNode.title = jamlihDoc.title; // Appends after meta
708
1081
  }
709
1082
 
710
- if (attVal.head) {
711
- attVal.head.forEach(_appendJML(head));
1083
+ if (jamlihDoc.head && head) {
1084
+ // each child of `head` is:
1085
+ // (JamilihArray|TextNodeString|Element|Comment|ProcessingInstruction|
1086
+ // Text|DocumentFragment|JamilihProcessingInstruction|JamilihDocumentFragment)
1087
+
1088
+ // * @typedef {JamilihDoc|JamilihDoctype|JamilihTextNode|
1089
+ // * JamilihAttributeNode|JamilihOptions|ElementName|Element|
1090
+ // * JamilihDocumentFragment
1091
+ // * } JamilihFirstArg
1092
+ // appender childJML param is: JamilihArray|JamilihFirstArg
1093
+
1094
+ jamlihDoc.head.forEach(_appendJML(head));
712
1095
  }
713
1096
  }
714
- if (attVal.body) {
715
- attVal.body.forEach(_appendJMLOrText(body));
1097
+ if (jamlihDoc.body && body) {
1098
+ jamlihDoc.body.forEach(_appendJMLOrText(body));
716
1099
  }
717
1100
  }
718
- nodes[nodes.length] = node;
1101
+ nodes[nodes.length] = docNode;
719
1102
  break;
720
1103
  }
721
1104
  case '$DOCTYPE':
722
1105
  {
723
- const node = doc.implementation.createDocumentType(attVal.name, attVal.publicId || '', attVal.systemId || '');
1106
+ const doctype = /** @type {JamilihDocumentType} */attVal;
1107
+ const node = doc.implementation.createDocumentType(doctype.name, doctype.publicId || '', doctype.systemId || '');
724
1108
  nodes[nodes.length] = node;
725
1109
  break;
726
1110
  }
@@ -728,21 +1112,21 @@ const jml = function jml(...args) {
728
1112
  {
729
1113
  // Events
730
1114
  // Allow for no-op by defaulting to `{}`
731
- for (let [p2, val] of Object.entries(attVal || {})) {
1115
+ for (let [p2, val] of Object.entries( /** @type {OnAttributeObject} */attVal || {})) {
732
1116
  if (typeof val === 'function') {
733
1117
  val = [val, false];
734
1118
  }
735
1119
  if (typeof val[0] !== 'function') {
736
1120
  throw new TypeError(`Expect a function for \`$on\`; args: ${JSON.stringify(args)}`);
737
1121
  }
738
- _addEvent(elem, p2, val[0], val[1]); // element, event name, handler, capturing
1122
+ _addEvent( /** @type {Element} */elem, p2, val[0], val[1]); // element, event name, handler, capturing
739
1123
  }
740
1124
 
741
1125
  break;
742
1126
  }
743
1127
  case 'className':
744
1128
  case 'class':
745
- attVal = checkPluginValue(elem, att, attVal, opts);
1129
+ attVal = checkPluginValue(elem, att, /** @type {string} */attVal, opts);
746
1130
  if (!_isNullish(attVal)) {
747
1131
  elem.className = attVal;
748
1132
  }
@@ -750,6 +1134,11 @@ const jml = function jml(...args) {
750
1134
  case 'dataset':
751
1135
  {
752
1136
  // Map can be keyed with hyphenated or camel-cased properties
1137
+ /**
1138
+ * @param {DatasetAttributeObject} atVal
1139
+ * @param {string} startProp
1140
+ * @returns {void}
1141
+ */
753
1142
  const recurse = (atVal, startProp) => {
754
1143
  let prop = '';
755
1144
  const pastInitialProp = startProp !== '';
@@ -766,7 +1155,7 @@ const jml = function jml(...args) {
766
1155
  recurse(value, prop);
767
1156
  });
768
1157
  };
769
- recurse(attVal, '');
1158
+ recurse( /** @type {DatasetAttributeObject} */attVal, '');
770
1159
  break;
771
1160
  // Todo: Disable this by default unless configuration explicitly allows (for security)
772
1161
  }
@@ -782,13 +1171,13 @@ const jml = function jml(...args) {
782
1171
  case 'htmlFor':
783
1172
  case 'for':
784
1173
  if (elStr === 'label') {
785
- attVal = checkPluginValue(elem, att, attVal, opts);
1174
+ attVal = checkPluginValue(elem, att, /** @type {string} */attVal, opts);
786
1175
  if (!_isNullish(attVal)) {
787
1176
  elem.htmlFor = attVal;
788
1177
  }
789
1178
  break;
790
1179
  }
791
- attVal = checkPluginValue(elem, att, attVal, opts);
1180
+ attVal = checkPluginValue(elem, att, /** @type {string} */attVal, opts);
792
1181
  elem.setAttribute(att, attVal);
793
1182
  break;
794
1183
  case 'xmlns':
@@ -797,13 +1186,14 @@ const jml = function jml(...args) {
797
1186
  default:
798
1187
  {
799
1188
  if (att.startsWith('on')) {
800
- attVal = checkPluginValue(elem, att, attVal, opts);
1189
+ attVal = checkPluginValue(elem, att, /** @type {HandlerAttributeValue} */attVal, opts);
801
1190
  elem[att] = attVal;
802
1191
  // _addEvent(elem, att.slice(2), attVal, false); // This worked, but perhaps the user wishes only one event
803
1192
  break;
804
1193
  }
805
1194
  if (att === 'style') {
806
- attVal = checkPluginValue(elem, att, attVal, opts);
1195
+ attVal = /** @type {string} */
1196
+ checkPluginValue(elem, att, /** @type {StyleAttributeValue} */attVal, opts);
807
1197
  if (_isNullish(attVal)) {
808
1198
  break;
809
1199
  }
@@ -834,36 +1224,46 @@ const jml = function jml(...args) {
834
1224
  */
835
1225
  break;
836
1226
  }
837
- const matchingPlugin = getMatchingPlugin(opts, att);
1227
+ const pluginName = att;
1228
+ const matchingPlugin = getMatchingPlugin(opts, pluginName);
838
1229
  if (matchingPlugin) {
839
1230
  matchingPlugin.set({
840
1231
  opts,
841
1232
  element: elem,
842
1233
  attribute: {
843
- name: att,
844
- value: attVal
1234
+ name: pluginName,
1235
+ value: /** @type {PluginReference} */attVal
845
1236
  }
846
1237
  });
847
1238
  break;
848
1239
  }
849
- attVal = checkPluginValue(elem, att, attVal, opts);
1240
+ attVal = checkPluginValue(elem, att, /** @type {string} */attVal, opts);
850
1241
  elem.setAttribute(att, attVal);
851
1242
  break;
852
1243
  }
853
1244
  }
854
1245
  }
855
1246
  }
1247
+
1248
+ /**
1249
+ * @type {JamilihReturn[]}
1250
+ */
856
1251
  const nodes = [];
1252
+
1253
+ /** @type {string} */
857
1254
  let elStr;
1255
+
1256
+ /** @type {JamilihOptions} */
858
1257
  let opts;
859
1258
  let isRoot = false;
1259
+ let argStart = 0;
860
1260
  if (_getType(args[0]) === 'object' && Object.keys(args[0]).some(key => possibleOptions.includes(key))) {
861
- opts = args[0];
1261
+ opts = /** @type {JamilihOptions} */args[0];
862
1262
  if (opts.$state === undefined) {
863
1263
  isRoot = true;
864
1264
  opts.$state = 'root';
865
1265
  }
866
- if (opts.$map && !opts.$map.root && opts.$map.root !== false) {
1266
+ if (Array.isArray(opts.$map)) {
867
1267
  opts.$map = {
868
1268
  root: opts.$map
869
1269
  };
@@ -884,51 +1284,57 @@ const jml = function jml(...args) {
884
1284
  }
885
1285
  });
886
1286
  }
887
- args = args.slice(1);
1287
+ argStart = 1;
888
1288
  } else {
889
1289
  opts = {
890
1290
  $state: undefined
891
1291
  };
892
1292
  }
893
1293
  const argc = args.length;
894
- const defaultMap = opts.$map && opts.$map.root;
1294
+ const defaultMap = opts.$map && /** @type {MapWithRoot} */opts.$map.root;
1295
+
1296
+ /**
1297
+ * @param {true|string[]|Map<any, any>|WeakMap<any, any>|DataAttributeObject} dataVal
1298
+ * @returns {void}
1299
+ */
895
1300
  const setMap = dataVal => {
896
1301
  let map, obj;
1302
+ const defMap = /** @type {[Map<Element, any> | WeakMap<Element, any>, any]} */defaultMap;
897
1303
  // Boolean indicating use of default map and object
898
1304
  if (dataVal === true) {
899
- [map, obj] = defaultMap;
1305
+ [map, obj] = defMap;
900
1306
  } else if (Array.isArray(dataVal)) {
901
1307
  // Array of strings mapping to default
902
1308
  if (typeof dataVal[0] === 'string') {
903
1309
  dataVal.forEach(dVal => {
904
- setMap(opts.$map[dVal]);
1310
+ setMap( /** @type {MapWithRoot} */opts.$map[dVal]);
905
1311
  });
906
1312
  return;
907
1313
  // Array of Map and non-map data object
908
1314
  }
909
1315
 
910
- map = dataVal[0] || defaultMap[0];
911
- obj = dataVal[1] || defaultMap[1];
1316
+ map = dataVal[0] || defMap[0];
1317
+ obj = dataVal[1] || defMap[1];
912
1318
  // Map
913
1319
  } else if (/^\[object (?:Weak)?Map\]$/u.test([].toString.call(dataVal))) {
914
1320
  map = dataVal;
915
- obj = defaultMap[1];
1321
+ obj = defMap[1];
916
1322
  // Non-map data object
917
1323
  } else {
918
- map = defaultMap[0];
1324
+ map = defMap[0];
919
1325
  obj = dataVal;
920
1326
  }
921
- map.set(elem, obj);
1327
+ /** @type {Map<Element, any> | WeakMap<Element, any>} */
1328
+ map.set( /** @type {Element} */
1329
+ elem, obj);
922
1330
  };
923
- for (let i = 0; i < argc; i++) {
1331
+ for (let i = argStart; i < argc; i++) {
924
1332
  let arg = args[i];
925
1333
  const type = _getType(arg);
926
1334
  switch (type) {
927
1335
  case 'null':
928
1336
  // null always indicates a place-holder (only needed for last argument if want array returned)
929
1337
  if (i === argc - 1) {
930
- _applyAnyStylesheet(nodes[0]); // We have to execute any stylesheets even if not appending or otherwise IE will never apply them
931
- // Todo: Fix to allow application of stylesheets of style tags within fragments?
932
1338
  return nodes.length <= 1 ? nodes[0]
933
1339
  // eslint-disable-next-line unicorn/no-array-callback-reference
934
1340
  : nodes.reduce(_fragReducer, doc.createDocumentFragment()); // nodes;
@@ -939,21 +1345,21 @@ const jml = function jml(...args) {
939
1345
  // Strings normally indicate elements
940
1346
  switch (arg) {
941
1347
  case '!':
942
- nodes[nodes.length] = doc.createComment(args[++i]);
1348
+ nodes[nodes.length] = doc.createComment( /** @type {string} */args[++i]);
943
1349
  break;
944
1350
  case '?':
945
1351
  {
946
- arg = args[++i];
947
- let procValue = args[++i];
1352
+ arg = /** @type {string} */args[++i];
1353
+ let procValue = /** @type {string} */args[++i];
948
1354
  const val = procValue;
949
1355
  if (val && typeof val === 'object') {
950
- procValue = [];
1356
+ const procValues = [];
951
1357
  for (const [p, procInstVal] of Object.entries(val)) {
952
- procValue.push(p + '=' + '"' +
1358
+ procValues.push(p + '=' + '"' +
953
1359
  // https://www.w3.org/TR/xml-stylesheet/#NT-PseudoAttValue
954
1360
  procInstVal.replace(/"/gu, '&quot;') + '"');
955
1361
  }
956
- procValue = procValue.join(' ');
1362
+ procValue = procValues.join(' ');
957
1363
  }
958
1364
  // Firefox allows instructions with ">" in this method, but not if placed directly!
959
1365
  try {
@@ -971,7 +1377,8 @@ const jml = function jml(...args) {
971
1377
  // Browsers don't support doc.createEntityReference, so we just use this as a convenience
972
1378
  }
973
1379
  case '&':
974
- nodes[nodes.length] = _createSafeReference('entity', '', args[++i]);
1380
+ nodes[nodes.length] = _createSafeReference('entity', '', /** @type {string} */
1381
+ args[++i]);
975
1382
  break;
976
1383
  case '#':
977
1384
  // // Decimal character reference - ['#', '01234'] // &#01234; // probably easier to use JavaScript Unicode escapes
@@ -979,16 +1386,18 @@ const jml = function jml(...args) {
979
1386
  break;
980
1387
  case '#x':
981
1388
  // Hex character reference - ['#x', '123a'] // &#x123a; // probably easier to use JavaScript Unicode escapes
982
- nodes[nodes.length] = _createSafeReference('hexadecimal', arg, args[++i]);
1389
+ nodes[nodes.length] = _createSafeReference('hexadecimal', arg, /** @type {string} */
1390
+ args[++i]);
983
1391
  break;
984
1392
  case '![':
985
1393
  // '![', ['escaped <&> text'] // <![CDATA[escaped <&> text]]>
986
1394
  // CDATA valid in XML only, so we'll just treat as text for mutual compatibility
987
1395
  // Todo: config (or detection via some kind of doc.documentType property?) of whether in XML
988
1396
  try {
989
- nodes[nodes.length] = doc.createCDATASection(args[++i]);
1397
+ nodes[nodes.length] = doc.createCDATASection( /** @type {string} */args[++i]);
990
1398
  } catch (e2) {
991
- nodes[nodes.length] = doc.createTextNode(args[i]); // i already incremented
1399
+ nodes[nodes.length] = doc.createTextNode( /** @type {string} */
1400
+ args[i]); // i already incremented
992
1401
  }
993
1402
 
994
1403
  break;
@@ -1000,19 +1409,21 @@ const jml = function jml(...args) {
1000
1409
  default:
1001
1410
  {
1002
1411
  // An element
1003
- elStr = arg;
1412
+ elStr = /** @type {string} */arg;
1004
1413
  const atts = args[i + 1];
1005
- if (_getType(atts) === 'object' && atts.is) {
1414
+ if (atts && _getType(atts) === 'object' && /** @type {JamilihAttributes} */atts.is) {
1006
1415
  const {
1007
1416
  is
1008
- } = atts;
1009
- // istanbul ignore next
1010
- elem = doc.createElementNS ? doc.createElementNS(NS_HTML, elStr, {
1011
- is
1417
+ } = /** @type {JamilihAttributes} */atts;
1418
+ /* c8 ignore next */
1419
+ elem = doc.createElementNS
1420
+ /* eslint-disable object-shorthand -- Casting */ ? doc.createElementNS(NS_HTML, elStr, {
1421
+ is: /** @type {string} */is
1012
1422
  }) : doc.createElement(elStr, {
1013
- is
1423
+ is: /** @type {string} */is
1014
1424
  });
1015
- } else /* istanbul ignore else */if (doc.createElementNS) {
1425
+ /* eslint-enable object-shorthand -- Casting */
1426
+ } else /* c8 ignore next */if (doc.createElementNS) {
1016
1427
  elem = doc.createElementNS(NS_HTML, elStr);
1017
1428
  } else {
1018
1429
  elem = doc.createElement(elStr);
@@ -1027,24 +1438,33 @@ const jml = function jml(...args) {
1027
1438
  case 'object':
1028
1439
  {
1029
1440
  // Non-DOM-element objects indicate attribute-value pairs
1441
+ if (!arg || typeof arg !== 'object') {
1442
+ throw new Error('Null should not reach here');
1443
+ }
1030
1444
  const atts = arg;
1031
- if (atts.xmlns !== undefined) {
1445
+ if ('xmlns' in atts) {
1032
1446
  // We handle this here, as otherwise may lose events, etc.
1033
1447
  // As namespace of element already set as XHTML, we need to change the namespace
1034
1448
  // elem.setAttribute('xmlns', atts.xmlns); // Doesn't work
1035
1449
  // Can't set namespaceURI dynamically, renameNode() is not supported, and setAttribute() doesn't work to change the namespace, so we resort to this hack
1036
- const replacer = typeof atts.xmlns === 'object' ? _replaceDefiner(atts.xmlns) : ' xmlns="' + atts.xmlns + '"';
1450
+ const xmlnsObj = /** @type {XmlnsAttributeObject} */atts;
1451
+ const replacer = xmlnsObj.xmlns && typeof xmlnsObj.xmlns === 'object' ? _replaceDefiner(xmlnsObj.xmlns) : ' xmlns="' + xmlnsObj.xmlns + '"';
1037
1452
  // try {
1038
1453
  // Also fix DOMParser to work with text/html
1039
- elem = nodes[nodes.length - 1] = new win.DOMParser().parseFromString(new win.XMLSerializer().serializeToString(elem)
1454
+ elem = nodes[nodes.length - 1] =
1455
+ // Why doesn't `HTMLWindow` have `DOMParser`?
1456
+ new /** @type {import('jsdom').DOMWindow} */win.DOMParser().parseFromString(new /** @type {import('jsdom').DOMWindow} */win.XMLSerializer().serializeToString(elem)
1040
1457
  // Mozilla adds XHTML namespace
1041
- .replace(' xmlns="' + NS_HTML + '"', replacer), 'application/xml').documentElement;
1458
+ .replace(' xmlns="' + NS_HTML + '"',
1459
+ // Needed to cast here, despite either overload working
1460
+ /** @type {string} */
1461
+ replacer), 'application/xml').documentElement;
1042
1462
  // Todo: Report to plugins
1043
1463
  opts.$state = 'element';
1044
1464
  // }catch(e) {alert(elem.outerHTML);throw e;}
1045
1465
  }
1046
1466
 
1047
- _checkAtts(atts);
1467
+ _checkAtts( /** @type {JamilihAttributes} */atts);
1048
1468
  break;
1049
1469
  }
1050
1470
  case 'document':
@@ -1056,26 +1476,24 @@ const jml = function jml(...args) {
1056
1476
  */
1057
1477
  if (i === 0) {
1058
1478
  // Allow wrapping of element, fragment, or document
1059
- elem = arg;
1060
- // Todo: Report to plugins
1479
+ elem = /** @type {Document|DocumentFragment|Element} */arg;
1480
+ // Todo: Report to plugins and change for document/fragment
1061
1481
  opts.$state = 'element';
1062
1482
  }
1063
1483
  if (i === argc - 1 || i === argc - 2 && args[i + 1] === null) {
1064
1484
  // parent
1065
1485
  const elsl = nodes.length;
1066
1486
  for (let k = 0; k < elsl; k++) {
1067
- _appendNode(arg, nodes[k]);
1487
+ _appendNode( /** @type {Document|DocumentFragment|Element} */arg, nodes[k]);
1068
1488
  }
1069
- // Todo: Apply stylesheets if any style tags were added elsewhere besides the first element?
1070
- _applyAnyStylesheet(nodes[0]); // We have to execute any stylesheets even if not appending or otherwise IE will never apply them
1071
1489
  } else {
1072
- nodes[nodes.length] = arg;
1490
+ nodes[nodes.length] = /** @type {Document|DocumentFragment|Element} */arg;
1073
1491
  }
1074
1492
  break;
1075
1493
  case 'array':
1076
1494
  {
1077
1495
  // Arrays or arrays of arrays indicate child nodes
1078
- const child = arg;
1496
+ const child = /** @type {JamilihChildren} */arg;
1079
1497
  const cl = child.length;
1080
1498
  for (let j = 0; j < cl; j++) {
1081
1499
  // Go through children array container to handle elements
@@ -1089,21 +1507,30 @@ const jml = function jml(...args) {
1089
1507
  case 'string':
1090
1508
  case 'number':
1091
1509
  case 'boolean':
1092
- _appendNode(elem, doc.createTextNode(childContent));
1510
+ _appendNode(elem, doc.createTextNode(String(childContent)));
1093
1511
  break;
1094
1512
  default:
1513
+ if (!childContent || typeof childContent !== 'object') {
1514
+ throw new Error('Unexpected type');
1515
+ }
1095
1516
  if (Array.isArray(childContent)) {
1096
1517
  // Arrays representing child elements
1097
1518
  opts.$state = 'children';
1519
+ // @ts-expect-error Should be ok
1098
1520
  _appendNode(elem, jml(opts, ...childContent));
1099
- } else if (childContent['#']) {
1521
+ } else if ('#' in childContent) {
1100
1522
  // Fragment
1101
1523
  opts.$state = 'fragmentChildren';
1102
1524
  _appendNode(elem, jml(opts, childContent['#']));
1103
1525
  } else {
1104
- // Single DOM element children
1105
- const newChildContent = checkPluginValue(elem, null, childContent, opts);
1106
- _appendNode(elem, newChildContent);
1526
+ // Single DOM element children or plugin
1527
+ let newChildContent;
1528
+ if (!('nodeType' in childContent)) {
1529
+ newChildContent = /** @type {string} */
1530
+ checkPluginValue(elem, null, childContent, opts, 'children');
1531
+ }
1532
+ _appendNode(elem, /** @type {string | Element | DocumentFragment | Comment} */
1533
+ newChildContent || childContent);
1107
1534
  }
1108
1535
  break;
1109
1536
  }
@@ -1115,34 +1542,116 @@ const jml = function jml(...args) {
1115
1542
  }
1116
1543
  }
1117
1544
  const ret = nodes[0] || elem;
1118
- if (isRoot && opts.$map && opts.$map.root) {
1545
+ if (isRoot && opts.$map && /** @type {MapWithRoot} */opts.$map.root) {
1119
1546
  setMap(true);
1120
1547
  }
1121
1548
  return ret;
1122
1549
  };
1123
1550
 
1551
+ /**
1552
+ * Configuration object.
1553
+ * @typedef {object} ToJmlConfig
1554
+ * @property {boolean} [stringOutput=false] Whether to output the Jamilih object as a string.
1555
+ * @property {boolean} [reportInvalidState=true] If true (the default), will report invalid state errors
1556
+ * @property {boolean} [stripWhitespace=false] Strip whitespace for text nodes
1557
+ */
1558
+
1559
+ /**
1560
+ * @typedef {[namespace: string|null, name: string, value?: string]} JamilihAttributeNodeValue
1561
+ */
1562
+
1563
+ /**
1564
+ * @typedef {{
1565
+ * $attribute: JamilihAttributeNodeValue
1566
+ * }} JamilihAttributeNode
1567
+ */
1568
+
1569
+ /**
1570
+ * @typedef {{
1571
+ * $text: string
1572
+ * }} JamilihTextNode
1573
+ */
1574
+
1575
+ /**
1576
+ * @typedef {['![', string]} JamilihCDATANode
1577
+ */
1578
+
1579
+ /**
1580
+ * @typedef {['&', string]} JamilihEntityReference
1581
+ */
1582
+
1583
+ /**
1584
+ * @typedef {[code: '?', target: string, value: string]} JamilihProcessingInstruction
1585
+ */
1586
+
1587
+ /**
1588
+ * @typedef {[code: '!', value: string]} JamilihComment
1589
+ */
1590
+
1591
+ /**
1592
+ * @typedef {{
1593
+ * nodeType: number,
1594
+ * nodeName: string
1595
+ * }} Entity
1596
+ */
1597
+
1598
+ /* eslint-disable no-shadow, unicorn/custom-error-definition */
1599
+ /**
1600
+ * Polyfill for `DOMException`.
1601
+ */
1602
+ class DOMException extends Error {
1603
+ /* eslint-enable no-shadow, unicorn/custom-error-definition */
1604
+ /**
1605
+ * @param {string} message
1606
+ * @param {string} name
1607
+ */
1608
+ constructor(message, name) {
1609
+ super(message);
1610
+ this.code = 0;
1611
+ // eslint-disable-next-line unicorn/custom-error-definition
1612
+ this.name = name;
1613
+ }
1614
+ }
1615
+
1616
+ /**
1617
+ * @typedef {JamilihArray|JamilihDoctype|
1618
+ * JamilihCDATANode|JamilihEntityReference|JamilihProcessingInstruction|
1619
+ * JamilihComment|JamilihDocumentFragment} JamilihChildType
1620
+ */
1621
+
1622
+ /**
1623
+ * @typedef {JamilihDoc|JamilihAttributeNode|JamilihChildType} JamilihType
1624
+ */
1625
+
1124
1626
  /**
1125
1627
  * Converts a DOM object or a string of HTML into a Jamilih object (or string).
1126
- * @param {string|HTMLElement} dom If a string, will parse as document
1127
- * @param {PlainObject} [config] Configuration object
1128
- * @param {boolean} [config.stringOutput=false] Whether to output the Jamilih object as a string.
1129
- * @param {boolean} [config.reportInvalidState=true] If true (the default), will report invalid state errors
1130
- * @param {boolean} [config.stripWhitespace=false] Strip whitespace for text nodes
1628
+ * @param {string|HTMLElement|Node|Entity} nde If a string, will parse as document
1629
+ * @param {ToJmlConfig} [config] Configuration object
1131
1630
  * @throws {TypeError}
1132
- * @returns {JamilihArray|string} Array containing the elements which represent
1631
+ * @returns {JamilihType|string} Array containing the elements which represent
1133
1632
  * a Jamilih object, or, if `stringOutput` is true, it will be the stringified
1134
1633
  * version of such an object
1135
1634
  */
1136
- jml.toJML = function (dom, {
1635
+ jml.toJML = function (nde, {
1137
1636
  stringOutput = false,
1138
1637
  reportInvalidState = true,
1139
1638
  stripWhitespace = false
1140
1639
  } = {}) {
1141
- if (typeof dom === 'string') {
1142
- dom = new win.DOMParser().parseFromString(dom, 'text/html'); // todo: Give option for XML once implemented and change JSDoc to allow for Element
1640
+ if (!win) {
1641
+ throw new Error('No window object set');
1642
+ }
1643
+ if (typeof nde === 'string') {
1644
+ nde = new /** @type {import('jsdom').DOMWindow} */win.DOMParser().parseFromString(nde, 'text/html'); // todo: Give option for XML once implemented and change JSDoc to allow for Element
1143
1645
  }
1144
1646
 
1145
- const ret = [];
1647
+ const dom = /** @type {HTMLElement|Node|Entity} */nde;
1648
+
1649
+ /**
1650
+ * @todo Find more specific type than `any`
1651
+ * @typedef {{[key: (number|string)]: any}} IndexableObject
1652
+ */
1653
+
1654
+ const ret = /** @type {IndexableObject} */[];
1146
1655
  let parent = ret;
1147
1656
  let parentIdx = 0;
1148
1657
 
@@ -1153,22 +1662,6 @@ jml.toJML = function (dom, {
1153
1662
  */
1154
1663
  function invalidStateError(msg) {
1155
1664
  // These are probably only necessary if working with text/html
1156
- /* eslint-disable no-shadow, unicorn/custom-error-definition */
1157
- /**
1158
- * Polyfill for `DOMException`.
1159
- */
1160
- class DOMException extends Error {
1161
- /* eslint-enable no-shadow, unicorn/custom-error-definition */
1162
- /**
1163
- * @param {string} message
1164
- * @param {string} name
1165
- */
1166
- constructor(message, name) {
1167
- super(message);
1168
- // eslint-disable-next-line unicorn/custom-error-definition
1169
- this.name = name;
1170
- }
1171
- }
1172
1665
  if (reportInvalidState) {
1173
1666
  // INVALID_STATE_ERR per section 9.3 XHTML 5: http://www.w3.org/TR/html5/the-xhtml-syntax.html
1174
1667
  const e = new DOMException(msg, 'INVALID_STATE_ERR');
@@ -1179,8 +1672,8 @@ jml.toJML = function (dom, {
1179
1672
 
1180
1673
  /**
1181
1674
  *
1182
- * @param {DocumentType|Entity} obj
1183
- * @param {Node} node
1675
+ * @param {JamilihDocumentType} obj
1676
+ * @param {DocumentType} node
1184
1677
  * @returns {void}
1185
1678
  */
1186
1679
  function addExternalID(obj, node) {
@@ -1199,10 +1692,6 @@ jml.toJML = function (dom, {
1199
1692
  }
1200
1693
  }
1201
1694
 
1202
- /**
1203
- * @typedef {any} ArbitraryValue
1204
- */
1205
-
1206
1695
  /**
1207
1696
  *
1208
1697
  * @param {ArbitraryValue} val
@@ -1225,7 +1714,7 @@ jml.toJML = function (dom, {
1225
1714
  /**
1226
1715
  *
1227
1716
  * @param {string} prop1
1228
- * @param {string} prop2
1717
+ * @param {string} [prop2]
1229
1718
  * @returns {void}
1230
1719
  */
1231
1720
  function setObj(prop1, prop2) {
@@ -1238,29 +1727,47 @@ jml.toJML = function (dom, {
1238
1727
 
1239
1728
  /**
1240
1729
  *
1241
- * @param {Node} node
1242
- * @param {Object<{string: string}>} namespaces
1730
+ * @param {Node|Entity} nodeOrEntity
1731
+ * @param {Object<string, string|null>} namespaces
1243
1732
  * @throws {TypeError}
1244
1733
  * @returns {void}
1245
1734
  */
1246
- function parseDOM(node, namespaces) {
1735
+ function parseDOM(nodeOrEntity, namespaces) {
1247
1736
  // namespaces = clone(namespaces) || {}; // Ensure we're working with a copy, so different levels in the hierarchy can treat it differently
1248
1737
 
1249
1738
  /*
1250
- if ((node.prefix && node.prefix.includes(':')) || (node.localName && node.localName.includes(':'))) {
1739
+ if ((nodeOrEntity.prefix && nodeOrEntity.prefix.includes(':')) || (nodeOrEntity.localName && nodeOrEntity.localName.includes(':'))) {
1251
1740
  invalidStateError('Prefix cannot have a colon');
1252
1741
  }
1253
1742
  */
1254
1743
 
1255
- const type = 'nodeType' in node ? node.nodeType : null;
1744
+ const type = 'nodeType' in nodeOrEntity ? nodeOrEntity.nodeType : null;
1745
+ if (!type) {
1746
+ throw new TypeError('Not an XML type');
1747
+ }
1748
+ if (type === 5) {
1749
+ // ENTITY REFERENCE (though not in browsers (was already resolved
1750
+ // anyways), ok to keep for parity with our "entity" shorthand)
1751
+ set(['&', nodeOrEntity.nodeName]);
1752
+ return;
1753
+ }
1256
1754
  namespaces = {
1257
1755
  ...namespaces
1258
1756
  };
1259
1757
  const xmlChars = /^([\u0009\u000A\u000D\u0020-\uD7FF\uE000-\uFFFD]|[\uD800-\uDBFF][\uDC00-\uDFFF])*$/u; // eslint-disable-line no-control-regex
1260
- if ([2, 3, 4, 7, 8].includes(type) && !xmlChars.test(node.nodeValue)) {
1758
+ if ([2, 3, 4, 7, 8].includes(type) && /** @type {Node} */nodeOrEntity.nodeValue && !xmlChars.test( /** @type {Node} */nodeOrEntity.nodeValue)) {
1261
1759
  invalidStateError('Node has bad XML character value');
1262
1760
  }
1263
- let tmpParent, tmpParentIdx;
1761
+
1762
+ /**
1763
+ * @type {IndexableObject}
1764
+ */
1765
+ let tmpParent;
1766
+
1767
+ /**
1768
+ * @type {Integer}
1769
+ */
1770
+ let tmpParentIdx;
1264
1771
 
1265
1772
  /**
1266
1773
  * @returns {void}
@@ -1282,11 +1789,16 @@ jml.toJML = function (dom, {
1282
1789
  case 1:
1283
1790
  {
1284
1791
  // ELEMENT
1792
+ const node = /** @type {Element} */nodeOrEntity;
1285
1793
  setTemp();
1286
1794
  const nodeName = node.nodeName.toLowerCase(); // Todo: for XML, should not lower-case
1287
1795
 
1288
1796
  setChildren(); // Build child array since elements are, except at the top level, encapsulated in arrays
1289
1797
  set(nodeName);
1798
+
1799
+ /**
1800
+ * @type {{[key: string]: string|null} & {xmlns?: string|null}}
1801
+ */
1290
1802
  const start = {};
1291
1803
  let hasNamespaceDeclaration = false;
1292
1804
  if (namespaces[node.prefix || ''] !== node.namespaceURI) {
@@ -1322,57 +1834,79 @@ jml.toJML = function (dom, {
1322
1834
  }
1323
1835
  case undefined: // Treat as attribute node until this is fixed: https://github.com/jsdom/jsdom/issues/1641 / https://github.com/jsdom/jsdom/pull/1822
1324
1836
  case 2:
1325
- // ATTRIBUTE (should only get here if passing in an attribute node)
1326
- set({
1327
- $attribute: [node.namespaceURI, node.name, node.value]
1328
- });
1329
- break;
1837
+ {
1838
+ // ATTRIBUTE (should only get here if passing in an attribute node)
1839
+ const node = /** @type {Attr} */nodeOrEntity;
1840
+ set({
1841
+ $attribute: [node.namespaceURI, node.name, node.value]
1842
+ });
1843
+ break;
1844
+ }
1330
1845
  case 3:
1331
- // TEXT
1332
- if (stripWhitespace && /^\s+$/u.test(node.nodeValue)) {
1333
- set('');
1334
- return;
1846
+ {
1847
+ // TEXT
1848
+ const node = /** @type {Text} */nodeOrEntity;
1849
+ /* c8 ignore next 3 */
1850
+ if (!node.nodeValue) {
1851
+ throw new Error('Unexpected null comment value');
1852
+ }
1853
+ if (stripWhitespace && /^\s+$/u.test(node.nodeValue)) {
1854
+ set('');
1855
+ return;
1856
+ }
1857
+ set(node.nodeValue);
1858
+ break;
1335
1859
  }
1336
- set(node.nodeValue);
1337
- break;
1338
1860
  case 4:
1339
- // CDATA
1340
- if (node.nodeValue.includes(']]' + '>')) {
1341
- invalidStateError('CDATA cannot end with closing ]]>');
1861
+ {
1862
+ var _node$nodeValue;
1863
+ // CDATA
1864
+ const node = /** @type {CDATASection} */nodeOrEntity;
1865
+ if ((_node$nodeValue = node.nodeValue) !== null && _node$nodeValue !== void 0 && _node$nodeValue.includes(']]' + '>')) {
1866
+ invalidStateError('CDATA cannot end with closing ]]>');
1867
+ }
1868
+ set(['![', node.nodeValue]);
1869
+ break;
1342
1870
  }
1343
- set(['![', node.nodeValue]);
1344
- break;
1345
- case 5:
1346
- // ENTITY REFERENCE (though not in browsers (was already resolved
1347
- // anyways), ok to keep for parity with our "entity" shorthand)
1348
- set(['&', node.nodeName]);
1349
- break;
1871
+ // case 5:
1872
+ // Handled earlier
1350
1873
  case 7:
1351
- // PROCESSING INSTRUCTION
1352
- if (/^xml$/iu.test(node.target)) {
1353
- invalidStateError('Processing instructions cannot be "xml".');
1354
- }
1355
- if (node.target.includes('?>')) {
1356
- invalidStateError('Processing instruction targets cannot include ?>');
1357
- }
1358
- if (node.target.includes(':')) {
1359
- invalidStateError('The processing instruction target cannot include ":"');
1360
- }
1361
- if (node.data.includes('?>')) {
1362
- invalidStateError('Processing instruction data cannot include ?>');
1874
+ {
1875
+ // PROCESSING INSTRUCTION
1876
+ const node = /** @type {ProcessingInstruction} */nodeOrEntity;
1877
+ if (/^xml$/iu.test(node.target)) {
1878
+ invalidStateError('Processing instructions cannot be "xml".');
1879
+ }
1880
+ if (node.target.includes('?>')) {
1881
+ invalidStateError('Processing instruction targets cannot include ?>');
1882
+ }
1883
+ if (node.target.includes(':')) {
1884
+ invalidStateError('The processing instruction target cannot include ":"');
1885
+ }
1886
+ if (node.data.includes('?>')) {
1887
+ invalidStateError('Processing instruction data cannot include ?>');
1888
+ }
1889
+ set(['?', node.target, node.data]); // Todo: Could give option to attempt to convert value back into object if has pseudo-attributes
1890
+ break;
1363
1891
  }
1364
- set(['?', node.target, node.data]); // Todo: Could give option to attempt to convert value back into object if has pseudo-attributes
1365
- break;
1366
1892
  case 8:
1367
- // COMMENT
1368
- if (node.nodeValue.includes('--') || node.nodeValue.length && node.nodeValue.lastIndexOf('-') === node.nodeValue.length - 1) {
1369
- invalidStateError('Comments cannot include --');
1893
+ {
1894
+ // COMMENT
1895
+ const node = /** @type {Comment} */nodeOrEntity;
1896
+ /* c8 ignore next 3 */
1897
+ if (!node.nodeValue) {
1898
+ throw new Error('Unexpected null comment value');
1899
+ }
1900
+ if (node.nodeValue.includes('--') || node.nodeValue.length && node.nodeValue.lastIndexOf('-') === node.nodeValue.length - 1) {
1901
+ invalidStateError('Comments cannot include --');
1902
+ }
1903
+ set(['!', node.nodeValue]);
1904
+ break;
1370
1905
  }
1371
- set(['!', node.nodeValue]);
1372
- break;
1373
1906
  case 9:
1374
1907
  {
1375
1908
  // DOCUMENT
1909
+ const node = /** @type {Document} */nodeOrEntity;
1376
1910
  setTemp();
1377
1911
  const docObj = {
1378
1912
  $document: {
@@ -1402,16 +1936,17 @@ jml.toJML = function (dom, {
1402
1936
  case 10:
1403
1937
  {
1404
1938
  // DOCUMENT TYPE
1939
+ const node = /** @type {DocumentType} */nodeOrEntity;
1405
1940
  setTemp();
1406
1941
 
1407
1942
  // Can create directly by doc.implementation.createDocumentType
1408
1943
  const start = {
1409
1944
  $DOCTYPE: {
1410
- name: node.name
1945
+ name: /** @type {DocumentType} */node.name
1411
1946
  }
1412
1947
  };
1413
1948
  const pubIdChar = /^(\u0020|\u000D|\u000A|[a-zA-Z0-9]|[-'()+,./:=?;!*#@$_%])*$/u; // eslint-disable-line no-control-regex
1414
- if (!pubIdChar.test(node.publicId)) {
1949
+ if (!pubIdChar.test( /** @type {DocumentType} */node.publicId)) {
1415
1950
  invalidStateError('A publicId must have valid characters.');
1416
1951
  }
1417
1952
  addExternalID(start.$DOCTYPE, node);
@@ -1424,6 +1959,7 @@ jml.toJML = function (dom, {
1424
1959
  case 11:
1425
1960
  {
1426
1961
  // DOCUMENT FRAGMENT
1962
+ const node = /** @type {DocumentFragment} */nodeOrEntity;
1427
1963
  setTemp();
1428
1964
  set({
1429
1965
  '#': []
@@ -1451,15 +1987,23 @@ jml.toJML = function (dom, {
1451
1987
  }
1452
1988
  return ret[0];
1453
1989
  };
1990
+
1991
+ /**
1992
+ * @param {string|HTMLElement} dom
1993
+ * @param {ToJmlConfig} [config]
1994
+ * @returns {string}
1995
+ */
1454
1996
  jml.toJMLString = function (dom, config) {
1455
- return jml.toJML(dom, Object.assign(config || {}, {
1456
- stringOutput: true
1457
- }));
1997
+ return (/** @type {string} */
1998
+ jml.toJML(dom, Object.assign(config || {}, {
1999
+ stringOutput: true
2000
+ }))
2001
+ );
1458
2002
  };
1459
2003
 
1460
2004
  /**
1461
2005
  *
1462
- * @param {...JamilihArray} args
2006
+ * @param {JamilihArray} args
1463
2007
  * @returns {JamilihReturn}
1464
2008
  */
1465
2009
  jml.toDOM = function (...args) {
@@ -1469,21 +2013,75 @@ jml.toDOM = function (...args) {
1469
2013
 
1470
2014
  /**
1471
2015
  *
1472
- * @param {...JamilihArray} args
2016
+ * @param {JamilihArray} args
1473
2017
  * @returns {string}
1474
2018
  */
1475
2019
  jml.toHTML = function (...args) {
1476
2020
  // Todo: Replace this with version of jml() that directly builds a string
1477
2021
  const ret = jml(...args);
1478
- // Todo: deal with serialization of properties like 'selected',
1479
- // 'checked', 'value', 'defaultValue', 'for', 'dataset', 'on*',
1480
- // 'style'! (i.e., need to build a string ourselves)
1481
- return ret.outerHTML;
2022
+ switch (ret.nodeType) {
2023
+ case 1:
2024
+ {
2025
+ // Element
2026
+ // Todo: deal with serialization of properties like 'selected',
2027
+ // 'checked', 'value', 'defaultValue', 'for', 'dataset', 'on*',
2028
+ // 'style'! (i.e., need to build a string ourselves)
2029
+ return (/** @type {Element} */ret.outerHTML
2030
+ );
2031
+ }
2032
+ case 2:
2033
+ {
2034
+ // ATTR
2035
+ return `${
2036
+ /** @type {Attr} */ret.name}="${
2037
+ /** @type {Attr} */ret.value.replace(/"/gu, '&quot;')}"`;
2038
+ }
2039
+ case 3:
2040
+ case 4:
2041
+ {
2042
+ // CDATA
2043
+ if (!ret.nodeValue) {
2044
+ throw new TypeError('Unexpected null Text node');
2045
+ }
2046
+ return (/** @type {Text|CDATASection} */ret.nodeValue
2047
+ );
2048
+ // case 5: // Entity
2049
+ }
2050
+ case 7:
2051
+ {
2052
+ // PROCESSING INSTRUCTION
2053
+ const node = /** @type {ProcessingInstruction} */ret;
2054
+ return `<?${node.target} ${node.data}?>`;
2055
+ }
2056
+ case 8:
2057
+ {
2058
+ // Comment
2059
+ return `<!--${ret.nodeValue}-->`;
2060
+ }
2061
+ case 9:
2062
+ {
2063
+ // Document
2064
+ throw new Error('Document serialization not yet implemented');
2065
+ }
2066
+ case 10:
2067
+ {
2068
+ // DOCUMENT TYPE
2069
+ const node = /** @type {DocumentType} */ret;
2070
+ return `<!DOCTYPE ${node.name}${node.publicId ? `PUBLIC "${node.publicId}" "${node.systemId}"` : node.systemId ? `SYSTEM "${node.systemId}"` : ``}>`;
2071
+ }
2072
+ case 11:
2073
+ {
2074
+ // DOCUMENT FRAGMENT
2075
+ throw new Error('Document fragment serialization not yet implemented');
2076
+ }
2077
+ default:
2078
+ throw new Error('Unexpected node type');
2079
+ }
1482
2080
  };
1483
2081
 
1484
2082
  /**
1485
2083
  *
1486
- * @param {...JamilihArray} args
2084
+ * @param {JamilihArray} args
1487
2085
  * @returns {string}
1488
2086
  */
1489
2087
  jml.toDOMString = function (...args) {
@@ -1493,17 +2091,20 @@ jml.toDOMString = function (...args) {
1493
2091
 
1494
2092
  /**
1495
2093
  *
1496
- * @param {...JamilihArray} args
2094
+ * @param {JamilihArray} args
1497
2095
  * @returns {string}
1498
2096
  */
1499
2097
  jml.toXML = function (...args) {
2098
+ if (!win) {
2099
+ throw new Error('No window object set');
2100
+ }
1500
2101
  const ret = jml(...args);
1501
- return new win.XMLSerializer().serializeToString(ret);
2102
+ return new /** @type {import('jsdom').DOMWindow} */win.XMLSerializer().serializeToString(ret);
1502
2103
  };
1503
2104
 
1504
2105
  /**
1505
2106
  *
1506
- * @param {...JamilihArray} args
2107
+ * @param {JamilihArray} args
1507
2108
  * @returns {string}
1508
2109
  */
1509
2110
  jml.toXMLDOMString = function (...args) {
@@ -1516,63 +2117,73 @@ jml.toXMLDOMString = function (...args) {
1516
2117
  */
1517
2118
  class JamilihMap extends Map {
1518
2119
  /**
1519
- * @param {string|Element} elem
2120
+ * @param {?(string|Element)} element
1520
2121
  * @returns {ArbitraryValue}
1521
2122
  */
1522
- get(elem) {
1523
- elem = typeof elem === 'string' ? $(elem) : elem;
2123
+ get(element) {
2124
+ const elem = typeof element === 'string' ? $(element) : element;
1524
2125
  return super.get.call(this, elem);
1525
2126
  }
1526
2127
  /**
1527
- * @param {string|Element} elem
2128
+ * @param {string|Element} element
1528
2129
  * @param {ArbitraryValue} value
1529
2130
  * @returns {ArbitraryValue}
1530
2131
  */
1531
- set(elem, value) {
1532
- elem = typeof elem === 'string' ? $(elem) : elem;
2132
+ set(element, value) {
2133
+ const elem = typeof element === 'string' ? $(element) : element;
1533
2134
  return super.set.call(this, elem, value);
1534
2135
  }
1535
2136
  /**
1536
- * @param {string|Element} elem
2137
+ * @param {string|Element} element
1537
2138
  * @param {string} methodName
1538
2139
  * @param {...ArbitraryValue} args
1539
2140
  * @returns {ArbitraryValue}
1540
2141
  */
1541
- invoke(elem, methodName, ...args) {
1542
- elem = typeof elem === 'string' ? $(elem) : elem;
2142
+ invoke(element, methodName, ...args) {
2143
+ const elem = typeof element === 'string' ? $(element) : element;
1543
2144
  return this.get(elem)[methodName](elem, ...args);
1544
2145
  }
1545
2146
  }
1546
2147
 
1547
2148
  /**
1548
2149
  * Element-aware wrapper for `WeakMap`.
2150
+ * @extends {WeakMap<any>}
1549
2151
  */
1550
2152
  class JamilihWeakMap extends WeakMap {
1551
2153
  /**
1552
- * @param {string|Element} elem
2154
+ * @param {Element} element
1553
2155
  * @returns {ArbitraryValue}
1554
2156
  */
1555
- get(elem) {
1556
- elem = typeof elem === 'string' ? $(elem) : elem;
2157
+ get(element) {
2158
+ const elem = typeof element === 'string' ? $(element) : element;
2159
+ if (!elem) {
2160
+ throw new Error("Can't find the element");
2161
+ }
1557
2162
  return super.get.call(this, elem);
1558
2163
  }
1559
2164
  /**
1560
- * @param {string|Element} elem
2165
+ * @param {Element} element
1561
2166
  * @param {ArbitraryValue} value
1562
2167
  * @returns {ArbitraryValue}
1563
2168
  */
1564
- set(elem, value) {
1565
- elem = typeof elem === 'string' ? $(elem) : elem;
2169
+ set(element, value) {
2170
+ const elem = typeof element === 'string' ? $(element) : element;
2171
+ if (!elem) {
2172
+ throw new Error("Can't find the element");
2173
+ }
1566
2174
  return super.set.call(this, elem, value);
1567
2175
  }
1568
2176
  /**
1569
- * @param {string|Element} elem
2177
+ * @param {string|Element} element
1570
2178
  * @param {string} methodName
1571
2179
  * @param {...ArbitraryValue} args
1572
2180
  * @returns {ArbitraryValue}
1573
2181
  */
1574
- invoke(elem, methodName, ...args) {
1575
- elem = typeof elem === 'string' ? $(elem) : elem;
2182
+ invoke(element, methodName, ...args) {
2183
+ const elem = typeof element === 'string' ? $(element) : element;
2184
+ if (!elem) {
2185
+ throw new Error("Can't find the element");
2186
+ }
1576
2187
  return this.get(elem)[methodName](elem, ...args);
1577
2188
  }
1578
2189
  }
@@ -1580,14 +2191,12 @@ jml.Map = JamilihMap;
1580
2191
  jml.WeakMap = JamilihWeakMap;
1581
2192
 
1582
2193
  /**
1583
- * @typedef {GenericArray} MapAndElementArray
1584
- * @property {JamilihWeakMap|JamilihMap} 0
1585
- * @property {Element} 1
1586
- */
2194
+ * @typedef {[JamilihWeakMap|JamilihMap, Element]} MapAndElementArray
2195
+ */
1587
2196
 
1588
2197
  /**
1589
- * @param {GenericObject} obj
1590
- * @param {...JamilihArray} args
2198
+ * @param {{[key: string]: any}} obj
2199
+ * @param {JamilihArrayPostOptions} args
1591
2200
  * @returns {MapAndElementArray}
1592
2201
  */
1593
2202
  jml.weak = function (obj, ...args) {
@@ -1595,12 +2204,12 @@ jml.weak = function (obj, ...args) {
1595
2204
  const elem = jml({
1596
2205
  $map: [map, obj]
1597
2206
  }, ...args);
1598
- return [map, elem];
2207
+ return [map, /** @type {Element} */elem];
1599
2208
  };
1600
2209
 
1601
2210
  /**
1602
2211
  * @param {ArbitraryValue} obj
1603
- * @param {...JamilihArray} args
2212
+ * @param {JamilihArrayPostOptions} args
1604
2213
  * @returns {MapAndElementArray}
1605
2214
  */
1606
2215
  jml.strong = function (obj, ...args) {
@@ -1608,22 +2217,28 @@ jml.strong = function (obj, ...args) {
1608
2217
  const elem = jml({
1609
2218
  $map: [map, obj]
1610
2219
  }, ...args);
1611
- return [map, elem];
2220
+ return [map, /** @type {Element} */elem];
1612
2221
  };
1613
2222
 
1614
2223
  /**
1615
- * @param {string|Element} elem If a string, will be interpreted as a selector
2224
+ * @param {string|Element} element If a string, will be interpreted as a selector
1616
2225
  * @param {symbol|string} sym If a string, will be used with `Symbol.for`
1617
2226
  * @returns {ArbitraryValue} The value associated with the symbol
1618
2227
  */
1619
- jml.symbol = jml.sym = jml.for = function (elem, sym) {
1620
- elem = typeof elem === 'string' ? $(elem) : elem;
2228
+ jml.symbol = jml.sym = jml.for = function (element, sym) {
2229
+ const elem = typeof element === 'string' ? $(element) : element;
2230
+
2231
+ // @ts-expect-error Should be ok
1621
2232
  return elem[typeof sym === 'symbol' ? sym : Symbol.for(sym)];
1622
2233
  };
1623
2234
 
1624
2235
  /**
1625
- * @param {string|Element} elem If a string, will be interpreted as a selector
1626
- * @param {symbol|string|Map|WeakMap} symOrMap If a string, will be used with `Symbol.for`
2236
+ * @typedef {((elem: Element, ...args: any[]) => void)|{[key: string]: (elem: Element, ...args: any[]) => void}} MapCommand
2237
+ */
2238
+
2239
+ /**
2240
+ * @param {?(string|Element)} elem If a string, will be interpreted as a selector
2241
+ * @param {symbol|string|Map<Element, MapCommand>|WeakMap<Element, MapCommand>} symOrMap If a string, will be used with `Symbol.for`
1627
2242
  * @param {string|any} methodName Can be `any` if the symbol or map directly
1628
2243
  * points to a function (it is then used as the first argument).
1629
2244
  * @param {ArbitraryValue[]} args
@@ -1631,16 +2246,22 @@ jml.symbol = jml.sym = jml.for = function (elem, sym) {
1631
2246
  */
1632
2247
  jml.command = function (elem, symOrMap, methodName, ...args) {
1633
2248
  elem = typeof elem === 'string' ? $(elem) : elem;
2249
+ if (!elem) {
2250
+ throw new Error('No element found');
2251
+ }
1634
2252
  let func;
1635
2253
  if (['symbol', 'string'].includes(typeof symOrMap)) {
1636
- func = jml.sym(elem, symOrMap);
2254
+ func = jml.sym(elem, /** @type {symbol|string} */symOrMap);
1637
2255
  if (typeof func === 'function') {
1638
2256
  return func(methodName, ...args); // Already has `this` bound to `elem`
1639
2257
  }
1640
2258
 
1641
2259
  return func[methodName](...args);
1642
2260
  }
1643
- func = symOrMap.get(elem);
2261
+ func = /** @type {Map<Element, MapCommand>|WeakMap<Element, MapCommand>} */symOrMap.get(elem);
2262
+ if (!func) {
2263
+ throw new Error('No map found');
2264
+ }
1644
2265
  if (typeof func === 'function') {
1645
2266
  return func.call(elem, methodName, ...args);
1646
2267
  }
@@ -1651,42 +2272,51 @@ jml.command = function (elem, symOrMap, methodName, ...args) {
1651
2272
  /**
1652
2273
  * Expects properties `document`, `XMLSerializer`, and `DOMParser`.
1653
2274
  * Also updates `body` with `document.body`.
1654
- * @param {Window} wind
2275
+ * @param {import('jsdom').DOMWindow|HTMLWindow} wind
1655
2276
  * @returns {void}
1656
2277
  */
1657
2278
  jml.setWindow = wind => {
1658
2279
  win = wind;
1659
2280
  doc = win.document;
1660
2281
  if (doc && doc.body) {
1661
- ({
1662
- body
1663
- } = doc);
2282
+ // eslint-disable-next-line prefer-destructuring -- Needed for typing
2283
+ body = /** @type {HTMLBodyElement} */doc.body;
1664
2284
  }
1665
2285
  };
1666
2286
 
1667
2287
  /**
1668
- * @returns {Window}
2288
+ * @returns {import('jsdom').DOMWindow|HTMLWindow}
1669
2289
  */
1670
2290
  jml.getWindow = () => {
2291
+ if (!win) {
2292
+ throw new Error('No window object set');
2293
+ }
1671
2294
  return win;
1672
2295
  };
1673
2296
 
1674
2297
  /**
1675
2298
  * Does not run Jamilih so can be further processed.
1676
- * @param {JamilihArray} jmlArray
1677
- * @param {string|JamilihArray|Element} glu
1678
- * @returns {Element}
2299
+ * @param {ArbitraryValue[]} array
2300
+ * @param {ArbitraryValue} glu
2301
+ * @returns {ArbitraryValue[]}
1679
2302
  */
1680
- function glue(jmlArray, glu) {
1681
- return [...jmlArray].reduce((arr, item) => {
2303
+ function glue(array, glu) {
2304
+ return [...array].reduce((arr, item) => {
1682
2305
  arr.push(item, glu);
1683
2306
  return arr;
1684
2307
  }, []).slice(0, -1);
1685
2308
  }
1686
2309
 
1687
- // istanbul ignore next
1688
- let body = doc && doc.body; // eslint-disable-line import/no-mutable-exports
2310
+ /**
2311
+ * @type {HTMLBodyElement}
2312
+ */
2313
+ let body; // eslint-disable-line import/no-mutable-exports
1689
2314
 
2315
+ /* c8 ignore next 3 */
2316
+ if (doc && doc.body) {
2317
+ // eslint-disable-next-line prefer-destructuring -- Needed for type
2318
+ body = /** @type {HTMLBodyElement} */doc.body;
2319
+ }
1690
2320
  const nbsp = '\u00A0'; // Very commonly needed in templates
1691
2321
 
1692
- export { $, $$, body, jml as default, glue, jml, nbsp };
2322
+ export { $, $$, DOMException, body, jml as default, glue, jml, nbsp };