pseudo-dom 0.11.0 → 0.14.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.
@@ -30,36 +30,53 @@ const generateDocument = (root, context = {}) => {
30
30
  */
31
31
  const newWindow = typeof root.document === 'undefined' ? root : new EventTargetService_1.default()
32
32
  /**
33
- * @type {Node|PseudoNode}
33
+ * The Node class itself (matching the DOM's window.Node), not an instance - the right-hand side of `instanceof`
34
+ * must be a constructor, so `x instanceof Node` needs this, not `new PseudoNode()`.
35
+ * @type {Function}
34
36
  */
35
- const Node = root.Node || new NodeService_1.NodeService()
37
+ const Node = root.Node || NodeService_1.NodeService
36
38
  if (typeof newWindow.Node === 'undefined') {
37
39
  newWindow.Node = Node
38
40
  }
39
41
  /**
40
- *
41
- * @type {Element|PseudoElement}
42
+ * The Element class itself, for the same reason as Node.
43
+ * @type {Function}
42
44
  */
43
- const Element = root.Element || new ElementService_1.ElementService()
45
+ const Element = root.Element || ElementService_1.ElementService
44
46
  if (typeof newWindow.Element === 'undefined') {
45
47
  newWindow.Element = Element
46
48
  }
47
49
  /**
48
- * Create an instance of HTMLElement if not available
49
- * @type {HTMLElement|PseudoHTMLElement}
50
+ * The HTMLElement class itself, for the same reason as Node.
51
+ * @type {Function}
50
52
  */
51
- const HTMLElement = root.HTMLElement || new HTMLElementService_1.HTMLElementService()
53
+ const HTMLElement = root.HTMLElement || HTMLElementService_1.HTMLElementService
52
54
  if (typeof newWindow.HTMLElement === 'undefined') {
53
55
  newWindow.HTMLElement = HTMLElement
54
56
  }
55
57
  /**
56
- * Define document when not available
58
+ * The HTMLDocument class itself, for the same reason as Node (so `document instanceof HTMLDocument`, a common
59
+ * real-DOM-detection check, works).
60
+ * @type {Function}
61
+ */
62
+ const HTMLDocument = root.HTMLDocument || PseudoHTMLDocument_1.default
63
+ if (typeof newWindow.HTMLDocument === 'undefined') {
64
+ newWindow.HTMLDocument = HTMLDocument
65
+ }
66
+ /**
67
+ * Define document when not available - a real instance, unlike the classes above (window.document IS an object,
68
+ * not a constructor).
57
69
  * @type {Document|PseudoHTMLDocument}
58
70
  */
59
71
  const document = root.document || new PseudoHTMLDocument_1.default()
60
72
  if (typeof newWindow.document === 'undefined') {
61
73
  newWindow.document = document
62
74
  }
63
- return context ? Object.assign(context, newWindow) : Object.assign(root, newWindow)
75
+ // When there was no real document, newWindow is root itself (see above), so every assignment above already
76
+ // mutated it directly. If context is also that same object (root and context given as one and the same, as
77
+ // installGlobal does) merging is already done - doing it again would be Object.assign(target, target), which
78
+ // throws on any getter-only own property target already has (globalThis.crypto, in Node).
79
+ const mergeInto = context || root
80
+ return newWindow === mergeInto ? newWindow : Object.assign(mergeInto, newWindow)
64
81
  }
65
82
  exports.default = generateDocument
@@ -1 +1 @@
1
- "use strict";var __importDefault=function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(exports,"__esModule",{value:!0});const EventTargetService_1=__importDefault(require("../services/EventTargetService")),NodeService_1=require("../services/NodeService"),ElementService_1=require("../services/ElementService"),HTMLElementService_1=require("../services/HTMLElementService"),PseudoHTMLDocument_1=__importDefault(require("../classes/PseudoHTMLDocument")),generateDocument=(e,t={})=>{const r=void 0===e.document?e:new EventTargetService_1.default,n=e.Node||new NodeService_1.NodeService;void 0===r.Node&&(r.Node=n);const c=e.Element||new ElementService_1.ElementService;void 0===r.Element&&(r.Element=c);const o=e.HTMLElement||new HTMLElementService_1.HTMLElementService;void 0===r.HTMLElement&&(r.HTMLElement=o);const i=e.document||new PseudoHTMLDocument_1.default;return void 0===r.document&&(r.document=i),t?Object.assign(t,r):Object.assign(e,r)};exports.default=generateDocument;
1
+ "use strict";var __importDefault=function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(exports,"__esModule",{value:!0});const EventTargetService_1=__importDefault(require("../services/EventTargetService")),NodeService_1=require("../services/NodeService"),ElementService_1=require("../services/ElementService"),HTMLElementService_1=require("../services/HTMLElementService"),PseudoHTMLDocument_1=__importDefault(require("../classes/PseudoHTMLDocument")),generateDocument=(e,t={})=>{const n=void 0===e.document?e:new EventTargetService_1.default,r=e.Node||NodeService_1.NodeService;void 0===n.Node&&(n.Node=r);const o=e.Element||ElementService_1.ElementService;void 0===n.Element&&(n.Element=o);const c=e.HTMLElement||HTMLElementService_1.HTMLElementService;void 0===n.HTMLElement&&(n.HTMLElement=c);const i=e.HTMLDocument||PseudoHTMLDocument_1.default;void 0===n.HTMLDocument&&(n.HTMLDocument=i);const u=e.document||new PseudoHTMLDocument_1.default;void 0===n.document&&(n.document=u);const m=t||e;return n===m?n:Object.assign(m,n)};exports.default=generateDocument;
@@ -0,0 +1,15 @@
1
+ /**
2
+ * When there is no real DOM already available (a real browser, or a jsdom-based test environment - isBrowser is
3
+ * true for both, since jsdom's window has a document too, and a jsdom document should never be overwritten, real
4
+ * DOM behaviour is always closer to the real thing), populate the given global-like target with document / Node /
5
+ * Element / HTMLElement / HTMLDocument, generated by generateDocument, plus window (a self-reference, matching a
6
+ * real browser's global scope - some code checks for a bare `window` before falling back to `global`), so code
7
+ * written against a real DOM's globals works as-is. Does nothing when a real DOM is already there, matching
8
+ * generateDocument's own only-fill-what-is-missing behaviour - calling this is always safe, in any environment.
9
+ * @memberOf module:factories
10
+ * @param {*} [target] The object to add document / Node / Element / HTMLElement / window onto - defaults to
11
+ * globalThis, so bare `document`, `Node`, `window`, etc. resolve from anywhere once this has run
12
+ * @returns {*} The same target, for convenience
13
+ */
14
+ declare const installGlobal: (target?: any) => any;
15
+ export default installGlobal;
@@ -0,0 +1,44 @@
1
+ 'use strict'
2
+
3
+ const __importDefault = void 0 && (void 0).__importDefault || function (mod) {
4
+ return mod && mod.__esModule
5
+ ? mod
6
+ : {
7
+ default: mod
8
+ }
9
+ }
10
+ Object.defineProperty(exports, '__esModule', {
11
+ value: true
12
+ })
13
+ /**
14
+ * @file Makes pseudo-dom a drop-in swap for a real DOM: code written against bare globals (document.createElement,
15
+ * new Node(), ...) works unmodified, in the browser or in Node, without an if (typeof document === 'undefined')
16
+ * check at every call site.
17
+ * @author Joshua Heagle <joshuaheagle@gmail.com>
18
+ * @version 1.0.0
19
+ */
20
+ const browser_or_node_1 = require('browser-or-node')
21
+ const generateDocument_1 = __importDefault(require('./generateDocument'))
22
+ /**
23
+ * When there is no real DOM already available (a real browser, or a jsdom-based test environment - isBrowser is
24
+ * true for both, since jsdom's window has a document too, and a jsdom document should never be overwritten, real
25
+ * DOM behaviour is always closer to the real thing), populate the given global-like target with document / Node /
26
+ * Element / HTMLElement / HTMLDocument, generated by generateDocument, plus window (a self-reference, matching a
27
+ * real browser's global scope - some code checks for a bare `window` before falling back to `global`), so code
28
+ * written against a real DOM's globals works as-is. Does nothing when a real DOM is already there, matching
29
+ * generateDocument's own only-fill-what-is-missing behaviour - calling this is always safe, in any environment.
30
+ * @memberOf module:factories
31
+ * @param {*} [target] The object to add document / Node / Element / HTMLElement / window onto - defaults to
32
+ * globalThis, so bare `document`, `Node`, `window`, etc. resolve from anywhere once this has run
33
+ * @returns {*} The same target, for convenience
34
+ */
35
+ const installGlobal = (target = typeof globalThis !== 'undefined' ? globalThis : {}) => {
36
+ if (!browser_or_node_1.isBrowser) {
37
+ (0, generateDocument_1.default)(target, target)
38
+ if (typeof target.window === 'undefined') {
39
+ target.window = target
40
+ }
41
+ }
42
+ return target
43
+ }
44
+ exports.default = installGlobal
@@ -0,0 +1 @@
1
+ "use strict";var __importDefault=function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(exports,"__esModule",{value:!0});const browser_or_node_1=require("browser-or-node"),generateDocument_1=__importDefault(require("./generateDocument")),installGlobal=(e=("undefined"!=typeof globalThis?globalThis:{}))=>(browser_or_node_1.isBrowser||((0,generateDocument_1.default)(e,e),void 0===e.window&&(e.window=e)),e);exports.default=installGlobal;
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Print a node's markup to the console, indented for readability (see prettyPrint).
3
+ * @memberOf module:factories
4
+ * @param {*} node
5
+ * @param {string} [label=''] A label printed above the markup, to identify this log call
6
+ * @returns {undefined}
7
+ */
8
+ declare const logElement: (node: any, label?: string) => void;
9
+ export default logElement;
@@ -0,0 +1,23 @@
1
+ 'use strict'
2
+
3
+ Object.defineProperty(exports, '__esModule', {
4
+ value: true
5
+ })
6
+ /**
7
+ * @file Prints an element (or any node) to the console, readably - the point of pseudo-dom running headlessly is
8
+ * usually to watch what code does to a tree without a real browser to look at.
9
+ * @author Joshua Heagle <joshuaheagle@gmail.com>
10
+ * @version 1.0.0
11
+ */
12
+ const serializeHTML_1 = require('./serializeHTML')
13
+ /**
14
+ * Print a node's markup to the console, indented for readability (see prettyPrint).
15
+ * @memberOf module:factories
16
+ * @param {*} node
17
+ * @param {string} [label=''] A label printed above the markup, to identify this log call
18
+ * @returns {undefined}
19
+ */
20
+ const logElement = (node, label = '') => {
21
+ console.log(`${label ? `${label}\n` : ''}${(0, serializeHTML_1.prettyPrint)(node)}`)
22
+ }
23
+ exports.default = logElement
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0});const serializeHTML_1=require("./serializeHTML"),logElement=(e,t="")=>{console.log(`${t?`${t}\n`:""}${(0,serializeHTML_1.prettyPrint)(e)}`)};exports.default=logElement;
@@ -1,13 +1,13 @@
1
- /**
2
- * Parse an HTML string into the nodes it describes (siblings at the top level, exactly like the DOM's own HTML
3
- * parsing does for innerHTML / insertAdjacentHTML - there is no single root unless the markup itself has one).
4
- * @memberOf module:factories
5
- * @param {string} html
6
- * @param {*} ownerDocument The document the new nodes belong to (matches what innerHTML etc. would set), or null
7
- * @param {function(new: *, {tagName: string})} ElementClass The class to build each parsed element with
8
- * @returns {Array<*>}
9
- */
10
- declare const parseHTML: (html: string, ownerDocument: any, ElementClass: new (options: {
11
- tagName: string;
12
- }) => any) => Array<any>;
13
- export default parseHTML;
1
+ /**
2
+ * Parse an HTML string into the nodes it describes (siblings at the top level, exactly like the DOM's own HTML
3
+ * parsing does for innerHTML / insertAdjacentHTML - there is no single root unless the markup itself has one).
4
+ * @memberOf module:factories
5
+ * @param {string} html
6
+ * @param {*} ownerDocument The document the new nodes belong to (matches what innerHTML etc. would set), or null
7
+ * @param {function(new: *, {tagName: string})} ElementClass The class to build each parsed element with
8
+ * @returns {Array<*>}
9
+ */
10
+ declare const parseHTML: (html: string, ownerDocument: any, ElementClass: new (options: {
11
+ tagName: string;
12
+ }) => any) => Array<any>;
13
+ export default parseHTML;
@@ -1,84 +1,84 @@
1
- 'use strict'
2
-
3
- Object.defineProperty(exports, '__esModule', {
4
- value: true
5
- })
6
- /**
7
- * @file Parses an HTML string into pseudo-dom nodes, for innerHTML / outerHTML / insertAdjacentHTML. Built on
8
- * htmlparser2's Parser (a SAX-style tokenizer) with a custom handler which builds pseudo-dom nodes directly -
9
- * htmlparser2's own default DomHandler / domutils tree (which this never uses) is stubbed out of the browser bundle
10
- * by the browser.ignore config, the same way css-select's unused default adapter already is.
11
- *
12
- * The class to build parsed elements with is given by the caller (rather than imported here) so this has no
13
- * dependency on ElementService / HTMLElementService - importing either here, from a factory ElementService itself
14
- * would need to call, would create an import cycle.
15
- * @author Joshua Heagle <joshuaheagle@gmail.com>
16
- * @version 1.0.0
17
- */
18
- const htmlparser2_1 = require('htmlparser2')
19
- const NodeService_1 = require('../services/NodeService')
20
- /**
21
- * Parse an HTML string into the nodes it describes (siblings at the top level, exactly like the DOM's own HTML
22
- * parsing does for innerHTML / insertAdjacentHTML - there is no single root unless the markup itself has one).
23
- * @memberOf module:factories
24
- * @param {string} html
25
- * @param {*} ownerDocument The document the new nodes belong to (matches what innerHTML etc. would set), or null
26
- * @param {function(new: *, {tagName: string})} ElementClass The class to build each parsed element with
27
- * @returns {Array<*>}
28
- */
29
- const parseHTML = (html, ownerDocument, ElementClass) => {
30
- const roots = []
31
- const stack = []
32
- const appendNode = node => {
33
- node.ownerDocumentStore = ownerDocument
34
- if (stack.length) {
35
- stack[stack.length - 1].appendChild(node)
36
- } else {
37
- roots.push(node)
38
- }
39
- }
40
- const parser = new htmlparser2_1.Parser({
41
- onopentagname (name) {
42
- const element = new ElementClass({
43
- tagName: name
44
- })
45
- appendNode(element)
46
- stack.push(element)
47
- },
48
- onattribute (name, value) {
49
- if (!stack.length) {
50
- return
51
- }
52
- const element = stack[stack.length - 1]
53
- // pseudo-dom's className / classList and style are not kept in sync with a literal 'class' / 'style'
54
- // attribute the way setAttribute usually is (there is no such special-casing here, unlike a real DOM) - route
55
- // them to the real objects directly, the same way cssSelectAdapter does for reading 'class' when matching a
56
- // CSS selector, so parsed markup's class="..." / style="..." actually populates className/classList and style.
57
- if (name === 'class') {
58
- element.className = value
59
- return
60
- }
61
- if (name === 'style' && element.style) {
62
- element.style.cssText = value
63
- return
64
- }
65
- element.setAttribute(name, value)
66
- },
67
- ontext (text) {
68
- appendNode(new NodeService_1.TextService(text))
69
- },
70
- oncomment (data) {
71
- appendNode(new NodeService_1.CommentService(data))
72
- },
73
- onclosetag () {
74
- stack.pop()
75
- }
76
- }, {
77
- lowerCaseTags: true,
78
- lowerCaseAttributeNames: true
79
- })
80
- parser.write(String(html || ''))
81
- parser.end()
82
- return roots
83
- }
84
- exports.default = parseHTML
1
+ 'use strict'
2
+
3
+ Object.defineProperty(exports, '__esModule', {
4
+ value: true
5
+ })
6
+ /**
7
+ * @file Parses an HTML string into pseudo-dom nodes, for innerHTML / outerHTML / insertAdjacentHTML. Built on
8
+ * htmlparser2's Parser (a SAX-style tokenizer) with a custom handler which builds pseudo-dom nodes directly -
9
+ * htmlparser2's own default DomHandler / domutils tree (which this never uses) is stubbed out of the browser bundle
10
+ * by the browser.ignore config, the same way css-select's unused default adapter already is.
11
+ *
12
+ * The class to build parsed elements with is given by the caller (rather than imported here) so this has no
13
+ * dependency on ElementService / HTMLElementService - importing either here, from a factory ElementService itself
14
+ * would need to call, would create an import cycle.
15
+ * @author Joshua Heagle <joshuaheagle@gmail.com>
16
+ * @version 1.0.0
17
+ */
18
+ const htmlparser2_1 = require('htmlparser2')
19
+ const NodeService_1 = require('../services/NodeService')
20
+ /**
21
+ * Parse an HTML string into the nodes it describes (siblings at the top level, exactly like the DOM's own HTML
22
+ * parsing does for innerHTML / insertAdjacentHTML - there is no single root unless the markup itself has one).
23
+ * @memberOf module:factories
24
+ * @param {string} html
25
+ * @param {*} ownerDocument The document the new nodes belong to (matches what innerHTML etc. would set), or null
26
+ * @param {function(new: *, {tagName: string})} ElementClass The class to build each parsed element with
27
+ * @returns {Array<*>}
28
+ */
29
+ const parseHTML = (html, ownerDocument, ElementClass) => {
30
+ const roots = []
31
+ const stack = []
32
+ const appendNode = node => {
33
+ node.ownerDocumentStore = ownerDocument
34
+ if (stack.length) {
35
+ stack[stack.length - 1].appendChild(node)
36
+ } else {
37
+ roots.push(node)
38
+ }
39
+ }
40
+ const parser = new htmlparser2_1.Parser({
41
+ onopentagname (name) {
42
+ const element = new ElementClass({
43
+ tagName: name
44
+ })
45
+ appendNode(element)
46
+ stack.push(element)
47
+ },
48
+ onattribute (name, value) {
49
+ if (!stack.length) {
50
+ return
51
+ }
52
+ const element = stack[stack.length - 1]
53
+ // pseudo-dom's className / classList and style are not kept in sync with a literal 'class' / 'style'
54
+ // attribute the way setAttribute usually is (there is no such special-casing here, unlike a real DOM) - route
55
+ // them to the real objects directly, the same way cssSelectAdapter does for reading 'class' when matching a
56
+ // CSS selector, so parsed markup's class="..." / style="..." actually populates className/classList and style.
57
+ if (name === 'class') {
58
+ element.className = value
59
+ return
60
+ }
61
+ if (name === 'style' && element.style) {
62
+ element.style.cssText = value
63
+ return
64
+ }
65
+ element.setAttribute(name, value)
66
+ },
67
+ ontext (text) {
68
+ appendNode(new NodeService_1.TextService(text))
69
+ },
70
+ oncomment (data) {
71
+ appendNode(new NodeService_1.CommentService(data))
72
+ },
73
+ onclosetag () {
74
+ stack.pop()
75
+ }
76
+ }, {
77
+ lowerCaseTags: true,
78
+ lowerCaseAttributeNames: true
79
+ })
80
+ parser.write(String(html || ''))
81
+ parser.end()
82
+ return roots
83
+ }
84
+ exports.default = parseHTML
@@ -1,15 +1,25 @@
1
- /**
2
- * A node's children, serialized in order (this is what innerHTML returns).
3
- * @memberOf module:factories
4
- * @param {*} node
5
- * @returns {string}
6
- */
7
- declare const serializeChildren: (node: any) => string;
8
- /**
9
- * An element itself, serialized with its children (this is what outerHTML returns).
10
- * @memberOf module:factories
11
- * @param {*} element
12
- * @returns {string}
13
- */
14
- declare const serializeOuter: (element: any) => string;
15
- export { serializeChildren, serializeOuter };
1
+ /**
2
+ * A node's children, serialized in order (this is what innerHTML returns).
3
+ * @memberOf module:factories
4
+ * @param {*} node
5
+ * @returns {string}
6
+ */
7
+ declare const serializeChildren: (node: any) => string;
8
+ /**
9
+ * An element itself, serialized with its children (this is what outerHTML returns).
10
+ * @memberOf module:factories
11
+ * @param {*} element
12
+ * @returns {string}
13
+ */
14
+ declare const serializeOuter: (element: any) => string;
15
+ /**
16
+ * A node's markup, indented one level per level of nesting - a real DOM's outerHTML / innerHTML has no line breaks
17
+ * at all, which does not read well for a whole tree (a board full of cells, say). Useful for watching pseudo-dom-
18
+ * driven code run headlessly, printed to a terminal - see also logElement, which does the printing too.
19
+ * @memberOf module:factories
20
+ * @param {*} node
21
+ * @param {string} [indent=' '] The indentation used per level of nesting
22
+ * @returns {string}
23
+ */
24
+ declare const prettyPrint: (node: any, indent?: string) => string;
25
+ export { serializeChildren, serializeOuter, prettyPrint };
@@ -22,7 +22,7 @@ require('core-js/modules/esnext.set.union.js')
22
22
  Object.defineProperty(exports, '__esModule', {
23
23
  value: true
24
24
  })
25
- exports.serializeOuter = exports.serializeChildren = void 0
25
+ exports.prettyPrint = exports.serializeOuter = exports.serializeChildren = void 0
26
26
  /**
27
27
  * @file Serializes pseudo-dom nodes back into an HTML string, for innerHTML / outerHTML.
28
28
  * @author Joshua Heagle <joshuaheagle@gmail.com>
@@ -43,17 +43,21 @@ const ATTRIBUTE_NAME = {
43
43
  // Boolean HTML attributes: present (with no value) when true, absent entirely when false
44
44
  const BOOLEAN_ATTRIBUTES = new Set(['hidden'])
45
45
  /**
46
- * Escape text so it is safe inside HTML text content.
47
- * @param {string} text
46
+ * Escape text so it is safe inside HTML text content. Coerces to a string first - unlike a real DOM, pseudo-dom's
47
+ * setAttribute does not itself coerce (see the same note on escapeAttributeValue), and nodeValue is not guaranteed
48
+ * to be a string either.
49
+ * @param {*} text
48
50
  * @returns {string}
49
51
  */
50
- const escapeText = text => text.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')
52
+ const escapeText = text => String(text).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')
51
53
  /**
52
- * Escape a value so it is safe inside a double-quoted HTML attribute.
53
- * @param {string} value
54
+ * Escape a value so it is safe inside a double-quoted HTML attribute. Coerces to a string first: a real DOM's
55
+ * setAttribute always stores a string, however pseudo-dom's does not coerce what it is given, so a value set via
56
+ * setAttribute(name, 5) is stored (and read back by getAttribute) as the number 5, not the string '5'.
57
+ * @param {*} value
54
58
  * @returns {string}
55
59
  */
56
- const escapeAttributeValue = value => value.replace(/&/g, '&amp;').replace(/"/g, '&quot;')
60
+ const escapeAttributeValue = value => String(value).replace(/&/g, '&amp;').replace(/"/g, '&quot;')
57
61
  /**
58
62
  * Every attribute of the element, serialized (class instead of className, boolean attributes bare, the never-real
59
63
  * mock properties left out, style added from the live CSSStyleDeclaration when it is not empty).
@@ -112,3 +116,45 @@ exports.serializeChildren = serializeChildren
112
116
  */
113
117
  const serializeOuter = element => serializeNode(element)
114
118
  exports.serializeOuter = serializeOuter
119
+ /**
120
+ * One node, indented for readability (see prettyPrint) - unlike serializeNode, every non-empty node is its own
121
+ * line, so the structure of a whole tree is easy to read at a glance.
122
+ * @param {*} node
123
+ * @param {number} depth
124
+ * @param {string} indent
125
+ * @returns {string}
126
+ */
127
+ const prettyPrintNode = (node, depth, indent) => {
128
+ const pad = indent.repeat(depth)
129
+ if (node.nodeType === NodeService_1.NodeService.TEXT_NODE) {
130
+ const text = escapeText((node.nodeValue || '').trim())
131
+ return text ? `${pad}${text}` : ''
132
+ }
133
+ if (node.nodeType === NodeService_1.NodeService.COMMENT_NODE) {
134
+ return `${pad}<!--${node.nodeValue || ''}-->`
135
+ }
136
+ if (node.nodeType !== NodeService_1.NodeService.ELEMENT_NODE) {
137
+ return ''
138
+ }
139
+ const tagName = node.tagName
140
+ const attributes = serializeAttributes(node)
141
+ if (VOID_ELEMENTS.has(tagName)) {
142
+ return `${pad}<${tagName}${attributes}>`
143
+ }
144
+ const children = Array.from(node.childNodes).map(child => prettyPrintNode(child, depth + 1, indent)).filter(line => line !== '')
145
+ if (children.length === 0) {
146
+ return `${pad}<${tagName}${attributes}></${tagName}>`
147
+ }
148
+ return `${pad}<${tagName}${attributes}>\n${children.join('\n')}\n${pad}</${tagName}>`
149
+ }
150
+ /**
151
+ * A node's markup, indented one level per level of nesting - a real DOM's outerHTML / innerHTML has no line breaks
152
+ * at all, which does not read well for a whole tree (a board full of cells, say). Useful for watching pseudo-dom-
153
+ * driven code run headlessly, printed to a terminal - see also logElement, which does the printing too.
154
+ * @memberOf module:factories
155
+ * @param {*} node
156
+ * @param {string} [indent=' '] The indentation used per level of nesting
157
+ * @returns {string}
158
+ */
159
+ const prettyPrint = (node, indent = ' ') => prettyPrintNode(node, 0, indent)
160
+ exports.prettyPrint = prettyPrint
@@ -1 +1 @@
1
- "use strict";require("core-js/modules/esnext.iterator.constructor.js"),require("core-js/modules/esnext.iterator.filter.js"),require("core-js/modules/esnext.iterator.map.js"),require("core-js/modules/esnext.set.add-all.js"),require("core-js/modules/esnext.set.delete-all.js"),require("core-js/modules/esnext.set.difference.js"),require("core-js/modules/esnext.set.every.js"),require("core-js/modules/esnext.set.filter.js"),require("core-js/modules/esnext.set.find.js"),require("core-js/modules/esnext.set.intersection.js"),require("core-js/modules/esnext.set.is-disjoint-from.js"),require("core-js/modules/esnext.set.is-subset-of.js"),require("core-js/modules/esnext.set.is-superset-of.js"),require("core-js/modules/esnext.set.join.js"),require("core-js/modules/esnext.set.map.js"),require("core-js/modules/esnext.set.reduce.js"),require("core-js/modules/esnext.set.some.js"),require("core-js/modules/esnext.set.symmetric-difference.js"),require("core-js/modules/esnext.set.union.js"),Object.defineProperty(exports,"__esModule",{value:!0}),exports.serializeOuter=exports.serializeChildren=void 0;const NodeService_1=require("../services/NodeService"),VOID_ELEMENTS=new Set(["area","base","br","col","embed","hr","img","input","link","meta","param","source","track","wbr"]),NEVER_SERIALIZE=new Set(["innerHTML","clientHeight","clientLeft","clientTop","clientWidth","scrollHeight","scrollLeft","scrollTop","scrollWidth","offsetHeight","offsetLeft","offsetParent","offsetTop","offsetWidth"]),ATTRIBUTE_NAME={className:"class"},BOOLEAN_ATTRIBUTES=new Set(["hidden"]),escapeText=e=>e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;"),escapeAttributeValue=e=>e.replace(/&/g,"&amp;").replace(/"/g,"&quot;"),serializeAttributes=e=>{let s=(e.getAttributeNames?e.getAttributeNames():[]).filter(e=>!NEVER_SERIALIZE.has(e)).map(s=>{if(BOOLEAN_ATTRIBUTES.has(s))return e[s]?` ${s}`:"";const r=e.getAttribute(s);return""===r?"":` ${ATTRIBUTE_NAME[s]||s}="${escapeAttributeValue(r)}"`}).join("");const r=e.style;return r&&r.cssText&&(s+=` style="${escapeAttributeValue(r.cssText)}"`),s},serializeNode=e=>{if(e.nodeType===NodeService_1.NodeService.TEXT_NODE)return escapeText(e.nodeValue||"");if(e.nodeType===NodeService_1.NodeService.COMMENT_NODE)return`\x3c!--${e.nodeValue||""}--\x3e`;if(e.nodeType!==NodeService_1.NodeService.ELEMENT_NODE)return"";const s=e.tagName,r=serializeAttributes(e);return VOID_ELEMENTS.has(s)?`<${s}${r}>`:`<${s}${r}>${serializeChildren(e)}</${s}>`},serializeChildren=e=>Array.from(e.childNodes).map(serializeNode).join("");exports.serializeChildren=serializeChildren;const serializeOuter=e=>serializeNode(e);exports.serializeOuter=serializeOuter;
1
+ "use strict";require("core-js/modules/esnext.iterator.constructor.js"),require("core-js/modules/esnext.iterator.filter.js"),require("core-js/modules/esnext.iterator.map.js"),require("core-js/modules/esnext.set.add-all.js"),require("core-js/modules/esnext.set.delete-all.js"),require("core-js/modules/esnext.set.difference.js"),require("core-js/modules/esnext.set.every.js"),require("core-js/modules/esnext.set.filter.js"),require("core-js/modules/esnext.set.find.js"),require("core-js/modules/esnext.set.intersection.js"),require("core-js/modules/esnext.set.is-disjoint-from.js"),require("core-js/modules/esnext.set.is-subset-of.js"),require("core-js/modules/esnext.set.is-superset-of.js"),require("core-js/modules/esnext.set.join.js"),require("core-js/modules/esnext.set.map.js"),require("core-js/modules/esnext.set.reduce.js"),require("core-js/modules/esnext.set.some.js"),require("core-js/modules/esnext.set.symmetric-difference.js"),require("core-js/modules/esnext.set.union.js"),Object.defineProperty(exports,"__esModule",{value:!0}),exports.prettyPrint=exports.serializeOuter=exports.serializeChildren=void 0;const NodeService_1=require("../services/NodeService"),VOID_ELEMENTS=new Set(["area","base","br","col","embed","hr","img","input","link","meta","param","source","track","wbr"]),NEVER_SERIALIZE=new Set(["innerHTML","clientHeight","clientLeft","clientTop","clientWidth","scrollHeight","scrollLeft","scrollTop","scrollWidth","offsetHeight","offsetLeft","offsetParent","offsetTop","offsetWidth"]),ATTRIBUTE_NAME={className:"class"},BOOLEAN_ATTRIBUTES=new Set(["hidden"]),escapeText=e=>String(e).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;"),escapeAttributeValue=e=>String(e).replace(/&/g,"&amp;").replace(/"/g,"&quot;"),serializeAttributes=e=>{let r=(e.getAttributeNames?e.getAttributeNames():[]).filter(e=>!NEVER_SERIALIZE.has(e)).map(r=>{if(BOOLEAN_ATTRIBUTES.has(r))return e[r]?` ${r}`:"";const t=e.getAttribute(r);return""===t?"":` ${ATTRIBUTE_NAME[r]||r}="${escapeAttributeValue(t)}"`}).join("");const t=e.style;return t&&t.cssText&&(r+=` style="${escapeAttributeValue(t.cssText)}"`),r},serializeNode=e=>{if(e.nodeType===NodeService_1.NodeService.TEXT_NODE)return escapeText(e.nodeValue||"");if(e.nodeType===NodeService_1.NodeService.COMMENT_NODE)return`\x3c!--${e.nodeValue||""}--\x3e`;if(e.nodeType!==NodeService_1.NodeService.ELEMENT_NODE)return"";const r=e.tagName,t=serializeAttributes(e);return VOID_ELEMENTS.has(r)?`<${r}${t}>`:`<${r}${t}>${serializeChildren(e)}</${r}>`},serializeChildren=e=>Array.from(e.childNodes).map(serializeNode).join("");exports.serializeChildren=serializeChildren;const serializeOuter=e=>serializeNode(e);exports.serializeOuter=serializeOuter;const prettyPrintNode=(e,r,t)=>{const s=t.repeat(r);if(e.nodeType===NodeService_1.NodeService.TEXT_NODE){const r=escapeText((e.nodeValue||"").trim());return r?`${s}${r}`:""}if(e.nodeType===NodeService_1.NodeService.COMMENT_NODE)return`${s}\x3c!--${e.nodeValue||""}--\x3e`;if(e.nodeType!==NodeService_1.NodeService.ELEMENT_NODE)return"";const i=e.tagName,o=serializeAttributes(e);if(VOID_ELEMENTS.has(i))return`${s}<${i}${o}>`;const n=Array.from(e.childNodes).map(e=>prettyPrintNode(e,r+1,t)).filter(e=>""!==e);return 0===n.length?`${s}<${i}${o}></${i}>`:`${s}<${i}${o}>\n${n.join("\n")}\n${s}</${i}>`},prettyPrint=(e,r=" ")=>prettyPrintNode(e,0,r);exports.prettyPrint=prettyPrint;