index.js 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. import { html } from 'parse5';
  2. import { Element, Document, ProcessingInstruction, Comment, Text, isDirective, isText, isComment, isTag, } from 'domhandler';
  3. function enquoteDoctypeId(id) {
  4. const quote = id.includes('"') ? "'" : '"';
  5. return quote + id + quote;
  6. }
  7. /** @internal */
  8. export function serializeDoctypeContent(name, publicId, systemId) {
  9. let str = '!DOCTYPE ';
  10. if (name) {
  11. str += name;
  12. }
  13. if (publicId) {
  14. str += ` PUBLIC ${enquoteDoctypeId(publicId)}`;
  15. }
  16. else if (systemId) {
  17. str += ' SYSTEM';
  18. }
  19. if (systemId) {
  20. str += ` ${enquoteDoctypeId(systemId)}`;
  21. }
  22. return str;
  23. }
  24. export const adapter = {
  25. // Re-exports from domhandler
  26. isCommentNode: isComment,
  27. isElementNode: isTag,
  28. isTextNode: isText,
  29. //Node construction
  30. createDocument() {
  31. const node = new Document([]);
  32. node['x-mode'] = html.DOCUMENT_MODE.NO_QUIRKS;
  33. return node;
  34. },
  35. createDocumentFragment() {
  36. return new Document([]);
  37. },
  38. createElement(tagName, namespaceURI, attrs) {
  39. const attribs = Object.create(null);
  40. const attribsNamespace = Object.create(null);
  41. const attribsPrefix = Object.create(null);
  42. for (let i = 0; i < attrs.length; i++) {
  43. const attrName = attrs[i].name;
  44. attribs[attrName] = attrs[i].value;
  45. attribsNamespace[attrName] = attrs[i].namespace;
  46. attribsPrefix[attrName] = attrs[i].prefix;
  47. }
  48. const node = new Element(tagName, attribs, []);
  49. node.namespace = namespaceURI;
  50. node['x-attribsNamespace'] = attribsNamespace;
  51. node['x-attribsPrefix'] = attribsPrefix;
  52. return node;
  53. },
  54. createCommentNode(data) {
  55. return new Comment(data);
  56. },
  57. createTextNode(value) {
  58. return new Text(value);
  59. },
  60. //Tree mutation
  61. appendChild(parentNode, newNode) {
  62. const prev = parentNode.children[parentNode.children.length - 1];
  63. if (prev) {
  64. prev.next = newNode;
  65. newNode.prev = prev;
  66. }
  67. parentNode.children.push(newNode);
  68. newNode.parent = parentNode;
  69. },
  70. insertBefore(parentNode, newNode, referenceNode) {
  71. const insertionIdx = parentNode.children.indexOf(referenceNode);
  72. const { prev } = referenceNode;
  73. if (prev) {
  74. prev.next = newNode;
  75. newNode.prev = prev;
  76. }
  77. referenceNode.prev = newNode;
  78. newNode.next = referenceNode;
  79. parentNode.children.splice(insertionIdx, 0, newNode);
  80. newNode.parent = parentNode;
  81. },
  82. setTemplateContent(templateElement, contentElement) {
  83. adapter.appendChild(templateElement, contentElement);
  84. },
  85. getTemplateContent(templateElement) {
  86. return templateElement.children[0];
  87. },
  88. setDocumentType(document, name, publicId, systemId) {
  89. const data = serializeDoctypeContent(name, publicId, systemId);
  90. let doctypeNode = document.children.find((node) => isDirective(node) && node.name === '!doctype');
  91. if (doctypeNode) {
  92. doctypeNode.data = data !== null && data !== void 0 ? data : null;
  93. }
  94. else {
  95. doctypeNode = new ProcessingInstruction('!doctype', data);
  96. adapter.appendChild(document, doctypeNode);
  97. }
  98. doctypeNode['x-name'] = name;
  99. doctypeNode['x-publicId'] = publicId;
  100. doctypeNode['x-systemId'] = systemId;
  101. },
  102. setDocumentMode(document, mode) {
  103. document['x-mode'] = mode;
  104. },
  105. getDocumentMode(document) {
  106. return document['x-mode'];
  107. },
  108. detachNode(node) {
  109. if (node.parent) {
  110. const idx = node.parent.children.indexOf(node);
  111. const { prev, next } = node;
  112. node.prev = null;
  113. node.next = null;
  114. if (prev) {
  115. prev.next = next;
  116. }
  117. if (next) {
  118. next.prev = prev;
  119. }
  120. node.parent.children.splice(idx, 1);
  121. node.parent = null;
  122. }
  123. },
  124. insertText(parentNode, text) {
  125. const lastChild = parentNode.children[parentNode.children.length - 1];
  126. if (lastChild && isText(lastChild)) {
  127. lastChild.data += text;
  128. }
  129. else {
  130. adapter.appendChild(parentNode, adapter.createTextNode(text));
  131. }
  132. },
  133. insertTextBefore(parentNode, text, referenceNode) {
  134. const prevNode = parentNode.children[parentNode.children.indexOf(referenceNode) - 1];
  135. if (prevNode && isText(prevNode)) {
  136. prevNode.data += text;
  137. }
  138. else {
  139. adapter.insertBefore(parentNode, adapter.createTextNode(text), referenceNode);
  140. }
  141. },
  142. adoptAttributes(recipient, attrs) {
  143. for (let i = 0; i < attrs.length; i++) {
  144. const attrName = attrs[i].name;
  145. if (recipient.attribs[attrName] === undefined) {
  146. recipient.attribs[attrName] = attrs[i].value;
  147. recipient['x-attribsNamespace'][attrName] = attrs[i].namespace;
  148. recipient['x-attribsPrefix'][attrName] = attrs[i].prefix;
  149. }
  150. }
  151. },
  152. //Tree traversing
  153. getFirstChild(node) {
  154. return node.children[0];
  155. },
  156. getChildNodes(node) {
  157. return node.children;
  158. },
  159. getParentNode(node) {
  160. return node.parent;
  161. },
  162. getAttrList(element) {
  163. return element.attributes;
  164. },
  165. //Node data
  166. getTagName(element) {
  167. return element.name;
  168. },
  169. getNamespaceURI(element) {
  170. return element.namespace;
  171. },
  172. getTextNodeContent(textNode) {
  173. return textNode.data;
  174. },
  175. getCommentNodeContent(commentNode) {
  176. return commentNode.data;
  177. },
  178. getDocumentTypeNodeName(doctypeNode) {
  179. var _a;
  180. return (_a = doctypeNode['x-name']) !== null && _a !== void 0 ? _a : '';
  181. },
  182. getDocumentTypeNodePublicId(doctypeNode) {
  183. var _a;
  184. return (_a = doctypeNode['x-publicId']) !== null && _a !== void 0 ? _a : '';
  185. },
  186. getDocumentTypeNodeSystemId(doctypeNode) {
  187. var _a;
  188. return (_a = doctypeNode['x-systemId']) !== null && _a !== void 0 ? _a : '';
  189. },
  190. //Node types
  191. isDocumentTypeNode(node) {
  192. return isDirective(node) && node.name === '!doctype';
  193. },
  194. // Source code location
  195. setNodeSourceCodeLocation(node, location) {
  196. if (location) {
  197. node.startIndex = location.startOffset;
  198. node.endIndex = location.endOffset;
  199. }
  200. node.sourceCodeLocation = location;
  201. },
  202. getNodeSourceCodeLocation(node) {
  203. return node.sourceCodeLocation;
  204. },
  205. updateNodeSourceCodeLocation(node, endLocation) {
  206. if (endLocation.endOffset != null)
  207. node.endIndex = endLocation.endOffset;
  208. node.sourceCodeLocation = {
  209. ...node.sourceCodeLocation,
  210. ...endLocation,
  211. };
  212. },
  213. };